在使用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 : {})
})
})
}
用中文表达的话,大概是这样的。
-
- HTTP服务器:将会将会话管理设置为使用Redis
-
- HTTP服务器:一旦登录完成,将会向客户端返回会话ID
-
- 客户端:通过WebSocket发送会话ID
-
- WebSocket服务器:将会把收到的会话ID与套接字关联并记录
- WebSocket服务器:将会使用会话ID从Redis存储中读取会话数据
需要注意的是与客户端编码有关,需要根据WebSocket连接的情况(未连接、已连接、已断开)来适当地处理。
我认为这个区域的正确答案会因条件而异,所以可能令人困惑。
广告。。。
这篇文章开头提到的网络服务,也使用了这种方法(虽然还有很多不同之处)。
这是为网站制作人员提供的服务,希望您可以随意尝试。(可以免费使用)
我们发布了一个希望网站制作人员使用的网络服务。在创建网站时,需要管理大量的信息和数据,但现在可以整理得井井有条,并且轻松地在团队中共享。我们从去年夏天开始制作,终于公开了。https://t.co/kFOCip3eUD