in Technology

ลองทำ Serverless WebSocket ด้วย AWS Lambda และ AWS API Gateway

อยากอัพเดทข้อมูลแบบ Real-time, แสดงข้อความ Notification ทันทีเมื่อมีการแจ้งข่าวให้สมาชิก, ทำระบบแชทที่พูดคุยกันได้ตลอดเวลา ฯลฯ ระบบแบบนี้เรามักทำเป็น WebSocket ซึ่งเป็น Protocol หนึ่งที่จะเปิดท่อ Connection ไว้เพื่อให้สื่อสารระหว่าง Server และ Client ได้ตลอดเวลา ซึ่งรายละเอียดมากกว่านี้ หรือทำอย่างไร ลองไปหาจาก Google ได้เยอะแยะ

แต่ที่ชวนฉงนคือ การทำงานของ Serverless เป็นแบบ เกิดขึ้น และทำ Process แล้วก็ดับไป มันจะทำเป็น WebSocket ได้ด้วยหรอ เพราะต้องเปิดแช่ Connection เพื่อสื่อสารกันตลอดเวลา

โพสต์นี้จะแชร์แนวคิดให้อ่านกัน ว่ามันเป็นไปได้จริงๆ ด้วยบริการของ Amazon API Gateway และ AWS Lambda ส่วนโค้ดตัวอย่าง ไปดูได้ที่ Github (aws-lambda-websocket) ผมได้เลย

อธิบายการทำงาน

การทำ Websocket ด้วย Lambda มีจุดสำคัญอยู่ 2 จุด คือ

1.Amazon API Gateway

ซึ่งทาง Amazon เอง เพิ่งประกาศฟีเจอร์ WebSocket นี้เมื่อ 3 เดือนที่แล้วนี่เอง (18 ธันวาคม 2561) โดยสามารถใช้งานกับบริการต่างๆของ Amazon ได้ เช่น AWS Lambda, Amazon Kinesis, หรืออื่นๆที่เชื่อมต่อด้วย HTTP endpoint

หน้าตาของ Amazon API Gateway ตอนที่จะสร้าง WebSocket API

ซึ่งใน Amazon API Gateway ส่วนของ Websocket API จะมีการ Config Resource Type ต่างจากแบบ REST คือ

  1. route ที่เอาไว้เป็นช่องทางจัดการคำขอต่างๆที่มาจากผู้เรียกใช้ (route selection expression)
  2. routeKey ที่เป็นช่องทางของคำขอ โดยแบ่งได้ 3 routeKey หลัก คือ
    • $default – ใช้สำหรับคำขอที่ไม่เข้าเงื่อนไขใดๆเลย จากผู้รองขอ เช่น การส่งข้อมูล Error ให้กับผู้ร้องขอ
    • $connect – ใช้สำหรับเมื่อมีผู้ร้องขอเพื่อขอใช้งาน และตรวจพบว่าเป็นการเรียกใช้ Websockets นี้ครั้งแรก (การที่เคยขอใช้ และ Connection ของ WebSocket หมดอายุไป ก็ต้องเรียก Connect ใหม่เช่นกัน)
    • $disconnect – ใช้สำหรับบอกว่าผู้ใช้งานมีการ Disconnect จาก API ไปแล้ว ก็จะทำการตัด Connection ของ WebSocket ออกไปด้วย

2.การเก็บ Connection ID ลง ฐานข้อมูล

โดยปกติถ้าเราเขียน WebSocket ทั่วไป มันจะทำการเก็บ Connection ID ไว้ใน Server แต่เมื่อเราทำบน Serverless อย่าง AWS Lambda ที่ธรรรมชาติของมันพร้อมจะปิดสวิตส์ตัวเองเสมอเมือ่ไม่ได้ใช้งานนานๆ เราจึงต้องเพิ่มการเก็บ Connection ID ลงในฐานข้อมูลแทน เพื่ออ้างอิงไว้ใช้ เมื่อ Lambda เราตื่นขึ้นมาทำงานต่อ

ตัวอย่างการทำงาน AWS Lambda กับ WebSocket

รูปจาก https://aws.amazon.com/th/blogs/compute/announcing-websocket-apis-in-amazon-api-gateway/

ในรูปตัวอย่าง อธิบายการทำงานของระบบ Chat ที่ใช้ Lambda และ WebSocket API โดยมีขั้นตอนคือ

  • ผู้ใช้ เข้ามาใช้งาน Chat Room ซึ่งระบบก็จะทำการ connect ไปที่ WebSocket API
  • ระบบ WebSocket API จะทำการสร้าง Connection ID และส่ง message กลับไปหาผู้ร้องขอตาม URL ที่มีการเรียกใช้มาตอนแรก (URL Callback) เพื่อบอกว่า ทำการเชื่อมต่อสำเร็จ (Connected) พร้อมทั้งเก็บ Connection ID ไว้ในฐานข้อมูลด้วย
  • เมื่อผู้ใช้ส่งข้อความ Chat ไปให้ WebSocket API มันจะส่ง message นั้นไปที่ Reosource ที่เรากำหนดไว้ (ซึ่งในที่นี้คือ Lambda Function สำหรับการ Chat) โดยไม่ต้อง connect ใหม่อีกรอบ
  • message ที่ถูกส่งมา จะถูกกระจายส่งไปหา Connection ทุกคนที่มี Connection ID อยู่ในรายการฐานข้อมูล
  • สุดท้าย เมื่อพบว่าผู้ใช้ออกจากห้อง Chat Room ไปแล้ว ตัว WebSocket API จะ Disconnect ผู้ใช้คนนั้นอัตโนมัติ

