Redis
 sql >> Base de Dados >  >> NoSQL >> Redis

Como o PubSub funciona no BookSleeve/Redis?


1:há apenas um canal no seu exemplo (Test ); um canal é apenas o nome usado para uma troca particular de pub/sub. No entanto, é necessário usar 2 conexões devido às especificidades de como a API redis funciona. Uma conexão que tenha qualquer assinaturas não podem fazer mais nada, exceto:
  • ouvir mensagens
  • gerenciar suas próprias assinaturas (subscribe , psubscribe , unsubscribe , punsubscribe )

No entanto, não entendo isso:
private static Dictionary<string, RedisSubscriberConnection>

Você não deve precisar de mais de uma conexão de assinante, a menos que esteja atendendo a algo específico para você. Uma única conexão de assinante pode lidar com um número arbitrário de assinaturas. Uma verificação rápida na client list em um dos meus servidores e tenho uma conexão com (no momento da redação) 23.002 assinaturas. O que provavelmente poderia ser reduzido, mas:funciona.

2:assinaturas padrão suportam curingas; então, em vez de assinar /topic/1 , /topic/2/ etc você pode se inscrever em /topic/* . O nome do real canal usado por publish é fornecido ao receptor como parte da assinatura de retorno de chamada.

Qualquer um pode funcionar. Deve-se notar que o desempenho de publish é afetado pelo número total de assinaturas únicas - mas, francamente, ainda é estupidamente rápido (como em:0ms), mesmo se você tiver dezenas de milhares de canais inscritos usando subscribe em vez de psubscribe .

Mas de publish

Complexidade de tempo:O(N+M) onde N é o número de clientes inscritos no canal de recebimento e M é o número total de padrões inscritos (por qualquer cliente).

Eu recomendo ler a documentação do redis do pub/sub.

Editar para seguir em perguntas:

a) Presumo que teria que "publicar" de forma síncrona (usando Result ou Wait()) se quiser garantir que a ordem de envio dos itens da mesma editora seja preservada ao receber os itens, correto?

isso não fará nenhuma diferença; desde que você mencionou Result / Wait() , suponho que você esteja falando de BookSleeve - nesse caso, o multiplexador já preserva a ordem de comando. O próprio Redis é de thread único e sempre processará comandos em uma única conexão em ordem. No entanto:os retornos de chamada no assinante podem ser executados de forma assíncrona e podem ser entregues (separadamente) a um thread de trabalho. No momento, estou investigando se posso forçar que isso esteja em ordem em RedisSubscriberConnection .

Atualização:de 1.3.22 em diante, você pode definir o CompletionMode para PreserveOrder - então todos os retornos de chamada serão concluídos sequencialmente em vez de simultaneamente.

b) depois de fazer os ajustes de acordo com suas sugestões obtenho um ótimo desempenho ao publicar poucos itens independente do tamanho do payload. No entanto, ao enviar 100.000 ou mais itens pelo mesmo editor, o desempenho cai rapidamente (até 7-8 segundos apenas para enviar da minha máquina).

Em primeiro lugar, esse tempo parece alto - testando localmente eu recebo (para 100.000 publicações, inclusive aguardando a resposta de todas elas) 1766ms (local) ou 1219ms (remoto) (isso pode parecer contra-intuitivo, mas meu "local" não é t executando a mesma versão do redis; meu "remoto" é 2.6.12 no Centos; meu "local" é 2.6.8-pre2 no Windows).

Não consigo tornar seu servidor real mais rápido ou acelerar a rede, mas:caso isso seja fragmentação de pacotes, adicionei (só para você) um SuspendFlush() / ResumeFlush() par. Isso desabilita a liberação antecipada (ou seja, quando a fila de envio está vazia; outros tipos de liberação ainda acontecem); você pode achar que isso ajuda:
conn.SuspendFlush();
try {
    // start lots of operations...
} finally {
    conn.ResumeFlush();
}

Observe que você não deve Wait até que você tenha retomado, porque até você chamar ResumeFlush() pode haver algumas operações ainda no buffer de envio. Com isso tudo pronto, recebo (para 100.000 operações):
local: 1766ms (eager-flush) vs 1554ms (suspend-flush)
remote: 1219ms (eager-flush) vs 796ms (suspend-flush)

Como você pode ver, isso ajuda mais com servidores remotos, pois colocará menos pacotes na rede.

Não posso usar transações porque mais tarde os itens a serem publicados não estão todos disponíveis de uma só vez. Existe uma maneira de otimizar com esse conhecimento em mente?

Eu acho que é abordado acima - mas observe que recentemente CreateBatch foi adicionado também. Um lote funciona muito como uma transação - apenas:sem a transação. Novamente, é outro mecanismo para reduzir a fragmentação de pacotes. No seu caso particular, suspeito que suspender/retomar (no flush) seja sua melhor aposta.

Você recomenda ter um RedisConnection geral e um RedisSubscriberConnection ou qualquer outra configuração para que esse wrapper execute as funções desejadas?

Contanto que você não esteja realizando operações de bloqueio (blpop , brpop , brpoplpush etc), ou colocando BLOBs superdimensionados no fio (potencialmente atrasando outras operações enquanto ele limpa), então uma única conexão de cada tipo geralmente funciona muito bem. Mas YMMV dependendo de seus requisitos de uso exatos.