Adaptateurs
Le module WebSockets est agnostique, vous pouvez donc apporter votre propre bibliothèque (ou même une implémentation native) en utilisant l'interface WebSocketAdapter
. Cette interface oblige à implémenter quelques méthodes décrites dans le tableau suivant :
create | Crée une instance de socket sur la base des arguments fournis |
bindClientConnect | Lie l'événement de connexion du client |
bindClientDisconnect | Lie l'événement de déconnexion du client (optionnel*) |
bindMessageHandlers | Relie le message entrant au gestionnaire de message correspondant |
close | Met fin à une instance de serveur |
Étendre socket.io#
Le package socket.io est enveloppé dans une classe IoAdapter
. Que se passe-t-il si vous souhaitez améliorer la fonctionnalité de base de l'adaptateur ? Par exemple, vos exigences techniques requièrent une capacité à diffuser des événements à travers de multiples instances de votre service web. Pour cela, vous pouvez étendre IoAdapter
et surcharger une méthode unique dont la responsabilité est d'instancier de nouveaux serveurs socket.io. Mais tout d'abord, installons le package nécessaire.
Attention Pour utiliser socket.io avec plusieurs instances à load-balancées, vous devez soit désactiver le polling en réglant transports : ['websocket']
dans la configuration de socket.io de vos clients ou vous devez activer le routage basé sur les cookies dans votre équilibreur de charge. Redis seul n'est pas suffisant. Voir ici pour plus d'informations.
$ 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(): 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;
}
}
Ensuite, il suffit de basculer vers l'adaptateur Redis nouvellement créé.
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 le WsAdapter
qui agit comme un proxy entre le framework et la bibliothèque ws, rapide et soigneusement testée. Cet adaptateur est entièrement compatible avec les WebSockets natifs des navigateurs et est bien plus rapide que le package socket.io. Malheureusement, il a beaucoup moins de fonctionnalités disponibles par défaut. Dans certains cas, vous n'en avez pas nécessairement besoin.
Astuce La bibliothèquews
ne supporte pas les espaces de noms (canaux de communication popularisés parsocket.io
). Cependant, pour imiter cette fonctionnalité, vous pouvez monter plusieurs serveursws
sur des chemins différents (exemple :@WebSocketGateway({ path : '/users' })
).
Afin d'utiliser ws
, nous devons tout 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));
Astuce LeWsAdapter
est importé de@nestjs/platform-ws
.
Le wsAdapter
est conçu pour gérer les messages au format { event: string, data: any }
. Si vous avez besoin de recevoir et de traiter des messages dans un format différent, vous devrez configurer un analyseur de messages pour les convertir dans le format requis.
const wsAdapter = new WsAdapter(app, {
// Pour traiter les messages au format [event, data]
messageParser: (data) => {
const [event, payload] = JSON.parse(data.toString());
return { event, data: payload };
},
});
Alternativement, vous pouvez configurer l'analyseur de messages après la création de l'adaptateur en utilisant la méthode setMessageParser
.
Avancé (adaptateur personnalisé)#
Pour la démonstration, nous allons intégrer manuellement la bibliothèque ws. Comme mentionné, l'adaptateur pour cette bibliothèque est déjà créé et est exposé depuis le package @nestjs/platform-ws
en tant que 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 = {}): 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();
}
}
Astuce Si vous voulez profiter de la bibliothèque ws, utilisez l'adaptateur intégré WsAdapter
au lieu de créer votre propre adaptateur.
Ensuite, nous pouvons mettre en place un adaptateur personnalisé en utilisant la méthode useWebSocketAdapter()
:
const app = await NestFactory.create(AppModule);
app.useWebSocketAdapter(new WsAdapter(app));
Exemple#
Un exemple de travail utilisant WsAdapter
est disponible ici.