Implementação do servidor de mensagens de quarto que está usando um protocolo RPC bidirecional para implementar a comunicação semelhante a bate-papo. Projetado para lidar com problemas comuns de mensagens de rede pública, como entrega confiável, várias conexões de um único usuário, permissões e presença em tempo real. O processamento de solicitações de RPC e um formato de mensagens de sala são personalizáveis por meio de ganchos, permitindo implementar qualquer coisa de um servidor de salas de bate-papo a um aplicativo colaborativo com uma resolução complexa de conflitos. As mensagens da sala também podem ser usadas para criar APIs públicas ou para tunnel as comunicações M2M para dispositivos IoT.
Mensagens de sala confiáveis usando um armazenamento do histórico do servidor e uma API de sincronização.
Formato de mensagens arbitrárias através de apenas uma função de validação (gancho), permitindo formatos de mensagens personalizadas/heterogêneas (incluindo dados binários dentro de mensagens).
API de presença do usuário por sala com notificações.
Criação de salas em tempo real e APIs de gerenciamento de permissões por usuários por sala. Suporta modos de acesso baseados na lista negra ou na lista de permissões e um grupo de administradores opcionais.
Suporte contínuo de várias conexões dos usuários de vários dispositivos para qualquer instância de serviço.
Escrito como um microsserviço sem estado, usa o Redis (também suporta configurações de cluster) como uma loja estadual, pode ser escalada horizontalmente sob demanda.
Suporte de personalização extenso. A funcionalidade personalizada pode ser adicionada via ganchos antes/depois para qualquer processamento de solicitação do cliente. E os manipuladores de solicitações (comandos) podem ser chamados de servidor por meio de uma API.
Transporte de rede de plug -in. A comunicação do cliente-servidor é feita através de um protocolo RPC bidirecional. A implementação do transporte do Socket.io está incluída.
Loja de estado pluginable. As lojas de memória e Redis estão incluídas.
Suporta o usuário leve on -line para mensagens de usuário on -line.
Leia este artigo para obter mais informações básicas.
Este projeto é um módulo de nó disponível via NPM. Vá conferir se você não os tiver instalado localmente.
$ npm i chat-servicePrimeiro defina uma configuração do servidor. Em um servidor, defina um gancho de conexão de soquete, pois o serviço depende de uma implementação externa de autenticação. Um usuário só precisa passar por uma verificação de autenticação, não é necessário um usuário explícito de adição de etapa.
const ChatService = require ( 'chat-service' )
const port = 8000
function onConnect ( service , id ) {
// Assuming that auth data is passed in a query string.
let { query } = service . transport . getHandshakeData ( id )
let { userName } = query
// Actually check auth data.
// ...
// Return a promise that resolves with a login string.
return Promise . resolve ( userName )
} Criar um servidor é uma instanciação simples de objeto. Nota: O método close deve ser chamado para desligar corretamente uma instância de serviço (consulte a recuperação de falhas).
const chatService = new ChatService ( { port } , { onConnect } )
process . on ( 'SIGINT' , ( ) => chatService . close ( ) . finally ( ( ) => process . exit ( ) ) ) O servidor agora está em execução na porta 8000 , usando o estado memory . Por padrão, o espaço de nome '/chat-service' é usado. Adicione uma sala com o usuário admin como proprietário da sala. Todos os quartos devem ser criados explicitamente (a opção para permitir a criação de salas do lado do cliente também é fornecida).
// The room configuration and messages will persist if redis state is
// used. addRoom will reject a promise if the room is already created.
chatService . hasRoom ( 'default' ) . then ( hasRoom => {
if ( ! hasRoom ) {
return chatService . addRoom ( 'default' , { owner : 'admin' } )
}
} ) Em um cliente, é necessária apenas uma implementação de socket.io-client . Para enviar uma solicitação (comando) usar o método emit , o resultado (ou um erro) será retornado no retorno de chamada Socket.io ACK. Para ouvir as mensagens do servidor usam on método.
const io = require ( 'socket.io-client' )
// Use https or wss in production.
let url = 'ws://localhost:8000/chat-service'
let userName = 'user' // for example and debug
let token = 'token' // auth token
let query = `userName= ${ userName } &token= ${ token } `
let opts = { query }
// Connect to a server.
let socket = io . connect ( url , opts )
// Rooms messages handler (own messages are here too).
socket . on ( 'roomMessage' , ( room , msg ) => {
console . log ( ` ${ msg . author } : ${ msg . textMessage } ` )
} )
// Auth success handler.
socket . on ( 'loginConfirmed' , userName => {
// Join room named 'default'.
socket . emit ( 'roomJoin' , 'default' , ( error , data ) => {
// Check for a command error.
if ( error ) { return }
// Now we will receive 'default' room messages in 'roomMessage' handler.
// Now we can also send a message to 'default' room:
socket . emit ( 'roomMessage' , 'default' , { textMessage : 'Hello!' } )
} )
} )
// Auth error handler.
socket . on ( 'loginRejected' , error => {
console . error ( error )
} ) É um código executável, os arquivos estão no diretório example .
É possível usar outros transportes diferentes do soquete.io. Existe uma prova de transporte conceitual, que está usando uma conexão do WebSocket com algumas mensagens WS da camada de abstração API mínima e um simples corretor de pubsub-pubsub emissor como abstração de fanout de mensagens de back-end.
Aqui estão as principais coisas que um transporte deve permitir:
Envie mensagens de um servidor para grupos de clientes (com base em uma única string de critérios de correspondência completa, as mensagens da sala também conhecidas).
Implementar a comunicação repetida de um cliente para um servidor.
Implementar algum tipo de conexão persistente (ou semanticamente equivalente), é necessário para um rastreamento de presença.
O serviço de bate -papo está usando o Redis como uma loja compartilhada com persistência. Em uma aplicação real, algumas dessas informações podem ser necessárias para outros serviços, mas não é prático reimplementar completamente a loja do estado. Uma abordagem alternativa melhor é usar ganchos. Por exemplo, para salvar todas as mensagens da sala dentro de um outro banco de dados, apenas um gancho de roomMessageAfter pode ser usado. Também ServiceAPI pode ser exposto por meio de barramentos de mensagens de back -end a outros servidores internos.
Em circunstâncias normais, todos os erros retornados a um usuário do serviço (via Solicy Respondes, Mensagens loginConfirmed ou loginRejected ) são instâncias de ChatServiceError . Todos os outros erros indicam um bug do programa ou uma falha em uma infraestrutura de serviço. Para ativar o log de depuração de tais erros, use export NODE_DEBUG=ChatService . A biblioteca está usando o Bluebird ^3.0.0 promete a implementação; portanto, para ativar rastreios de pilha longa, use export BLUEBIRD_DEBUG=1 . É altamente recomendável usar versões de promessa de APIs para ganchos e subclasses de ChatServiceError para retornar erros personalizados de ganchos.
A API do lado do servidor e a documentação do RPC estão disponíveis online.
O serviço abstrava completamente um conceito de conexão de um conceito de usuário, para que um único usuário possa ter mais de uma conexão (incluindo conexões em diferentes nós). Para presença do usuário, o número de soquetes unidos deve ser maior que zero. Todas as APIs projetadas para funcionar no nível do usuário, lidando com várias conexões do usuário de usuário.
As conexões são completamente independentes, não é necessário suporte colateral adicional do cliente. Mas existem mensagens de informação e comandos que podem ser usados para obter informações sobre as conexões de outros usuários. É possível realizar padrões de sincronização do lado do cliente, como manter todas as conexões a serem unidas aos mesmos quartos.
Cada quarto possui um sistema de permissões. Há um único usuário do proprietário, que possui todos os privilégios de administrador e pode atribuir usuários ao grupo de administradores. Os administradores podem gerenciar as permissões de acesso de outros usuários. Dois modos são suportados: Lista Negra e Lista de Lista. Após as listas de acesso/modificações de modo, o serviço remove automaticamente os usuários que perderam uma permissão de acesso.
Se enableRoomsManagement Options estiver ativado, os usuários poderão criar salas via comando roomCreate . O criador de uma sala será o proprietário e também pode excluí -lo por comando roomDelete .
Antes que os ganchos possam ser usados para implementar sistemas de permissões adicionais.
Quando um usuário envia uma mensagem da sala, na RPC Responder, o id da mensagem é retornado. Isso significa que a mensagem foi salva em uma loja (em um buffer circular de anexo apenas como estrutura). Os IDs da mensagem da sala são uma sequência a partir de 1 , que aumenta em um para cada uma mensagem enviada com sucesso na sala. Um cliente sempre pode verificar o último ID da mensagem do quarto via Comando roomHistoryInfo e usar o comando roomHistoryGet para obter mensagens ausentes. Essa abordagem garante que uma mensagem possa ser recebida, a menos que seja excluída devido à rotação.
Por padrão, um cliente pode enviar mensagens limitadas a apenas um {textMessage: 'Some string'} . Para habilitar o formato de mensagens personalizadas, forneça ganchos directMessagesChecker ou ganchos de esceca roomMessagesChecker . Quando um gancho resolve, um formato de mensagem é aceito. As mensagens podem ser dados arbitrários com algumas restrições. O nível superior deve ser um Object , sem timestamp , author ou campos id (o serviço preencherá esses campos antes de enviar mensagens). Os níveis aninhados podem incluir tipos de dados arbitrários (mesmo binários), mas nenhum objetos aninhados com um type de campo definido como 'Buffer' (usado para manipulações de dados binários).
Cada comando do usuário suporta suporte antes e depois da adição de gancho e os ganchos de conexão/desconexão do cliente também são suportados. Comando e ganchos são executados sequencialmente: antes do gancho - comando - após o gancho (ele também será chamado por erros de comando). A terminação de sequência em antes dos ganchos é possível. Os clientes podem enviar argumentos de comando adicionais, ganchos podem lê -los e responder com argumentos adicionais.
Para executar um servidor de comando do usuário execUserCommand é fornecido. Também existem mais alguns métodos apenas do servidor fornecidos pelo ServiceAPI e TransportInterface . Procure alguns casos de personalização em exemplos de personalização.
O serviço mantém a presença do usuário e os dados de conexão em uma loja, que podem ser persistentes ou compartilhados. Portanto, se uma instância for desligada incorretamente (sem ligar ou esperar o método close para terminar) ou a conexão de rede completamente perdida com uma loja, os dados de presença ficarão incorretos. Para corrigir este caso, é fornecido o método instanceRecovery .
Também existem casos mais sutis sobre a consistência dos dados dependentes de conexão. Instâncias de comunicação de transporte e instâncias da loja podem experimentar vários tipos de falhas de rede, software ou hardware. Em alguns casos de borda (como operação em vários usuários), essas falhas podem causar inconsistências (na maioria das vezes, erros serão devolvidos aos emissores do comando). Esses eventos são relatados por meio de um emissor de instância (como o evento storeConsistencyFailure ), e os dados podem ser sincronizados por métodos RecoveryAPI .
Por padrão, todos os usuários têm um login exclusivo (nome de usuário). Em vez de gerenciar a geração de nomes, uma integração com um transporte separada pode ser usada (ou uma conexão multiplexada, por exemplo, um espaço para nome de outro soquete.io). As mensagens da sala podem ser encaminhadas da roomMessage após o gancho para um transporte, que é acessível sem um login. E vice -versa Alguns comandos de serviço podem ser executados por usuários anônimos via execUserCommand , com a opção de permissões de ignição ativada.
Uma roomMessage após o gancho também pode ser usada para encaminhar mensagens de uma sala para outra. Portanto, os quartos podem ser usados para agregação de mensagens de outras salas. Como os ganchos são apenas funções e têm um acesso total ao conteúdo das mensagens, ele permite implementar regras de encaminhamento arbitrárias baseadas em conteúdo. Incluindo sistemas de implementação com feeds específicos de usuário altamente personalizado (cliente).
Por padrão, não há como outros usuários conhecerem o número e os tipos de conexões de usuário unidas a uma sala. Essas informações podem ser passadas, por exemplo, em uma sequência de consultas e salvas através de um gancho de conexão. O anúncio pode ser feito nos ganchos onJoin e onLeave , usando o método de transporte direto sendToChannel . Além disso, informações adicionais sobre os tipos de dispositivos unidos devem ser enviados do roomGetAccessList após o gancho (quando o nome da lista é igual a 'userlist' ).
Não há operação de exclusão ou edição, pois eles farão inconsistências dentro da história da sala. Uma alternativa comum para excluir e editar é usar mensagens de sala com um significado especial que os clientes usarão para ocultar ou alterar as mensagens.
Se você encontrar um bug neste pacote, envie um relatório de bug aos problemas do GitHub Repo.
PRs também são aceitos.
Mit