在使用Express时,通过WebSocket共享HTTP会话的方法如下

最近发布了专为网站开发者设计的网络服务,这是系列文章中的第三篇。希望能够对考虑尝试个人开发网络服务的人提供参考。

在使用Node.js服务器和WebSocket时,可能会遇到希望在WebSocket中读取http会话数据的情况。
在本文中,我想写一下如何在WebSocket中共享http会话(更准确地说是从WebSocket中只读取)的方法。

将Redis用作为会话存储

其实即使不使用Redis也可以实现,但考虑到以下原因还是决定使用Redis。

    • Nodoサーバで全部受け止めるのではなく負荷を分散させておきたい

 

    • 開発時にNodeサーバを起動し直してもセッションを維持できて便利

 

    複数のNodeサーバでセッションを共有できるので、機能によってサーバ(コンテナ)を分けられる

Redis是一个网络连接的内存数据存储,主要用于处理键值对类型的数据,并被称为一种NoSQL数据库。

通过将会话数据写入Redis,我们希望能够在多个程序中共享会话并进行参考。

使用WebSocket调用HTTP会话

我有一段代码,虽然有点长,但我会分享给你。

const express = require('express')
const WebSocket = require('ws')

const Redis = require('ioredis')
const Session = require('express-session')
const RedisStore = require('connect-redis')(Session)

// ---------------------------------------------------
// sessionデータを保管するstoreを用意する
// ---------------------------------------------------

// Redisのhostとportは環境変数から読み込む
const { REDIS_HOST, REDIS_PORT } = process.env

// Redis用インスタンスを生成する
// セッション&通知バッファリング用
const redis = new Redis({ 
  host: REDIS_HOST || 'redis',   // ※Dockerで使用する前提でこんな風に設定してます
  port: REDIS_PORT || 6379
})

// セッションストアを生成する
const store = new RedisStore({ client: redis })

// ---------------------------------------------------
// ここからexpressの設定(httpセッション)
// ---------------------------------------------------

// expressを生成する
const app = express()
app.use(express.json())

// Redisに紐づいたセッションプロバイダーを作る
const session = Session({
  store,
  secret: 'hogehoge',  // お好きにsecretを指定してください
  resave: false,
  saveUninitialized: false,
  cookie: {
    secure: 'auto'
  }
})

// expressにセッションプロバイダーを紐づける
app.use(session)

// port 3000 でlinten開始
const httpServer = app.listen(3000)


// expressを初期化する段階で、Redisに紐づいたセッションプロバイダーと差し替えるだけなので、
// すでに稼働しているプログラムの改変も最小限の作業で済む(かも)

// あとは普通にexpressの処理を記述すればOK!
...
...
...

// ログイン処理
app.post('login', (req, res, next) => {
  // ログインチェック
  ...
  ...
  // ログイン完了時の処理
  // DBなどから取得したユーザ情報をセッションに書き込むイメージ user = ユーザ情報
  req.session.user = user  
  // ログイン成功時にセッションID(req.sessionID)をクライアントに返す!  
  // これを後でWebSocketを通して返却してもらい紐づけに使用するべし
  res.json({ login: true, sessId: req.sessionID })
})

// ---------------------------------------------------
// ここからWebSocketの設定
//

// WebSocketサーバを生成する
const wsServer = new WebSocket.Server({ server: httpServer.server })

wsServer.on('connection', (ws) => {
  ws.on('open', () => {
    // ここでは、redisSessionId というパラメータにセッションIdを保管することにします
    ws.redisSessionId = ''
  })

  ws.on('message', (message) => {
    // データを復号する
    const msg = JSON.parse(message)
    // セッションidをwsに紐づける処理
    // この例では、クライアントから次のようなデータを受け取る想定
    // { cmd: 'connect_session', sessionID: 'xxxxxxxxxxxxx' }
    if (msg.cmd === 'connect_session') {
      ws.redisSessionId = msg.sessionID
      // クライアントにセッションidを受け取ったことを報告しておく
      return ws.send(JSON.stringify({
        result: 'received_session_id'
      }))
    }
    ...
    ...   
  })

  ...
  ...
})


// ---------------------------------------------------
// 何らかのイベント処理(WebHookなど)
//

const hogeEvent = (params) => {
  // wsServerには複数のsocketが接続されている
  // wsServer.clientsをループで処理して個々のWebSocketに対して処理を行う
  wsServer.clients.forEach(async (ws) => {
    // Sessionを取得する
    const session = !ws.redisSessionId
      ? await getSession(ws.redisSessionId)  // セッションデータ読込関数を呼ぶ
      : {}
    console.log(session.user)
    ...
    ...
  })
}


// ---------------------------------------------------
// セッションデータの読み込み関数
//
const getSession = (sid) => {
  return new Promise((resolve, reject) => {
    // Redisに紐づいたセッションストアからセッションデータを取得する
    store.get(sid, (err, session) => {
      if (err) {
        return reject(err)
      }
      resolve(session ? session : {})
    })
  })
}

用中文表达的话,大概是这样的。

    1. HTTP服务器:将会将会话管理设置为使用Redis

 

    1. HTTP服务器:一旦登录完成,将会向客户端返回会话ID

 

    1. 客户端:通过WebSocket发送会话ID

 

    1. WebSocket服务器:将会把收到的会话ID与套接字关联并记录

 

    WebSocket服务器:将会使用会话ID从Redis存储中读取会话数据

需要注意的是与客户端编码有关,需要根据WebSocket连接的情况(未连接、已连接、已断开)来适当地处理。

我认为这个区域的正确答案会因条件而异,所以可能令人困惑。

广告。。。

这篇文章开头提到的网络服务,也使用了这种方法(虽然还有很多不同之处)。

这是为网站制作人员提供的服务,希望您可以随意尝试。(可以免费使用)

我们发布了一个希望网站制作人员使用的网络服务。在创建网站时,需要管理大量的信息和数据,但现在可以整理得井井有条,并且轻松地在团队中共享。我们从去年夏天开始制作,终于公开了。https://t.co/kFOCip3eUD

bannerAds