NestJS Logo

适配器

WebSocket 模块与平台无关,因此,你可以通过使用 WebSocketAdapter 接口来使用自己的库(甚至是原生实现)。该接口强制实现了以下表格中描述的几个方法:

create基于传递的参数创建一个套接字实例。
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
JS 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
JS TS

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

示例#

一个使用 WsAdapter 的工作示例可以在这里找到。

支持一下