egg-websocket-plugin
TypeScript icon, indicating that this package has built-in type declarations

3.0.0-beta.0 • Public • Published

egg-websocket-plugin

NPM version build status Test coverage David deps Known Vulnerabilities npm download

依赖说明

依赖的 egg 版本

egg 2.x 安装 egg-websocket-plugin@1.0.1 egg 3.x 安装 egg-websocket-plugin@3.0.0+

特性支持

  • [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 异步交流。

License

MIT

Package Sidebar

Install

npm i egg-websocket-plugin

Weekly Downloads

226

Version

3.0.0-beta.0

License

MIT

Unpacked Size

29.9 kB

Total Files

10

Last publish

Collaborators

  • flare