Last updated on August 6, 2018
โพสต์ก่อนๆ ผมเขียนถึงการพัฒนา application ด้วย Nodejs และ Socket.io และจำเป็นต้องใช้ร่วมกันกับ PHP ที่รันบน Apache ปัญหาที่พบก็ตามข้อ 3 ในโพสต์เก่านั่นแหละครับคือทำ ProxyPass จาก port 80 ของ Apache ไปที่ app ที่รันไว้ port อื่นไม่ได้เพราะ Apache ไม่รองรับ Websockets
1.Infrastructure
เนื่องจาก Websockets ยังเป็นเรื่องใหม่อยู่ web server ส่วนมากยังไม่รองรับ ลองเสิร์ชหาวิธีแก้ปัญหาตามเว็บต่างๆ ส่วนมากถ้าเป็น PHP, Apache นิยมใช้ HAProxy เป็น proxy ถ้าเป็น Ubuntu สามารถติดตั้งผ่าน APT ได้เลย
sudo aptitude install haproxy
เราต้องการติดต่อกับ internet ด้วย port 80 ซึ่งเดิมรัน Apache web server ไว้อยู่แล้วเลยต้องเปลี่ยน architecture ของ server นิดหน่อยคือวาง HAProxy ขวาง server, app ตัวอื่นๆ และให้ listen ที่ port 80 แล้วค่อยกระจาย request ไปที่ port ต่างๆ ตามรูปข้างล่าง

เนื่องจากเราจะรัน HAProxy ไว้ที่ port 80 ซึ่งเป็น port เดียวกันกับ Apache web server เพราะฉะนั้นเราต้องเปลี่ยน port ของ Apache web server ไปรันที่ port อื่นก่อนตามรูปข้างบนคือเปลี่ยนไปใช้ port 4000 วิธีเปลี่ยน port ของ Apache (อ้างอิงที่เครื่องผม) ให้แก้ที่ไฟล์ /etc/apache2/httpd.conf หรือถ้า config virtual host ไว้ก็ไปแก้ไฟล์ในไดเรกทอรี /etc/apache2/site-availables/ ให้หมดทุกไฟล์แล้วสั่ง restart apache
sudo service apache2 reload
2.HAProxy Configuration
สมมุติว่า server และ app ทำงานตาม port ต่างๆ ได้แล้วทีนี้ก็ขั้นตอน config HAProxy ผมเจอ tutorial นี้ เขาเขียนไว้ดีมากครับมีแยกเป็นกรณีด้วยว่าจะทำ proxy แบบไหน แบบ sub-domain, แบบ url based และแบบใช้ WebSockets โดยตรง ส่วนตัวผมเองสะดวกทำแบบ url based ก็ config ตามนี้ครับ
ก่อนอื่นเพิ่ม option http-server-close ที่ section defaults
defaults
defaults log global mode http option httplog option dontlognull retries 3 option redispatch option http-server-close maxconn 2000 contimeout 5000 clitimeout 50000 srvtimeout 50000
frontend
ส่วน frontend เป็นส่วนที่ติดต่อกับ internet (ดูรูป architecture ข้างบน) config ข้างล่างหมายความว่า
- ให้ HAProxy listen ที่ port 80
- คำสั่ง acl คือการกำหนดเงื่อนไข
- use_backend คือการกำหนด logic การทำงาน ถ้าตรงกับเงื่อนไขก็ให้ใช้ backend ที่กำหนด ตัวอย่างเช่น ถ้ามีการเรียก url localhost/ws ให้ใช้ backend ws_backend
- บรรทัดสุดท้ายถ้าไม่ตรงกับเงื่อนไขใดเลยให้ใช้ backend www_backend ซึ่งก็คือ Apache web server นั่นเอง
frontend default bind *:80 acl is_www hdr_end(host) -i localhost acl is_api path_beg -i /ws use_backend ws_backend if is_api default_backend www_backend
backend
backend นี้คือ Apache web server กำหนด timeout เป็นมาตรฐานของ web server ทั่วไปคือ 30 วินาที
backend www_backend timeout server 30s server www1 127.0.0.1:4000
backend นี้คือ Nodejs app ที่รันไว้ที่ port 3000 กำหนด timeout ของ server มากหน่อยเพราะเป็นการทำงานแบบ long-polling เลยตั้งค่าไว้ที่ 600 วินาที (มากน้อยตามความเหมาะสม)
backend ws_backend timeout server 600s server ws1 127.0.0.1:3000
เสร็จแล้วเซฟ config ไฟล์แล้วต้อง enable HAProxy ก่อนไม่งั้น HAProxy จะไม่ทำงานโดยแก้ไฟล์ /etc/default/haproxy เปลี่ยนตัวแปรจาก ENABLED=0 เป็น ENABLED=1
# Set ENABLED to 1 if you want the init script to start haproxy. ENABLED=1 # Add extra flags here. #EXTRAOPTS="-de -m 16"
เสร็จแล้ว restart HAProxy
sudo service haproxy restart
3.Nodejs, Socket.io Configuration
Server
เนื่องจากเรากำหนด proxy แบบ url based จำเป็นต้องแก้โค้ดโดยกำหนด option resource ของ socket.io ทั้งฝั่ง server และฝั่ง client ด้วย ฝั่ง server เพิ่ม option ดังนี้
var io = require("socket.io").listen(app, { resource : '/ws/socket.io' });
/ws คือ path ที่เรา config ไว้ที่ HAProxy อย่าลืม “/” ข้างหน้าด้วยนะครับ
Client
ฝั่ง client ก็คล้ายกันเพิ่ม path เป็น
<script type="text/javascript" src="socket.io/socket.io.js"></script> <script type="text/javascript"> var socket = io.connect("http://localhost", { resource : 'ws/socket.io' }); ... ... </script>
ที่สำคัญ resource ฝั่ง client ไม่ต้องมี “/” นำหน้าครับ คิดว่าเป็นบั๊กของ Socket.io เองที่ทำให้ config ไม่เหมือนกัน
ลองเรียก Nodejs, Socket.io application ดู ถ้าทำถูกก็ได้แบบนี้ครับ ใช้ Firebug ดู request จะเห็นว่า connect WebSockets ได้แล้วไม่ขึ้น error สีแดงเหมือนเดิมแล้ว

ตอนนี้เท่าที่ลองใช้งานดูก็ยังไม่เจอปัญหาอะไรครับ เวลาใช้งานร่วมกันก็ดูเนียนดีเพราะไม่ต้องระบุ port แล้ว
[…] port 80 เช่น HAProxy, Nginx เวอร์ชัน 1.3 ขึ้นไป (เคยบล็อกถึงไปแล้ว) สำหรับคนที่ใช้ Nginx เป็น web […]
ผมขอปรึกษาเรื่อง websocket ไม่ค่อยเข้าใจเวลาอัพขึ้น digitalocean