egg-websocket-plugin
依赖说明
依赖的 egg 版本
特性支持
- [x] 兼容 egg 自身的 controller
- [x] 兼容 egg 路由中间件
- [x] 不需要像 socket.io 一样开启
sticky
模式 - [x] 支持消息广播订阅
使用方式
yarn add egg-websocket-plugin
1. 开启插件
// config/plugin.js
exports.websocket = {
enable: true,
package: 'egg-websocket-plugin',
};
2. 配置 WebSocket 路由
// app/router.js
app.ws.route('/ws', app.controller.home.hello);
// 可使用路由参数,使用方式同 egg 自身路由
app.ws.route('/foo/:id', app.controller.home.foo);
3. 配置 WebSocket 全局中间件【可选】
注意:配置 WebSocket 全局中间件应当在所有 app.ws.route
注册路由之前进行,否则之前的路由不会应用到全局中间件,且插件会生成一条警告日志
// app/router.js
// 配置 WebSocket 全局中间件
app.ws.use(async (ctx, next) => {
console.log('websocket open');
await next();
console.log('websocket closed');
});
4. 配置路由中间件【可选】
路由会依次用到 app.use, app.ws.use, 以及 app.ws.router 中配置的中间件
// app/router.js
async function middleware(ctx, next) {
console.log('open', ctx.starttime);
await next();
}
// 配置路由中间件
app.ws.route('/ws', middleware, app.controller.home.hello);
5. 禁用 App 全局中间件
插件默认会使用 app.use(...) 注册的中间件,如果你不想使用它们,或者这些插件与 websocket 有冲突,你可以在
config.default.js
中禁用它们
config.websocket = {
useAppMiddlewares: false,
};
6. 在控制器中使用 websocket
websocket 是一个 ws
插件的实例,可阅读 ws 插件的说明文档或 TypeScript 的定义
// app/controller/home.js
import { Controller } from 'egg';
export default class HomeController extends Controller {
async hello() {
const { ctx, app } = this;
if (!ctx.websocket) {
throw new Error('this function can only be use in websocket router');
}
console.log('client connected');
ctx.websocket
.on('message', (msg) => {
console.log('receive', msg);
})
.on('close', (code, reason) => {
console.log('websocket closed', code, reason);
});
}
}
支持 Redis 消息发布订阅【可选】
开启消息广播
注意:egg-websocket-plugin
依赖于 ioredis
但可与其他插件如 egg-redis
共用一个 ioredis
依赖
开启广播相关的 API 需要在 config.default.js 中配置 redis
连接,配置项可参见 ioredis
config.websocket = {
// 配置 websocket 使用 redis 作消息广播,配置项目参见 ioredis
redis: {
host: '127.0.0.1',
port: 6379,
},
};
房间消息广播 API
开启 redis 配置后,即可使用下面的广播 API
1. app.ws.sendTo(room, data)
发送消息到某个房间,将 data 原样发送到客户端连接
-
room
<string>: 房间名 -
data
<string | Buffer>: 发送内容,原样发送
2. app.ws.sendJsonTo(room, data)
发送消息到某个房间,会自动使用 JSON.stringify 对 data 进行序列化
-
room
<string> 房间名 -
data
<any> 发送内容,可进行 JSON 序列化的任意对象
3. ctx.websocket.room.join(room, fn?)
将当前连接加入房间
-
room
<string> 要加入的房间名 -
fn
<function({room, message})> 可选参数,用于配置房间消息处理函数,配置详情见 自定义房间消息处理函数-
room
<string> 消息所属房间名 -
message
<string | Buffer> 房间内的广播消息,消息类型取决于sendTo()
发送时的类型 - 忽略此参数,则该房间的消息将直接发送到客户端连接
- 配置这个参数,则可以手动处理消息发送逻辑,不再自动将房间内的消息发送到客户端连接
-
4. ctx.websocket.room.leave(room)
当前连接离开房间,当前连接关闭后,将自动退出当前连接加入过的房间,因此关闭连接前无需要手动离开房间
-
room
<string> 房间名
5. ctx.websocket.room.sendTo(room, data)
发送消息到某个房间,将 data 原样发送到客户端连接,功能与 app.ws.sendTo 相同
6. ctx.websocket.room.sendJsonTo(room, data)
发送消息到某个房间,会自动使用 JSON.stringify 对 data 进行序列化,功能与
app.ws.sendTo
相同
// 当前连接加入房间 foo,收到房间的消息将直接发送到当前客户端连接
ctx.websocket.room.join('foo');
// 当前连接加入房间 bar,并设置该房间消息处理函数,设置了处理函数将不再直接发送消息到当前客户端
// room: string, message: Buffer | string
ctx.websocket.room.join('bar', ({ room, message }) => {
console.log(room, message);
});
// 当前连接离开房间
ctx.websocket.room.leave('foo');
// 通过当前 ctx 广播消息,所有加入房间的连接会收到这条消息
ctx.websocket.room.sendTo('foo', 'hello foo');
// 也可以通过通过 app 对象来广播消息
app.ws.sendTo('bar', 'hello bar');
自定义房间消息处理函数
注意:使用 room.join()
函数,忽略第二个参数的情况下,房间内的消息会直接发送到当前客户端连接。
如果你需要自定义这部分的处理逻辑,那么可配置 room.join()
的第二个参数。
ctx.websocket.room.join('foo', ({ room, message }) => {
// room: string 房间名
// message: Buffer | string 消息内容
// message 的类型取决于使用 sendTo 发送的是 string 还是 Buffer
console.log(room, message);
});
例子
router.ts
import { Application } from 'egg';
export default (app: Application) => {
const { controller, router } = app;
// 配置 http 路由
router.get('/', controller.home.index);
// 配置 websocket 路由,websocket 可与 http 使用同一个 url,不会导致冲突
app.ws.route('/', controller.home.ws);
};
controller/home.ts
import { Controller } from 'egg';
export default class HomeController extends Controller {
public async index() {
const { ctx, app } = this;
// 向房间 foo 发送消息
app.ws.sendTo('foo', 'hello from index');
ctx.body = 'index';
}
public async ws() {
const { websocket } = this.ctx;
if (!websocket) {
throw new Error('this function can only be use in websocket router');
}
// 加入到房间 foo
websocket.room.join('foo');
}
}
浏览器连接 WebSocket
const ws = new WebSocket('ws://127.0.0.1:7001/');
ws.addEventListener('message', data => {
console.log(data);
});
这时候,访问 http://localhost:7001/ WebSocket 客户端将会收到一条 hello from index
的字符串
使用场景
- 使用 egg 开发标准 WebSocket 服务器
- 微信小程序 WebSocket 服务器
提问交流
请到 issues 异步交流。