跳到主要内容

适配器

WebSockets模块是平台无关的,因此, 您可以通过使用WebSocketAdapter接口引入自己的库(甚至是本地实现)。 该接口强制实现以下表中描述的几种方法:

  • create:基于传递的参数创建一个socket实例
  • bindClientConnect:绑定客户端连接事件
  • bindClientDisconnect:绑定客户端断开连接事件(可选)
  • bindMessageHandlers:将传入的消息绑定到相应的消息处理程序
  • close:终止服务器实例

扩展socket.io

socket.io包包装在IoAdapter类中。 如果您想增强适配器的基本功能,例如,技术要求需要在负载平衡的多个实例之间广播事件, 您可以扩展IoAdapter并覆盖负责实例化新socket.io服务器的单个方法。 首先,让我们安装所需的包。

注意

要在多个负载平衡实例上使用socket.io, 您必须通过在客户端socket.io配置中设置transports: ['websocket'] 或在负载均衡器中启用基于cookie的路由来禁用轮询。 仅使用Redis是不够的。有关更多信息,请参见此处

npm i --save redis socket.io @socket.io/redis-adapter

安装包后,我们可以创建一个RedisIoAdapter类。

import { IoAdapter } from '@nestjs/platform-socket.io';
import { ServerOptions } from 'socket.io';
import { createAdapter } from '@socket.io/redis-adapter';
import { createClient } from 'redis';

export class RedisIoAdapter extends IoAdapter {
private adapterConstructor: ReturnType<typeof createAdapter>;

async connectToRedis(): Promise<void> {
const pubClient = createClient({ url: `redis://localhost:6379` });
const subClient = pubClient.duplicate();

await Promise.all([pubClient.connect(), subClient.connect()]);

this.adapterConstructor = createAdapter(pubClient, subClient);
}

createIOServer(port: number, options?: ServerOptions): any {
const server = super.createIOServer(port, options);
server.adapter(this.adapterConstructor);
return server;
}
}

然后,简单地切换到新创建的Redis适配器。

const app = await NestFactory.create(AppModule);
const redisIoAdapter = new RedisIoAdapter(app);
await redisIoAdapter.connectToRedis();

app.useWebSocketAdapter(redisIoAdapter);

Ws库

另一个可用的适配器是WsAdapter,它充当框架与集成了高速且经过充分测试的ws库之间的代理。 该适配器完全兼容本机浏览器WebSockets,比socket.io包快得多。 不幸的是,它的可用功能明显较少。然而,在某些情况下,您可能不一定需要它们。

备注

ws库不支持命名空间(socket.io中广泛使用的通信通道)。 然而,为了模仿此功能,您可以在不同路径上 挂载多个ws服务器(例如:@WebSocketGateway({ path: '/users' }))。

要使用ws,首先必须安装所需的包:

npm i --save @nestjs/platform-ws

安装包后,我们可以切换适配器:

const app = await NestFactory.create(AppModule);
app.useWebSocketAdapter(new WsAdapter(app));
提示

WsAdapter@nestjs/platform-ws导入。

高级(自定义适配器)

出于演示目的,我们将手动集成ws库。 正如前面提到的,该库的适配器已经创建并在@nestjs/platform-ws包中以WsAdapter类的形式公开。 以下是简化实现的潜在外观:

ws-adapter.ts
import * as WebSocket from 'ws';
import { WebSocketAdapter, INestApplicationContext } from '@nestjs/common';
import { MessageMappingProperties } from '@nestjs/websockets';
import { Observable, fromEvent, EMPTY } from 'rxjs';
import { mergeMap, filter } from 'rxjs/operators';

export class WsAdapter implements WebSocketAdapter {
constructor(private app: INestApplicationContext) {}

create(port: number, options: any = {}): any {
return new WebSocket.Server({ port, ...options });
}

bindClientConnect(server, callback: Function) {
server.on('connection', callback);
}

bindMessageHandlers(
client: WebSocket,
handlers: MessageMappingProperties[],
process: (data: any) => Observable<any>,
) {
fromEvent(client, 'message')
.pipe(
mergeMap(data => this.bindMessageHandler(data, handlers, process)),
filter(result => result),
)
.subscribe(response => client.send(JSON.stringify(response)));
}

bindMessageHandler(
buffer,
handlers: MessageMappingProperties[],
process: (data: any) => Observable<any>,
): Observable<any> {
const message = JSON.parse(buffer.data);
const messageHandler = handlers.find(
handler => handler.message === message.event,
);
if (!messageHandler) {
return EMPTY;
}
return process(messageHandler.callback(message.data));
}

close(server) {
server.close();
}
}
备注

当您想利用ws库时, 使用内置的WsAdapter而不是创建自己的适配器。

然后,我们可以使用useWebSocketAdapter()方法设置自定义适配器:

main.ts
const app = await NestFactory.create(AppModule);
app.useWebSocketAdapter(new WsAdapter(app));

示例

可在此处找到使用WsAdapter的工作示例。