Introducción a la escalabilidad horizontal en SignalR

por Patrick Fletcher

Advertencia

Esta documentación no se aplica a la última versión de SignalR. Eche un vistazo a ASP.NET Core SignalR.

Versiones de software usadas en este tema

Versiones anteriores de este tema

Para obtener información sobre versiones anteriores de SignalR, consulte Versiones anteriores de SignalR.

Preguntas y comentarios

Deje sus comentarios sobre este tutorial y sobre lo que podríamos mejorar en los comentarios en la parte inferior de la página. Si tiene alguna pregunta que no esté directamente relacionadas con el tutorial, puede publicarla en el foro de ASP.NET SignalR o en StackOverflow.com.

En general, hay dos maneras de escalar una aplicación web: escalar verticalmente y escalar horizontalmente.

  • El escalado vertical significa usar un servidor mayor (o una máquina virtual mayor) con más RAM, CPU, etc.
  • El escalado horizontal significa agregar más servidores para administrar la carga.

El problema con el escalado vertical es que alcanza rápidamente un límite en el tamaño de la máquina. Más allá de eso, debe escalar horizontalmente. Sin embargo, al escalar horizontalmente, se puede enrutar a los clientes a distintos servidores. Un cliente conectado a un servidor no recibirá mensajes enviados desde otro servidor.

Diagram that shows arrows going from Clients to Load Balancer to servers.

Una solución consiste en reenviar mensajes entre servidores mediante un componente denominado backplane. Si se habilita un backplane, cada instancia de aplicación envía mensajes al backplane y este los reenvía a las demás instancias de aplicación. (En electrónica, un backplane es un grupo de conectores paralelos. Por analogía, un backplane de SignalR conecta varios servidores.)

Diagram that shows arrows going from Signal R App server to Backplane to Signal R App server to computers.

SignalR proporciona actualmente tres backplanes:

  • Azure Service Bus. Service Bus es una infraestructura de mensajería con la que los componentes pueden enviar mensajes acoplados de forma flexible.
  • Redis. Redis es un almacén de clave-valor en memoria. Redis admite un patrón de publicación/suscripción ("pub/sub") para enviar mensajes.
  • SQL Server. El backplane de SQL Server escribe mensajes en tablas SQL. El backplane usa Service Broker para una mensajería eficaz; sin embargo, también funciona aunque Service Broker no esté habilitado.

Si implementa la aplicación en Azure, considere la posibilidad de usar el backplane de Redis mediante Azure Redis Cache. Si la va a implementar en su propia granja de servidores, tenga en cuenta los backplanes de SQL Server o Redis.

Los temas siguientes contienen tutoriales paso a paso para cada backplane:

Implementación

En SignalR, cada mensaje se envía mediante un bus de mensajes. Un bus de mensajes implementa la interfaz IMessageBus, que ofrece una abstracción de publicación/suscripción. Los backplanes sustituyen el IMessageBus predeterminado por un bus diseñado para el backplane en cuestión. Por ejemplo, el bus de mensajes para Redis es RedisMessageBus y usa el mecanismo pub/sub de Redis para enviar y recibir mensajes.

Cada instancia del servidor se conecta al backplane mediante el bus. Cada mensaje que se envía llega al backplane y este, a su vez, lo envía a cada servidor. Cuando un servidor recibe un mensaje del backplane, coloca el mensaje en su caché local. A continuación, el servidor entrega mensajes a los clientes desde su caché local.

Para cada conexión de cliente, se realiza un seguimiento del progreso del cliente en la lectura de la secuencia de mensajes mediante un cursor. (Un cursor representa una posición en la secuencia de mensajes.) Si un cliente se desconecta y vuelve a conectarse luego, solicita al bus los mensajes que han llegado después del valor del cursor del cliente. Lo mismo sucede cuando una conexión utiliza sondeos largos. Una vez completada una solicitud de sondeo larga, el cliente abre una nueva conexión y solicita mensajes que hayan llegado después del cursor.

El mecanismo del cursor funciona incluso si se enruta a un cliente a un servidor diferente al volver a conectarse. El backplane es consciente de todos los servidores y da igual a qué servidor se conecte un cliente.

Limitaciones

Con un backplane, el rendimiento máximo de los mensajes es menor que cuando los clientes hablan directamente con un único nodo de servidor. Esto se debe a que el backplane reenvía todos los mensajes a cada nodo, por lo que el backplane puede convertirse en un cuello de botella. Que esta limitación suponga un problema depende de la aplicación. Por ejemplo, estos son algunos escenarios típicos de SignalR:

  • Difusión de servidor (por ejemplo, teletipo de bolsa): los backplanes funcionan bien en este escenario, ya que el servidor controla la velocidad a la que se envían los mensajes.
  • Cliente a cliente (por ejemplo, chat): en este escenario, el backplane podría convertirse en un cuello de botella si el número de mensajes se escala con el número de clientes; es decir, si la tasa de mensajes crece proporcionalmente a medida que se unen más clientes.
  • Tiempo real de alta frecuencia (por ejemplo, juegos en tiempo real): no se recomienda un backplane para este escenario.

Habilitación del seguimiento para el escalado horizontal de SignalR

Para habilitar el seguimiento de los backplanes, agregue las secciones siguientes al archivo web.config, en el elemento configuration raíz:

<configuration>
  <system.diagnostics>
    <sources>
      <source name="SignalR.SqlMessageBus">
        <listeners>
          <add name="SignalR-Bus" />
        </listeners>
      </source>
      <source name="SignalR.ServiceBusMessageBus">
        <listeners>
          <add name="SignalR-Bus" />
        </listeners>
      </source>
      <source name="SignalR.ScaleoutMessageBus">
        <listeners>
          <add name="SignalR-Bus" />
        </listeners>
      </source>
    </sources>
    <switches>
      <add name="SignalRSwitch" value="Verbose" />
      <!-- Off, Critical, Error, Warning, Information, Verbose -->
    </switches>
    <sharedListeners>
      <add name="SignalR-Bus" 
          type="System.Diagnostics.TextWriterTraceListener" 
          initializeData="bus.log.txt" />
    </sharedListeners>
    <trace autoflush="true" />
  </system.diagnostics>
  . . .
</configuration>