โดยปกติ Web Dev ทั่วๆไปก็จะเจองาน Devๆ ความท้าทายอยู่ที่ตัวงานเช่นความซับซ้อนของ layout (Front-end Dev) หรือการวางโครงสร้าง DB ทำตัว Backend (Backend Dev) และจะท้าทายไปอีกหากทำทั้งหมด (Full-Stack Dev) ซึ่งผมเองก็เคยเป็นหนึ่งในนั้น และด้วยความที่บริษัทที่ทำงานอยู่ค่อนข้างเปิดกว้างให้ Dev สามารถย้ายโปรเจคได้หากมีความต้องการ และมีความเหมาะสม

แรกๆ ก็ทำ Web Dev นั่นแหละ ใช้เทคโนโลยีที่(รู้สึกว่า)ค่อนข้างเยอะ ทั้ง Golang, MongoDB, React, VueJS, Scss เต็มหมด ยังไม่รวมพวก Webpack, NodeJS อีก หลังจากนั้น บริษัทได้เปิดโปรเจคใหม่ เป็น Live Streaming จ้า คือด้วยความที่ตอนนั้นเคยทำแต่ Web Dev แล้วบังเอิ้ญ เสนอหน้า อยากทำ Android Dev ขึ้นมา (ตอนนั้นก็เริ่มเบื่อเว็บแล้วแหละ เพราะทำมานานมากแล้ว) พอมาทำคือก็สนุกดี แต่ไม่กี่เดือนหลังจากนั้น Server Engineer ลาออก ทำให้ผมต้องรับบทบาทในการดูแลตัว Server ด้วย และด้วยความที่ Engineer คนเก่าวางระบบไว้ค่อนข้างดี ดังนั้นทุกอย่างจะเป็นระบบ แม้แต่ตัวโค้ดเอง เลยอยากจะมาเล่าการทำตรงส่วนนี้แบบคร่าวๆ ให้ได้อ่านกัน เพราะในไทยคิดว่ามีไม่กี่บริษัทที่ทำ Live Streaming Platform เอง

ตัวระบบนั้นประกอบด้วย 7 ส่วนหลักๆ คือ

  1. API Server ตัวนี้เราใช้ Golang และเป็นฐานข้อมูล SQL เนื่องจาก platform ที่ทำก็จะมีความ Social หน่อยๆ ข้อมูลมีความ Relate สูงมาก และยังต้องใช้ Transaction ที่ค่อนข้างหนักเลยเลือกเป็น SQL
  2. Chat Server ตัวนี้เป็น WebSocket ที่ใช้ Golang เขียนอีกที และมี Redis Server เอาไว้ทำ In-Memory Caching ซึ่งก็ไม่ค่อยเข้าไปแตะในส่วนนี้เท่าไหร่ เพราะพี่คนเก่าทำไว้ครบแล้ว ส่วนที่น่าสนใจคือระบบ Transaction โดยเวลา User ส่งสติกเกอร์ให้ Caster แล้ว เราจะยังไม่ตัดยอดเงินจนกระทั่ง Caster ส่ง Transaction กลับมา ถึงจะหัก เพื่อให้ Make Sure ว่า Caster จะได้รับของจริงๆ
  3. Admin Server อันนี้เป็นตัว Back-office ไว้จัดการข้อมูลต่างๆ ทั้งหมด รวมทั้งควบคุม Live Server
  4. Database Server เป็น PostgreSQL ที่ทำ Auto-Backup ขึ้น Google Storage วันละสองครั้ง
  5. Live Server ตัวนี้นับว่ายากสุด ความจริงคือโดยทฤษฎีแล้วมันไม่น่าจะมีอะไรมาก จะขอเล่าเป็น Partๆ ไป
    1. Live Server เราใช้ NGINX-RTMP ซึ่งรันอยู่บน Server 20 ตัว และมีระบบ Load-Balancing แบบ Least Connections ภายใน Server จะมี Docker Image ไว้ ซึ่งเราได้ปรับ callback ของตัว Server เองให้ทำการเชคกับ API Server อยู่ตลอดเวลา เพื่อให้เราสามารถรู้ว่า Live กำลังดำเนินอยู่ จบไปแล้ว หรือหลุด ซึ่งเวลาเรา Scale Server ก็จะทำโดยการใช้ Image ที่มีอยู่ไปรันต่อได้เรื่อยๆ แล้วเปิดใช้ได้โดยทันที
    2. ภายใน NGINX-RTMP ก็ได้ใช้ ffmpeg ในการ convert/process video แล้วก็ปล่อย Output Bitrate ที่ 1128kbps (ความจริงเราเคยปล่อย 3 bitrate แต่ตอนนี้ปล่อย Bitrate เดียว)
  6. Recorder Server เอาไว้สำหรับอัดวีดีโอย้อนหลัง ตัวนี้จะถูกสั่งจาก API หลังจากที่ Caster สร้าง Live ขึ้นมา และจะ Move ไป Google Storage ทุกๆ วันเช่นเดียวกับ Database
  7. Live Forwarder Server อันนี้เป็น Server ที่เอามาเปิดปิดใช้เป็นบางเวลา ใช้สำหรับ Forward Streaming ไปที่ Service อื่นๆ เช่น User Live ในแอพเรา แล้วเราอยากให้มันขึ้นบน Facebook Page ด้วย เราก็จะเปิด Server ตัวนี้ขึ้นมาแล้วใช้งาน แล้วปิดไปหลังจากใช้ (ไม่ได้เปิดตลอด เพราะนานๆ ครั้งเราถึงใช้ทีนึง) ใช้ ffmpeg

