Skip to content

Config HAProxy เพื่อใช้งาน Nodejs, Socket.io ร่วมกับ Apache web server

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 ต่างๆ ตามรูปข้างล่าง

Server Architecture with HAProxy
Server Architecture with HAProxy

เนื่องจากเราจะรัน 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 ข้างล่างหมายความว่า

  1. ให้ HAProxy listen ที่ port 80
  2. คำสั่ง acl คือการกำหนดเงื่อนไข
  3. use_backend  คือการกำหนด logic การทำงาน ถ้าตรงกับเงื่อนไขก็ให้ใช้ backend ที่กำหนด ตัวอย่างเช่น ถ้ามีการเรียก url localhost/ws ให้ใช้ backend ws_backend
  4. บรรทัดสุดท้ายถ้าไม่ตรงกับเงื่อนไขใดเลยให้ใช้ 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 สีแดงเหมือนเดิมแล้ว

Socket.io working on port 80
Socket.io working on port 80

ตอนนี้เท่าที่ลองใช้งานดูก็ยังไม่เจอปัญหาอะไรครับ เวลาใช้งานร่วมกันก็ดูเนียนดีเพราะไม่ต้องระบุ port แล้ว

อ้างอิง

2 Comments

  1. sompon sompon

    ผมขอปรึกษาเรื่อง websocket ไม่ค่อยเข้าใจเวลาอัพขึ้น digitalocean

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.