sql >> Base de Datos >  >> NoSQL >> Redis

¿Cómo funciona PubSub en BookSleeve/ Redis?

1:solo hay un canal en su ejemplo (Test ); un canal es solo el nombre utilizado para un intercambio de publicación/suscripción en particular. Sin embargo, es necesario utilizar 2 conexiones debido a detalles de cómo funciona la API de redis. Una conexión que tiene cualquier las suscripciones no pueden hacer nada más excepto:

  • escuchar mensajes
  • administrar sus propias suscripciones (subscribe , psubscribe , unsubscribe , punsubscribe )

Sin embargo, no entiendo esto:

private static Dictionary<string, RedisSubscriberConnection>

No debería necesitar más de una conexión de suscriptor a menos que esté atendiendo algo específico para usted. Una sola conexión de suscriptor puede manejar un número arbitrario de suscripciones. Una revisión rápida de la client list en uno de mis servidores, y tengo una conexión con (al momento de escribir) 23,002 suscripciones. Que probablemente podría reducirse, pero:funciona.

2:las suscripciones de patrones admiten comodines; así que en lugar de suscribirse a /topic/1 , /topic/2/ etc., puede suscribirse a /topic/* . El nombre del real canal usado por publish se proporciona al receptor como parte de la firma de devolución de llamada.

Cualquiera puede funcionar. Cabe señalar que el rendimiento de publish se ve afectado por la cantidad total de suscripciones únicas, pero, francamente, sigue siendo estúpidamente rápido (como en:0 ms), incluso si tiene decenas de miles de canales suscritos usando subscribe en lugar de psubscribe .

Pero desde publish

Complejidad temporal:O(N+M) donde N es el número de clientes suscritos al canal de recepción y M es el número total de patrones suscritos (por cualquier cliente).

Recomiendo leer la documentación redis de pub/sub.

Editar para seguimiento de preguntas:

a) Asumo que tendría que "publicar" sincrónicamente (usando Result o Wait()) si quiero garantizar que el orden de envío de elementos del mismo editor se conserve al recibir elementos, ¿correcto?

eso no hará ninguna diferencia en absoluto; ya que mencionas Result / Wait() , supongo que está hablando de BookSleeve, en cuyo caso el multiplexor ya conserva el orden de los comandos. Redis en sí tiene un solo subproceso y siempre procesará los comandos en una sola conexión en orden. Sin embargo:las devoluciones de llamada en el suscriptor se pueden ejecutar de forma asíncrona y se pueden entregar (por separado) a un subproceso de trabajo. Actualmente estoy investigando si puedo forzar que esto esté en orden desde RedisSubscriberConnection .

Actualización:desde el 1.3.22 en adelante, puede configurar el CompletionMode para PreserveOrder - entonces todas las devoluciones de llamada se completarán secuencialmente en lugar de simultáneamente.

b) después de hacer ajustes de acuerdo con sus sugerencias, obtengo un gran rendimiento al publicar pocos elementos, independientemente del tamaño de la carga útil. Sin embargo, cuando se envían 100 000 elementos o más por el mismo editor, el rendimiento cae rápidamente (a 7 u 8 segundos solo para enviar desde mi máquina).

En primer lugar, ese tiempo suena alto:las pruebas locales me dan (para 100 000 publicaciones, incluida la espera de la respuesta de todas ellas) 1766 ms (local) o 1219 ms (remoto) (eso puede sonar contradictorio, pero mi "local" no lo es) Estoy ejecutando la misma versión de redis; mi "remoto" es 2.6.12 en Centos; mi "local" es 2.6.8-pre2 en Windows).

No puedo hacer que su servidor real sea más rápido o acelerar la red, pero:en caso de que se trate de una fragmentación de paquetes, he agregado (solo para usted) un SuspendFlush() / ResumeFlush() par. Esto deshabilita el vaciado ansioso (es decir, cuando la cola de envío está vacía; aún ocurren otros tipos de vaciado); Puede que esto te ayude:

conn.SuspendFlush();
try {
    // start lots of operations...
} finally {
    conn.ResumeFlush();
}

Tenga en cuenta que no debe Wait hasta que haya reanudado, porque hasta que llame a ResumeFlush() podría haber algunas operaciones aún en el búfer de envío. Con todo eso en su lugar, obtengo (para 100,000 operaciones):

local: 1766ms (eager-flush) vs 1554ms (suspend-flush)
remote: 1219ms (eager-flush) vs 796ms (suspend-flush)

Como puede ver, ayuda más con los servidores remotos, ya que enviará menos paquetes a través de la red.

No puedo usar transacciones porque más adelante los elementos que se publicarán no están todos disponibles a la vez. ¿Hay alguna manera de optimizar con ese conocimiento en mente?

yo creo eso se aborda en lo anterior, pero tenga en cuenta que recientemente CreateBatch también se agregó. Un lote funciona de manera muy parecida a una transacción, solo que sin la transacción. Nuevamente, es otro mecanismo para reducir la fragmentación de paquetes. En su caso particular, sospecho que suspender/reanudar (en color) es su mejor apuesta.

¿Recomienda tener una RedisConnection general y una RedisSubscriberConnection o cualquier otra configuración para que dicho contenedor realice las funciones deseadas?

Siempre que no esté realizando operaciones de bloqueo (blpop , brpop , brpoplpush etc.), o colocando BLOB de gran tamaño en el cable (lo que podría retrasar otras operaciones mientras se borra), entonces una sola conexión de cada tipo generalmente funciona bastante bien. Pero YMMV depende de sus requisitos de uso exactos.