Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/flarestart/egg-websocket-plugin

A standard WebSocket plugin for egg.js
https://github.com/flarestart/egg-websocket-plugin

egg egg-websocket plugin websocket ws

Last synced: 3 months ago
JSON representation

A standard WebSocket plugin for egg.js

Awesome Lists containing this project

README

        

# egg-websocket-plugin

[![NPM version][npm-image]][npm-url]
[![build status][travis-image]][travis-url]
[![Test coverage][codecov-image]][codecov-url]
[![David deps][david-image]][david-url]
[![Known Vulnerabilities][snyk-image]][snyk-url]
[![npm download][download-image]][download-url]

[npm-image]: https://img.shields.io/npm/v/egg-websocket-plugin.svg?style=flat-square
[npm-url]: https://npmjs.org/package/egg-websocket-plugin
[travis-image]: https://img.shields.io/travis/flarestart/egg-websocket-plugin.svg?style=flat-square
[travis-url]: https://travis-ci.org/flarestart/egg-websocket-plugin
[codecov-image]: https://img.shields.io/codecov/c/github/flarestart/egg-websocket-plugin.svg?style=flat-square
[codecov-url]: https://codecov.io/github/flarestart/egg-websocket-plugin?branch=master
[david-image]: https://img.shields.io/david/flarestart/egg-websocket-plugin.svg?style=flat-square
[david-url]: https://david-dm.org/flarestart/egg-websocket-plugin
[snyk-image]: https://snyk.io/test/npm/egg-websocket-plugin/badge.svg?style=flat-square
[snyk-url]: https://snyk.io/test/npm/egg-websocket-plugin
[download-image]: https://img.shields.io/npm/dm/egg-websocket-plugin.svg?style=flat-square
[download-url]: https://npmjs.org/package/egg-websocket-plugin

## 依赖说明

### 依赖的 egg 版本

✅ egg 2.x 安装 [email protected]
✅ egg 3.x 安装 [email protected]+

## 特性支持

- [x] 兼容 egg 自身的 controller
- [x] 兼容 egg 路由中间件
- [x] 不需要像 socket.io 一样开启 `sticky` 模式
- [x] 支持消息广播订阅

## 使用方式

```bash
yarn add egg-websocket-plugin
```

### 1. 开启插件

```js
// config/plugin.js
exports.websocket = {
enable: true,
package: 'egg-websocket-plugin',
};
```

### 2. 配置 WebSocket 路由

```js
// 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` 注册路由之前进行,否则之前的路由不会应用到全局中间件,且插件会生成一条警告日志

```js
// 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 中配置的中间件**

```js
// 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` 中禁用它们

```js
config.websocket = {
useAppMiddlewares: false,
};
```

### 6. 在控制器中使用 websocket

websocket 是一个 `ws` 插件的实例,可阅读 [ws](https://www.npmjs.com/package/ws) 插件的说明文档或 TypeScript 的定义

```js
// 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](https://www.npmjs.com/package/ioredis)

```typescript
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})> 可选参数,用于配置房间消息处理函数,配置详情见 [自定义房间消息处理函数](#roomhandler)
- `room` <string> 消息所属房间名
- `message` 房间内的广播消息,消息类型取决于 `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` 相同

```typescript
// 当前连接加入房间 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()` 的第二个参数。

```typescript
ctx.websocket.room.join('foo', ({ room, message }) => {
// room: string 房间名
// message: Buffer | string 消息内容
// message 的类型取决于使用 sendTo 发送的是 string 还是 Buffer
console.log(room, message);
});
```

### 例子

router.ts

```typescript
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

```typescript
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

```javascript
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](https://github.com/flarestart/egg-websocket-plugin/issues) 异步交流。

## License

[MIT](LICENSE)