ผมทดลองโดยเปิด Connection ไว้ สามหน้าต่าง ซึ่งจะมี Connection ID ต่างกัน ดังนั้น เมื่อผมส่ง message ออกจากหน้าต่างไหนก็ตาม มันจะถูกเผยแพร่ไปหาหน้าต่างอื่นๆ ด้วย ดังนั้น จึงเหมาะกับงานประเภททำงานแบบ Realtime และ Broadcast เช่น Chat, Notification, Bidding หรือแม้แต่ระบบโหวตและนับคะแนนเสียงเลือกตั้ง

ตัวอย่างการทำงานเพื่อคุยข้าม Connection กัน

ตัวอย่างโค้ด ดาวน์โหลดได้ที่ https://github.com/ifew/aws-lambda-websocket

ค่าใช้จ่าย สำหรับ AWS Lambda WebSocket

ค่าใช้จ่ายในส่วนของ AWS Lambda จะเหมือนเดิม ตามที่เคยเขียนเล่าไว้ในบล็อก มาทำความรู้จักกับ Serverless และ AWS Lambda ฉบับคนคิดจะใช้

จะมีที่แตกต่างคือการคิดเงินในฝั่งของ Amazon API Gateway ที่คิดตาม request และ จำนวนนาทีที่มีการเชื่อมต่ออยู่ สามารถดูได้จากตารางด้านล่าง

ถามว่าแพงไหม ผมก็ไม่รู้นะ อยู่ที่การใช้งานของแต่ละระบบ แต่จำนวน request 1พันล้านแรก ราคา $1.15 กับ connection 1 ล้านนาที ราคา $0.288 คงเป็นตัวเลขที่ถูกแสนถูก มากๆ เท่ากับข้าวแกงจานนึง

แต่ตัวแปรสำคัญคงอยู่ที่ Lambda Compute ที่อาจจะแพง ฮ่าๆ เพราะ การทำงาน 1 ชุด จะมี Lambda อย่างน้อย 3 ตัวแน่นอน คือ Connect, Disconnect, Default ส่วน Lambda จะมีค่าใช้จ่ายสูงแค่ไหน ก็กลับไปดูจำนวน request และ compute time (วิธีคิดอ่านได้จากบล็อก มาทำความรู้จักกับ Serverless และ AWS Lambda ฉบับคนคิดจะใช้ )

ข้อจำกัด

  • รองรับผู้ใช้งานได้ 10,000 requests ต่อวินาที (RPS) ต่อ region (สามารถขอเพิ่มจากทาง Amazon ได้)
  • ระบบจะทำการเพิ่มให้ 500 requests ในทุกๆวินาที ต่อ region
  • ส่งข้อมูลผ่าน WebSocket (frame size) ได้ไม่เกิน 32 KB ถ้าต้องใช้มากกว่านั้น ต้องตัดข้อมูลเป็นเฟรมๆเพื่อส่ง, ซึ่งถ้าส่งเกิน ระบบจะทำการ Disconnect อัตโนมัติ
  • ระยะเวลาสำหรับ Connection อยู่ได้ 2 ชั่วโมง
  • เข้าสู่โหมด Idle Connection Timeout ที่เวลา 10 นาที กรณีไม่มีการส่งข้อมูลอะไรผ่าน WebSocket API
  • สามารถทำ AWS Lambda authorizers ต่อ API ได้ 10 ตัว (สามารถขอเพิ่มจากทาง Amazon ได้)
  • สร้าง Routes ต่อ API ได้ไม่เกิน 300 ตัว (สามารถขอเพิ่มจากทาง Amazon ได้)
  • ทำการเชื่อมต่อ API กับ Resource ต่างๆ เช่น AWS Lambda (Integrations) ได้ไม่เกิน 300 ตัว (สามารถขอเพิ่มจากทาง Amazon ได้)
  • สามารถมี Stages ต่อ API ได้ไม่เกิน 10 ตัว (สามารถขอเพิ่มจากทาง Amazon ได้)

สรุป

น่าสนใจมากๆ สำหรับการทำ WebSocket ด้วย Serverless ดูเป็นเรื่องน่าสนุกดีสำหรับผม เหมือนได้เล่นของใหม่ ที่พยายามพลิกแพลง Serverless มาทำ แต่ถามว่าดีหรือไม่ เหมาะสมไหม ตอนนี้มันยังใหม่มาก หาเคสตัวอย่างใหญ่ๆ ไม่เจอ (หรืออาจจะมีแต่ไม่มีใครเปิดเผย) ดังนั้นถ้าให้บอกข้อดีที่นึกออกตอนนี้ ก็คงเรื่องของราคาค่า Server ที่มันถูกลง ตาม benefit เดิมของ Serverless นั่นแหละ, ส่วนข้อเสียที่ผมพบตอนนี้ รู้สึกทดสอบยาก ณ ตอนเขียนบล็อกนี้ยังนึกการทำ Integration Test ไม่ออกเลย นอกจากการ Deploy ขึ้นไปทดสอบบน AWS เอง, หากใครคิดออก หรือมีตัวอย่างเจ๋งๆ ก็แชร์กันได้นะครับ

อ้างอิง