Eggjs学习系列(六) Socket.IO实践
Socket.IO 是一个基于 Node.js 的实时应用程序框架,在即时通讯、通知与消息推送,实时分析等场景中有较为广泛的应用。
Eggjs提供了 egg-socket.io
插件来实现 websocket 通信,并增加了下列开发规约:
namespace
: 通过配置的方式定义 namespace(命名空间)middleware
: 对每一次 socket 连接的建立/断开、每一次消息/数据传递进行预处理controller
: 响应 socket.io 的 event 事件router
: 统一了 socket.io 的 event 与 框架路由的处理配置方式
安装和配置
npm i egg-socket.io --save
开启插件
// app/config/plugin.ts
import {
EggPlugin } from 'egg';
const plugin: EggPlugin = {
// static: true,
io: {
enable: true,
package: 'egg-socket.io',
},
};
export default plugin;
添加配置
// app/config/config.default.ts
import {
EggAppConfig, EggAppInfo, PowerPartial } from 'egg';
export default (appInfo: EggAppInfo) => {
const config = {
} as PowerPartial<EggAppConfig>;
// add your special config in here
const bizConfig = {
io: {
init: {
}, // 设置引擎, 默认 ws 引擎
namespace: {
// namespace(nsp) 通常意味分配到不同的接入点或者路径。
// 如果客户端没有指定 nsp,则默认分配到 `/` 这个默认的命名空间。
'/io': {
connectionMiddleware: [],
packetMiddleware: [],
},
},
},
};
// the return config will combines to EggAppConfig
return {
...config,
...bizConfig
};
};
使用 redis
egg-socket.io
内置了socket.io-redis
,可在 cluster 模式下,使用 redis 实现信息共享
// app/config/config.default.ts
const bizConfig = {
io: {
init: {
}, // 设置引擎, 默认 ws 引擎
namespace: {
// namespace(nsp) 通常意味分配到不同的接入点或者路径。
// 如果客户端没有指定 nsp,则默认分配到 `/` 这个默认的命名空间。
'/io': {
connectionMiddleware: [],
packetMiddleware: [],
},
},
// 配置 redis
redis: {
host: '127.0.0.1',
port: 6379,
},
},
};
注意:
框架是以 Cluster 方式启动的,而 socket.io 协议实现需要 sticky 特性支持,否则在多进程模式下无法正常工作。 需要在启动命令添加 sticky
参数:
{
"scripts": {
"dev": "egg-bin dev --sticky",
"start": "egg-scripts start --sticky"
}
}
基本使用
在 app
目录下创建 io
用于存放 socket 相关代码
为了使用 Typescript 中的智能提示功能,需要在 index.d.ts
中引入 egg-socket.io
// typings/index.d.ts
import 'egg-socket.io'
引入之后,会发现使用 app.io
等的时候会有代码提示。为了能够使用 socket.io
的智能提示,还需要再安装
yarn add @types/socket.io
首先在 helper 中添加函数,用于封装 socket 交互的基本数据格式
// app/extend/helper.ts
// 添加扩展
export default {
/**
* 封装 socket 请求数据格式
* @param action 事件
* @param payload 数据
* @param metadata 元信息
*/
parseMsg(action, payload = {
}, metadata = {
}) {
// 封装 meta 数据,添加当前时间轴
const meta = Object.assign({
}, {
timestamp: Date.now(),
}, metadata);
// 格式化返回数据
return {
meta,
data: {
action,
payload,
},
};
},
};
Middleware
接下来在 middleware 中间件中处理 socket 连接。
注意: socket 中间件位于 app/io/middleware
目录下,用于处理 socket.io 的请求。
// app/io/middleware/auth.ts
import {
Context } from 'egg';
const PREFIX = 'room';
export default function AuthMiddleware() {
return async (ctx: Context, next: () => Promise<any>) => {
const {
app, socket, logger, helper <