1 :il n'y a qu'un seul canal dans votre exemple (Test
); un canal est simplement le nom utilisé pour un échange pub/sub particulier. Il faut cependant utiliser 2 connexions en raison des spécificités du fonctionnement de l'API redis. Une connexion qui a tout les abonnements ne peuvent rien faire d'autre que :
- écouter les messages
- gérer ses propres abonnements (
subscribe
,psubscribe
,unsubscribe
,punsubscribe
)
Cependant, je ne comprends pas ceci :
private static Dictionary<string, RedisSubscriberConnection>
Vous ne devriez pas avoir besoin de plus d'une connexion d'abonné, sauf si vous vous occupez de quelque chose de spécifique pour vous. Une seule connexion d'abonné peut gérer un nombre arbitraire d'abonnements. Une vérification rapide de la client list
sur l'un de mes serveurs, et j'ai une connexion avec (au moment de la rédaction) 23 002 abonnements. Ce qui pourrait probablement être réduit, mais :ça marche.
2 :les abonnements aux modèles prennent en charge les caractères génériques ; donc plutôt que de s'abonner à /topic/1
, /topic/2/
etc, vous pouvez vous abonner à /topic/*
. Le nom du réel canal utilisé par publish
est fourni au destinataire dans le cadre de la signature de rappel.
L'un ou l'autre peut fonctionner. Il convient de noter que les performances de publish
est impacté par le nombre total d'abonnements uniques - mais franchement, c'est toujours stupidement rapide (comme dans :0 ms) même si vous avez des dizaines de milliers de chaînes abonnées en utilisant subscribe
plutôt que psubscribe
.
Mais depuis publish
Complexité temporelle :O(N+M) où N est le nombre de clients abonnés au canal de réception et M est le nombre total de modèles abonnés (par n'importe quel client).
Je recommande de lire la documentation Redis de pub/sub.
Modifier pour suivre les questions :
a) Je suppose que je devrais "publier" de manière synchrone (en utilisant Result ou Wait()) si je veux garantir que l'ordre d'envoi des éléments du même éditeur est préservé lors de la réception des éléments, n'est-ce pas ?
cela ne fera aucune différence; puisque vous mentionnez Result
/ Wait()
, je suppose que vous parlez de BookSleeve - auquel cas le multiplexeur préserve déjà l'ordre des commandes. Redis lui-même est à thread unique et traitera toujours les commandes sur une seule connexion dans l'ordre. Cependant :les rappels sur l'abonné peuvent être exécutés de manière asynchrone et peuvent être transmis (séparément) à un thread de travail. J'étudie actuellement si je peux forcer cela à être dans l'ordre de RedisSubscriberConnection
.
Mise à jour :à partir de la version 1.3.22, vous pouvez définir le CompletionMode
à PreserveOrder
- alors tous les rappels seront effectués séquentiellement plutôt que simultanément.
b) après avoir effectué des ajustements en fonction de vos suggestions, j'obtiens d'excellentes performances lors de la publication de quelques éléments, quelle que soit la taille de la charge utile. Cependant, lors de l'envoi de 100 000 éléments ou plus par le même éditeur, les performances chutent rapidement (jusqu'à 7-8 secondes uniquement pour l'envoi depuis ma machine).
Premièrement, ce temps semble élevé - tester localement j'obtiens (pour 100 000 publications, y compris l'attente de la réponse pour toutes) 1766 ms (local) ou 1219 ms (distant) (cela peut sembler contre-intuitif, mais mon "local" n'est pas t exécutant la même version de redis ; ma "télécommande" est 2.6.12 sur Centos ; mon "local" est 2.6.8-pre2 sur Windows).
Je ne peux pas rendre votre serveur actuel plus rapide ou accélérer le réseau, mais :au cas où il s'agirait d'une fragmentation de paquets, j'ai ajouté (juste pour vous) un SuspendFlush()
/ ResumeFlush()
paire. Cela désactive le vidage hâtif (c'est-à-dire lorsque la file d'attente d'envoi est vide ; d'autres types de vidage se produisent encore ); vous pourriez trouver cela utile :
conn.SuspendFlush();
try {
// start lots of operations...
} finally {
conn.ResumeFlush();
}
Notez que vous ne devez pas Wait
jusqu'à ce que vous ayez repris, car jusqu'à ce que vous appeliez ResumeFlush()
il pourrait y avoir encore des opérations dans le tampon d'envoi. Avec tout cela en place, j'obtiens (pour 100 000 opérations) :
local: 1766ms (eager-flush) vs 1554ms (suspend-flush)
remote: 1219ms (eager-flush) vs 796ms (suspend-flush)
Comme vous pouvez le voir, cela aide davantage avec les serveurs distants, car cela mettra moins de paquets sur le réseau.
Je ne peux pas utiliser les transactions car plus tard, les éléments à publier ne sont pas tous disponibles en même temps. Existe-t-il un moyen d'optimiser en gardant ces connaissances à l'esprit ?
Je pense qui est traité par ce qui précède - mais notez que récemment CreateBatch
a été ajouté aussi. Un lot fonctionne un peu comme une transaction - juste :sans la transaction. Encore une fois, c'est un autre mécanisme pour réduire la fragmentation des paquets. Dans votre cas particulier, je soupçonne que la suspension/reprise (sur flush) est votre meilleur pari.
Recommandez-vous d'avoir une RedisConnection générale et une RedisSubscriberConnection ou toute autre configuration pour que ce wrapper exécute les fonctions souhaitées ?
Tant que vous n'effectuez pas d'opérations bloquantes (blpop
, brpop
, brpoplpush
etc), ou en mettant des BLOB surdimensionnés sur le fil (retardant potentiellement d'autres opérations pendant qu'il s'efface), alors une seule connexion de chaque type fonctionne généralement assez bien. Mais YMMV en fonction de vos besoins d'utilisation exacts.