Retour d’expérience SignalR avec load-balancing

Dans cet article je vais parler d’un cas d’utilisation assez avancé de SignalR : faire communiquer plusieurs applications Angular en temps réel via SignalR, chaque application étant load-balancée sur 2 serveurs. Etant donné que c’est un cas avancé, je considère que vous savez déjà comment fonctionne SignalR (si ce n’est pas le cas, il est très facile de trouver des bons tutoriels sur Google).

Architecture

En effet plusieurs applications peuvent communiquer entre elle via SignalR si celles-ci utilisent la même base de données et le même nom pour le hub. Pour un tutoriel sur comment configurer le Scale-Out SQL Server pour SignalR, je vous conseille celui de Microsoft.

Flux du message au sein de l’application

 

 

signalR

Comme vous pouvez le voir avec le schéma ci-dessus, on a décidé de propager le message SignalR au sein de l’application Angular en « broadcastant » un nouveau message. Ceci permet d’avoir une méthode unique pour effectuer la communication entre le serveur (C#) et le client qui ne fait pas intervenir de code métier.

Voici la classe utilisée pour le message :

public class ChannelEvent<T>
{

public string Id { get; private set; }

public string EventName { get; set; }

public string Timestamp { get; private set; } = DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss.fff");

public T Data { get; set; }

public ChannelEvent(string eventName, T data)
{
EventName = eventName;
Data = data;
Id = $"{EventName}:{Guid.NewGuid()}-{Timestamp}";
}

}

Côté client, vu que l’application utilisait Angular, nous avons créé un service pour encapsuler le hub.

Le service : 

import { Injectable, Inject } from "@angular/core";
import { Injectable, Inject } from "@angular/core";
import { Subject } from "rxjs/Subject";
import { Observable } from "rxjs/Observable";
import { Broadcaster } from './BroadCasterService';

@Injectable()export class EventHubService {
   public hubConnection: any;
   private hubProxy: any;
   private lastEventsIds = new Array<string>();

   constructor(    
     @Inject(Broadcaster) private broadcaster: Broadcaster)
  {
    this.hubConnection = window.$.hubConnection(window.location.href);
    this.hubProxy = this.hubConnection.createHubProxy("myHub");
    this.hubConnection.error((error: any) => {
    this.errorSubject.next(error);        });
    this.hubProxy.on("send", (ev: Models.ChannelEvent<any>) =>
    {
       if (this.IsNew(ev)) {
         this.broadcaster.broadcast(ev.EventName, ev.Data);
       }
    });
  }
  /** On évite de publier plusieurs fois le même message côté client. Cela peut arriver si par exemple, une personne est dans plusieurs groupes */
  IsNew(ev: Models.ChannelEvent<any>): boolean {
    if (Enumerable.from(this.lastEventsIds).any(x => x === ev.Id)) {
      return false;
    }
    else {
      this.lastEventsIds.push(ev.Id);
      return true;
    }
  }
}

Le broadcaster : service utilisé dans tout l’application pour faire communiquer les composants entre eux, et utilisé aussi par le service ci-dessus.

@Injectable()
export class Broadcaster {

private _eventBus: Subject<BroadcastEvent>;

constructor() {
 this._eventBus = new Subject<BroadcastEvent>();
 }

broadcast(key: any, data?: any) {
 this._eventBus.next({ key, data });
 }

/**
 * NE PAS OUBLIER DE SE DESINSCRIRE SINON L'INSTANCE RESTE EN MEMOIRE !!
 * @param key
 */
 on<T>(key: any): Observable<T> {

return this._eventBus.asObservable()
 .filter(event => event.key === key)
 .map(event => <T>event.data);
 }
}

interface BroadcastEvent {
 key: any;
 data?: any;
}

A savoir

  • Ne pas hésiter à faire une interface pour tester l’envoi de message et vérifier l’état de la connexion SignalR
  • SQL Server n’est pas viable comme Scale-Out pour SignalR si la contrainte temps réel est forte (le Service Broker peut mettre jusqu’à 3 minutes pour notifier si la charge est très importante)
  • Ne pas créer trop de groupes SignalR (> 1 000 ça ralentit)
  • Faire attention à ce que le load balancer laisse passer les web sockets
  • Prérequis : SQL Server 2005+, Windows Server 2012 / Win8 (iis 8.0)

 

Je pense que ce cas d’utilisation de SignalR, même s’il est complexe peut régulièrement être utilisé en entreprise où un utilisateur doit jongler entre plusieurs applications. Cela lui évite de devoir attendre et de rafraîchir la page manuellement, ou d’être automatiquement prévenu qu’un collègue est déjà en train d’éditer cet objet (contrat, client..).

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s