ปัญหาช่วงแรกๆ ของเราคือไม่สามารถ Track Live นับเวลาได้เลยเพราะ User ก็ใช้อินเทอร์เน็ตหลากหลายความเร็วและด้วยความที่ NGINX-RTMP ก็ไม่ได้ถูก config ให้เข้าที่เข้าทาง (และผมเองก็ยังเป็น junior อยู่) ก็เลยไม่สามารถ config ได้ เราเลยตกลงกันในทีมว่า งั้นลองใช้ WebSocket ไหม เพราะถ้า WebSocket หลุด แปลว่าห้องนั้น Live จบแล้ว ซึ่งปัญหาก็คลี่คลายไปช่วงนึง

จนกระทั่ง มันไม่ได้เป็นไปตามที่คิด ความชิบหายกลับมาเยือนอีกครั้ง คือ WebSocket เป็นอะไรที่ Sensitive มาก บางครั้งมันหลุดไปเอง แล้วต่อใหม่เองบ้าง หลุดหายไปเลยบ้าง ทั้งๆ ที่ Live ยังคงดำเนินอยู่

ไม่ได้การ ต้องแก้ ก็เลยไปนั่งอ่าน Docs ของ NGINX-RTMP แล้วลองปรับไปปรับมา สรุปไปเจอค่าที่ให้เชค Timeout อยู่ ก็เลยลองปรับ

ปัญหาคลี่คลาย Live สามารถจบเองได้ และเวลาก็ถูกต้อง

ปัญหาต่อมา Caster หลุด ส่วนนี้เรายังคงตรวจสอบกันอยู่ ว่ามันเกิดจากอะไรได้บ้าง เาเข้าใจดีว่ามันคงแก้ไม่ได้ 100% แต่เราก็จะทำให้มันเหลือน้อยที่สุด ไว้แก้ได้แล้วจะมาเล่าต่อทีหลังถ้าแก้ได้และไม่ลืม

คำถามต่อมา แล้วเรา Deploy ระบบกันยังไง

แน่นอนเมื่อทำอะไรที่มัน Real-time มันจะไม่ได้ Deploy ง่ายแบบ Web ทั่วๆไป เพราะโจทย์ของเราคือ Deploy ยังไงก็ได้ไม่ให้ Caster หรือ User หลุดเลย

ทีนี้มาเริ่มลิส สิ่งที่เกิดขึ้นเมื่อเรา Build & Deploy Golang ซึ่งมีอยู่ 3 Process คือ

  1. API Process ส่วนนี้ระบบที่ต้องมา fetch มันไปตลอดเวลาคือตัว NGIX-RTMP นั่นแปลว่า Process ดับเมื่อไหร่ ชิบหายทันที ดังนั้นเราต้อง restart ให้ไวที่สุด (NGINX-RTMP มันจะ fetch เป็น period อยู่ ดังนั้นยังพอมีทาง แต่ต้อง restart แบบรวดเร็วทันที ซึ่งแน่นอนไม่ Graceful และในอนาคตเราจะทำให้มัน Graceful ให้ได้!)
  2. Chat Process ส่วนนี้ถ้า Restart คือทุกคนหลุดจากแชทหมดทันที ชิบหายไม่น้อยไปกว่าด้านบน (ส่วนนี้อยู่ระหว่างการศึกษาเพิ่มเติม เพื่อให้มัน auto-reconnect ในกรณีที่ Client หลุด หรือ Server Restart)
  3. Admin Process อันนี้หายห่วง เพราะไม่มีอะไรเชื่อมโยงเลย

ประเด็นคือเราต้อง Reload Process ใหม่ แน่นอนการทำให้มัน Graceful Reload นี่จะเริ่มยากขึ้นมาทันใด เพราะมัน Realtimeดังนั้นเราจะแบ่งมันเป็น 3 Scenarios คือ

  • หากมีการแก้แค่ API/Admin เราสามารถ Build & Deploy ได้ทันที อันนี้ไม่มีปัญหา
  • หากม่ีการแก้ Chat Server เราจะทำการ Deactive Live Server ทุกตัว ในที่นี้คือปิดไม่ให้สามารถสร้าง Live ใหม่เพิ่มได้ และหลังจากนั้นก็รอให้ User ออกจนหมด Live ที่ค้างอยู่ จบหมด เลยเริ่มขึ้นตอนการ Deploy
  • หากมีการแก้ Live Server สมมติว่าเรา มี Server A B C หากเราจะทำการแก้ Config อะไรบางอย่าง เราอาจต้อง Launch Server ใหม่มา สมมติเป็น D E F ที่เป็น Config ที่แก้แล้ว และทำการ Deactive Server A B C รอให้ User Live จนหมด แล้ว Active อีกครั้ง

ครั้งหน้าเดี๋ยวมาเล่าความพังพินาศของ Android AppHappy Coding !