寫一個nuxt的socket.io server module與後續Apache Proxy設定

最近想把LiveTRA的Firebase RealtimeDatabase拔掉,這個功能是用在顯示於時刻表最上方,若台鐵發生什麼突發事件時,可以做到立即警示的效果。總覺得拔掉有點可惜,又不想單純只用傳統API去撈,這樣就失去即時性。所以索性最後自己用socket.io刻一個socket server。

Nuxt Module

我的構想是,把socket server寫在跟前台同一個nuxt專案中,當nuxt啟動(npm start)時,最好socket也能一起被啟動,這樣我就不用另外改package.json的scripts.start的腳本去另外起一個server。

為了可以跟nuxt一起啟動,想到似乎可以用nuxt module來做。先說,這絕對不是一個好方法。因為當socket server掛掉無法單獨重開,且socket連線過多時造成主機loading過大時,影響到的也不單止是socket server,連同前台也會被影響(因為放同一台)。但會選擇這樣做,單純只是因為是個省成本的方法。畢竟現在網站沒有收入orz(拜託快幫我點廣告XDD

開工吧!先開一個modules資料夾,新增一個檔案socket.js,到nuxt.config.js modules新增module config:

  // Modules: https://go.nuxtjs.dev/config-modules
  modules: [
    // https://go.nuxtjs.dev/axios
    '@nuxtjs/axios',
    // '@nuxtjs/proxy',
    // https://go.nuxtjs.dev/pwa
    '@nuxtjs/pwa',
    // i18n
    'nuxt-i18n',
    // socket
    '~/modules/socket' // 新增剛剛建立的檔案
  ]

安裝socket.io相依套件:

npm install socket.io socket.io-client

開始寫Socket Server,在 socket.js 中透過 nuxt 的 “listen” hook,讓nuxt server啟動時,也一起啟動socket server:

// 引入socket.io
import { createServer } from 'http'
import { Server } from 'socket.io'

export default function (moduleOptions) {
  // 這個hook就是當nuxt server開始listen port的時候會執行
  this.nuxt.hook('listen', () => {
    const httpServer = createServer()
    const io = new Server(httpServer, {
      cors: {
        // 這邊是跨網域的設定,可設定多組
        origins: ['https://xxxxxx.xxx']
      }
    })

    io.on('connection', (socket) => {
      // socket event
      socket.on('my_event', (data) => {
        // do something
      })

      // emit a event
      socket.emit('some_event', 'yoyoyo')
    })

    // 輸入你要聽的port號
    io.listen(1234)
  })
}

前端的部分:

// 引入socket.io client
import { io } from 'socket.io-client'

export default {
  data () {
    return {
      // 給個變數存socket實體
      socket: null
    }
  },

  mounted () {
    this.initSocket()
  },

  methods: {
    // 開始socket連線
    initSocket () {
      // 我這邊用環境變數寫入socket的網址
      this.socket = io(this.$config.LIVETRA_SOCKET)

      this.socket.on('some_event', (data) => {
        alert(data)
      })
    }
  }
}

前端跟Socket Server的重點大概就這些。接下來要部署到beta機來測試了!

Apache Proxy設定

先把code拉下來到測試站,用pm2啟動後,我們就要來進行apache的設定囉。以下是socket.io官方提供的apache設定

Listen 80

ServerName example.com

LoadModule mpm_event_module             modules/mod_mpm_event.so

LoadModule authn_file_module            modules/mod_authn_file.so
LoadModule authn_core_module            modules/mod_authn_core.so
LoadModule authz_host_module            modules/mod_authz_host.so
LoadModule authz_groupfile_module       modules/mod_authz_groupfile.so
LoadModule authz_user_module            modules/mod_authz_user.so
LoadModule authz_core_module            modules/mod_authz_core.so

LoadModule headers_module               modules/mod_headers.so
LoadModule lbmethod_byrequests_module   modules/mod_lbmethod_byrequests.so
LoadModule proxy_module                 modules/mod_proxy.so
LoadModule proxy_balancer_module        modules/mod_proxy_balancer.so
LoadModule proxy_http_module            modules/mod_proxy_http.so
LoadModule proxy_wstunnel_module        modules/mod_proxy_wstunnel.so
LoadModule rewrite_module               modules/mod_rewrite.so
LoadModule slotmem_shm_module           modules/mod_slotmem_shm.so
LoadModule unixd_module                 modules/mod_unixd.so

User daemon
Group daemon

ProxyPass / http://localhost:3000/
RewriteEngine on
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) "ws://localhost:3000/$1" [P,L]

ProxyTimeout 3

但因為我是要放在VirtualHost中,並且我對於apache的設定,算是只能複製貼上的那種程度,本來想說應該只要放以下這些吧:

<VirtualHost *:80>
  ServerAdmin boggyjan@gmail.com
  ServerName xxx.xxx.xxx

  ProxyPass / http://localhost:xxxx/
  RewriteEngine on
  RewriteCond %{HTTP:Upgrade} websocket [NC]
  RewriteCond %{HTTP:Connection} upgrade [NC]
  RewriteRule ^/?(.*) "ws://localhost:xxxx/$1" [P,L]

  ProxyTimeout 30
</VirtualHost>

reload了apache後測了一下,發現http可以連,但ws卻連不到。看了apache的說明,才想到要確認一下mod_proxy_wstunnel module有沒有開。檢查一下 /etc/apache2/mod-enabled發現果然沒有,執行 sudo a2enmod proxy_wstunnel 再執行一次 systemctl reload apache2 即可。

關於這個架構目前還在測試中,因為LiveTRA網站的瞬間流量還很低才敢先這樣搞搞看,之後有什麼心得再來更新。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料