Adaptateurs WebSocket
Le module WebSockets est agnostique à la plateforme, vous pouvez donc apporter votre propre bibliothèque (ou même une implémentation native) en utilisant l’interface WebSocketAdapter
. Cette interface impose d’implémenter quelques méthodes décrites dans le tableau suivant :
Méthode | Description |
---|---|
create | Crée une instance de socket basée sur les arguments passés |
bindClientConnect | Lie l’événement de connexion du client |
bindClientDisconnect | Lie l’événement de déconnexion du client (facultatif*) |
bindMessageHandlers | Lie le message entrant au gestionnaire de messages correspondant |
close | Termine une instance de serveur |
Étendre socket.io
Le package socket.io est encapsulé dans une classe IoAdapter
. Que se passerait-il si vous souhaitiez améliorer la fonctionnalité de base de l’adaptateur ? Par exemple, vos exigences techniques nécessitent une capacité de diffusion d’événements à travers plusieurs instances équilibrées du service web. Pour cela, vous pouvez étendre IoAdapter
et remplacer une seule méthode dont la responsabilité est d’instancier de nouveaux serveurs socket.io. Mais d’abord, installons le package requis.
$ npm i --save redis socket.io @socket.io/redis-adapter
Une fois le package installé, nous pouvons créer une classe 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() { 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) { const server = super.createIOServer(port, options); server.adapter(this.adapterConstructor); return server; }}
Ensuite, changez simplement pour votre nouvel adaptateur Redis.
const app = await NestFactory.create(AppModule);const redisIoAdapter = new RedisIoAdapter(app);await redisIoAdapter.connectToRedis();
app.useWebSocketAdapter(redisIoAdapter);
Bibliothèque Ws
Un autre adaptateur disponible est un WsAdapter
qui agit comme un proxy entre le framework et la bibliothèque ws qui est rapide et soigneusement testée. Cet adaptateur est pleinement compatible avec les WebSockets natifs du navigateur et est beaucoup plus rapide que le package socket.io. Malheureusement, il dispose de fonctionnalités significativement moins nombreuses disponibles en standard. Dans certains cas, vous n’en aurez peut-être pas nécessairement besoin.
Pour utiliser ws
, nous devons d’abord installer le package requis :
$ npm i --save @nestjs/platform-ws
Une fois le package installé, nous pouvons changer d’adaptateur :
const app = await NestFactory.create(AppModule);app.useWebSocketAdapter(new WsAdapter(app));
Avancé (adaptateur personnalisé)
À des fins de démonstration, nous allons intégrer la bibliothèque ws manuellement. Comme mentionné, l’adaptateur pour cette bibliothèque est déjà créé et est exposé à partir du package @nestjs/platform-ws
sous la forme d’une classe WsAdapter
. Voici à quoi pourrait ressembler l’implémentation simplifiée :
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 = {}) { 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(); }}
Ensuite, nous pouvons configurer un adaptateur personnalisé en utilisant la méthode useWebSocketAdapter()
:
const app = await NestFactory.create(AppModule);app.useWebSocketAdapter(new WsAdapter(app));
Exemple
Un exemple fonctionnel qui utilise WsAdapter
est disponible ici.