Iniciando o design arquitetural de um "chat online"

Keep your plane two mistakes high.
Autor Desconhecido
Há diversas soluções de chat online disponíveis no mercado, com funcionalidades para todos os interesses e preços para todos os bolsos. Mas, se você for “desafiado” a criar uma solução autoral, é bom estar preparado.
0
Concorda?x

Nesse capítulo, vamos utilizar alguns dos conceitos discutidos nesse livro para “pensar a arquitetura” de um sistema de chat online. Nosso objetivo não vai ser fazer uma proposta completa, tampouco definitiva, mas “aquecer os motores”. Obviamente, iremos trabalhar um um cenário fictício (afinal, precisamos de algumas delimitações para nossa solução).

O que é um “chat online

Todo mundo gosta de um bom bate-papo, não é mesmo? Chat online trata exatamente disso – conversa, utilizando alguma ferramenta específica para esse propósito.

Há quem converse por texto, trocar fotos, mensagens de áudio ou vídeo. Há também quem goste de enviar e receber documentos. As conversas online podem ser entre duas pessoas (assumindo que uma parte leia a e responda) ou entre um grupo de pessoas.

Em quase todas as ferramentas para trocas de mensagens você poderá manter uma rede de contatos (amigos) e saber quem está online ou offline.

O ICQ foi uma das ofertas pioneiras desse tipo de aplicação para chat online. Hoje em dia, alguns serviços populares são Whatsapp e Facebook Messenger.

Muitas empresas oferecem interfaces de atendimento através de serviços de mensagens instantâneas.

Um instrumento para a transformação digital

Um dos pilares da transformação digital é estreitar as relações das organizações com seus clientes, fornecedores e parceiros. Nesse contexto, aplicações de chat estão se tornado cada vez mais populares.

Explicando e justificando a transformação digital e ágil

Transformação Digital e Ágil

Em dúvidas do que é, de fato, transformação digital e ágil? Nesse capítulo, apresento alguns conceitos fundamentais para te ajudar no entendimento desse importante movimento.

Ler capítulo

Coletando informações para o Haiku

As funcionalidades em serviços de chat online variam bastante. Todos os dias, funcionalidades são incorporadas nas soluções líderes de mercado visando tornar a comunicação mais fluída – conversas em grupo, reações, respostas específicas, etc. Em nosso cenário fictício, o objetivo é fornecer um serviço leve de comunicação 1-para-1 (não haverá suporte para grupos, por enquanto), apenas em texto.

Queremos desenvolver uma solução autoral, ou seja, sem depender de componentes prontos.

Usuários poderão utilizar o serviço tanto com clientes web ou em aplicações mobile. São esperados 2 milhões DAU (daily active users). O tamanho máximo de uma mensagem é de 100 KB, mas, estima-se que elas tenham um tamanho médio de 100 caracteres. Todas as mensagens devem ser armazenadas, para sempre, em servidores da organização.

Cada usuário poderá possuir uma lista de “amigos”. O serviço deverá indicar quais “amigos” estão online em um determinado momento. Caso uma mensagem seja enviada para um “amigo” offline, ela deverá ser “guardada” até que ele possa receber.

Um mesmo usuário poderá estar conectado em mais do que um device simultaneamente.

Não há demanda por criptografia (que perigo!).

Espera-se que o serviço seja escalável e disponível.

O time de “negócios” deseja que seja desenvolvido um padrão proprietário para implementação que será incorporado como ativo da organização.

Conhecendo arquiteturas semelhantes

Antes de qualquer tentativa de produzir algum design arquitetural é importante buscar conhecer implementações anteriores para problemas semelhantes.

Sabe-se, por exemplo, que o Whatsapp adota uma variação, não documentada, de XMPP – um protocolo extensível de presença e troca de mensagens – em sua solução.

Facebook Messenger já adotou XMPP, mas, atualmente, utiliza solução proprietária, baseada em MQTT (um protocolo leve para trocas de mensagens, bastante popular em IoT).

O Telegram utiliza um protocolo também proprietário, porém documentado, chamado MTPro.

Conhecer arquiteturas de sistemas semelhantes ao que está projetando pode, além de inspirar e antecipar dificuldades, dar argumentos para discussões com o negócio.

O que aprendemos observando esses sistemas? Troca de mensagens (chat) e definição de presença são os componentes chaves nesse tipo de solução.

Design de alto nível para a troca de mensagens

Para sistemas mais complexos, produza visões de modelo, de alto nível, para facilitar discussões com o time.

A visão do modelo abaixo indica que aplicações “cliente” não se comunicam diretamente. Em lugar disso, conectam-se a um “servidor” que realiza a mediação das trocas de mensagens.

As principais atribuições desse serviço são:

  1. Aceitar mensagens enviadas por um usuário;
  2. Armazenar mensagens enviadas em um “registro de conversa” entre dois usuários;
  3. Garantir que mensagens enviadas sejam “entregues” imediatamente para “destinatários” que estejam conectados no serviço;
  4. Fornecer um “histórico recente” de conversas (mensagens trocadas).

O fluxo básico de operações pode ser melhor entendido a partir da representação abaixo.

Tudo começa com uma mensagem sendo enviada a partir de uma aplicação cliente; “enriquecida” com o um id no lado servidor; encaminhada para uma fila para processamento; armazenada em um banco de dados; caso o destinatário estiver online, enviar para o usuário destinatário; caso o destinatário estiver offline.

O “servidor” garante a “memória” de todas as mensagens. Além disso, possui duas interfaces primárias de comunicação para as “aplicações” cliente. Toda vez que um usuário, usando um “cliente”, deseja enviar ou receber mensagens, conecta-se ao “servidor” através de suas interfaces.

Ao produzir uma visão de modelo, certifique-se de ponderar sobre modularização – distribuição de responsabilidades – e a relação de componentes & conectores.

Memória de mensagens no Whatsapp

O Whatsapp é, provavelmente, o sistema de chat online mais popular nos dias de hoje. Ele implementa uma versão consideravelmente modificada do XMPP – um padrão aberto para troca de mensagens e presença, baseado em XML.

Pelo seu design o Whatsapp não precisa manter um registro centralizado de todas as mensagens deixando o armazenamento do histórico das conversas em bases de dados locais (SQLite), usando servidores em nuvem, eventualmente, como backup.

Design de alto nível para o controle de presença

O serviço de presença, em nosso chat online é responsável por determinar se um usuário está online e preparado para se comunicar com outros usuários.

Ele disponibiliza três interfaces principais:

  1. atualização externa de status, que deverá ser consumida primariamente pelo serviço de autenticação, mudando status para online quando acontecer loginoffline quando acontecer um logoff;
  2. monitoramento de pulsação de aplicativos clientes, visando detectar problemas de conexão;
  3. fornecimento de listas de status de usuários
Projetar as interfaces (e seus respectivos contratos) de um serviço/aplicação consiste uma excelente

Cada uma dessas interfaces apresenta desafios particulares.

A atualização externa pode ser modelada para evocação direta, acionamento via webhooks ou subscrição de eventos. A ideia inicial é habilitar interação entre o serviço de login (autenticação) e o serviço de presença.

O monitoramento de pulsação pode conectar diretamente com aplicações clientes ou através de “mensagens especiais” para o serviço de chat.

A atualização de status de amigos pode acontecer fornecendo listas completas atualizadas ou com atualizações parciais (fanout)

Considerações para outros serviços

Presença e chat são os dois serviços cruciais e específicos para a construção de um chat online. Mas, além deles, há alguns outros.

Para começar, é necessário ter um serviço para gerenciar login/logoff e assinatura de novos usuários. Também é necessário manter um serviço para registro de dados dos usuários (como fotos de perfil e dados de apresentação).

Tais serviços operam no padrão request/response e podem ser “feitos em casa” ou adquiridos no mercado.

O serviço para push notifications é um commodity necessário. Trata-se da maneira de informar usuários offline de que há novas mensagens recebidas, mesmo quando a aplicação, em um celular, não estiver em execução.

Organizando informações para gerar as primeiras ADRs

A partir das informações obtidas para formulação do Haiku e do “design de alto nível”, já é possível indicar algumas ADRs com status de “aberta para discussão”. Essas ADRs indicariam a “inclinação arquitetural” para diversos aspectos importantes, possibilitando debates entre especialistas.

Descartando XMPP

XMPP é um padrão constituído para troca de mensagens e presença. É a base tecnológica do Whatsapp, já foi o padrão adotado pelo Google Hangouts e o Facebook Messenger. Entretanto, não possui implementações ativas nas tecnologias familiares para os times, em nosso cenário fictício e, essa é uma das razões para estar sendo sendo deixado de lado. A outra é o fato do “negócio” desejar uma solução proprietária.

Nunca ignore a “qualificação técnica” do time para adoção de soluções tecnológicas. Eventualmente, a curva de aprendizagem pode inviabilizar a sustentação.

Em projetos reais, não seria prudente descartar um protocolo maduro e respeitado como o XMPP. Há implementações opensource para o protocolo que deveriam, pelo menos, serem submetidas a provas de conceito.
0
Concorda?x
Realize provas de conceito para conhecer e validar alternativas arquiteturais.

Facebook Messenger e MQTT

Modernamente, o Facebook Messenger adota MQTT como base para comunicação.

Do blog da Meta:

“The method we were using to send was reliable but slow, and there were limitations on how much we could improve it. With just a few weeks until launch, we ended up building a new mechanism that maintains a persistent connection to our servers. To do this without killing battery life, we used a protocol called MQTT that we had experimented with in Beluga. MQTT is specifically designed for applications like sending telemetry data to and from space probes, so it is designed to use bandwidth and batteries sparingly. By maintaining an MQTT connection and routing messages through our chat pipeline, we were able to often achieve phone-to-phone delivery in the hundreds of milliseconds, rather than multiple seconds.”

Adquira o hábito de acompanhar os blogs dos times de engenharia das big techs. Há sempre excelentes insights sobre motivações e critérios de decisão.

A “insuficiência” do HTTP para o chat service

O design arquitetural de uma aplicação contempla seus componentes, as responsabilidades de cada componente e a forma como eles se relacionam. Escolhas infelizes de protocolos podem representar sérias dificuldades para a construção de aplicações com bom evolvability.

Na maior parte dos sistemas com arquitetura cliente/servidor, a comunicação geralmente é iniciada pelo lado “cliente”. Isso também é válido para o envio de mensagens em um chat online. Isso  torna HTTP uma opção viável de protocolo para sending messages interface, principalmente considerando a alternativa de usar a opção keep-alive  para reduzir a quantidade de TCP handshakes.

Para a relaying messages interface, entretanto, HTTP não parece ser uma opção muito boa, já que, nessa interface seria mais natural se a comunicação começasse pelo lado “servidor”.

Polling e Long Polling

Ao longo dos anos, algumas técnicas foram desenvolvidas para “disfarçar” as limitações de HTTP para comunicações iniciadas no “servidor” para os “clientes”.

Uma delas, polling, consiste no “cliente” questionando “o que há de novo?” para o “servidor”, repetidamente, em intervalos regulares (por exemplo, a cada 5 segundos). Dessa forma, sempre que houver uma novidade no servidor, ela é comunicada . Obviamente, tal abordagem pode ser custosa, consumindo recursos do servidor (escassos) para, na maior parte das vezes, informar que não há nada a comunicar.

Outra técnica, variante da primeira, é o long pollingSua diferença é que o”cliente” e “servidor” mantém a conexão aberta até houver algo a comunicar ou aconteça um timeout (default 100s).

Websockets como alternativa viável

As limitações de HTTP na comunicação do “servidor” para o “cliente”, necessária para a relaying messages interface, são superadas rapidamente com a adoção de websockets. 

Websockets

WebSocket é uma tecnologia – padronizada pelo W3C e pelo IETF – que permite a comunicação bidirecional por canais full-duplex sobre um único soquete TCP.  

Uma conexão websocket é iniciada no lado “cliente” com uma requisição HTTP normal, atualizada emum handshake. Ela funciona nas portas 80 e 443.

Embora HTTP seja “good enough” para sending message interface, não há razões para não adotar websockets ali também.

A manutenção de conexões ativas persistentes entre clientes e o serviço de chat classifica esse serviço como stateful.

Suportando a escala (em servidores stateful)

A decisão de usar Websockets com conexões ativas persistentes implica na impossibilidade de adotar estratégias de balanceamento simplificadas entre clientes e servidores.

Fazendo cálculos no “papel de pão”, assumindo 2 milhões de usuários ativos simultaneamente (respeitando o caso extremo apontado pelo “negócio”), ponderando-se 10KB para cada “conexão ativa” sendo mantida pela aplicação, temos uma alocação total de 20GB. Ou seja, um volume suportável para um único servidor.

Obviamente, a distribuição na horizontal é uma demanda importante para garantir a disponibilidade, que é fundamental para a escalabilidade.

Uma primeira alternativa para gerenciar os handlers de conexões por cliente é utilizar alguma estratégia de service discovery como a oferecida pelo Apache Zookeeper – Ele registra todos os servidores ativos e direciona conexões conforme critérios pré-especificados.

Outra alternativa, mais radical, é adotar o modelo de programação com atores – a base de tecnologias como ERLANG, Akka e Orleans.

Actors Model

O modelo de atores é um modelo de programação para computação concorrente. Nele, um “ator” é a principal primitiva. Atores interagem trocando mensagens.

Em resposta a uma mensagem recebida, um ator pode: 1) atuar localmente; 2) criar novos atores; 3) enviar novas mensagens ou 4) responder a mensagem recebida.

Um ator tem poder apenas para modificar seu estado local, mas somente pode afetar o estado global indiretamente através do envio de mensagens.

Frameworks que implementam o modelo de atores destacam-se por sua capacidade para escalar e distribuir processamento, com alta tolerância a falhas e baixa latência.

Por agora, nossa opção de design é utilizando abordagens padrões de service discovery.

Suportando a escala (em servidores stateless)

Escalar serviços de autenticação, service discovery e perfil pode adotar abordagens tradicionais de replicação. Ou seja, replicação horizontal com carga distribuída através de um load balancer.

Armazenamento das mensagens

O armazenamento das mensagens trocadas pelo serviço é relativamente simples, contendo poucos campos.

O campo id precisa suportar ordenação temporal inteligente e, por isso, sugere-se a adoção de um servidor de tickets ou uma estratégia como o Twitter Snowflake.

Twitter Snowflake

Twitter Snowflake é uma estratégia para geração de IDs únicos, com 64 bits, de maneira distribuída, que possibilita ordenação temporal.

Na proposta da estratégia:

  • O primeiro bit é reservado e sempre “zero”
  • 41 bits são destinados ao armazenamento de um timestamp regular (suficiente para até 69 anos).
  • 5 bits são utilizados para identificar o datacenter, determinado durante o setup, onde se encontra a máquina onde o id está sendo gerado (até 32 datacenters)
  • 5 bits são utilizados para identificar a máquina do datacenter, determinado durante o setup, onde o ID está sendo gerado (até 32 máquinas por datacenter)
  • 12 bits são um sequencial reinicializado a cada milissegundo.

É fundamental que o clock das diversas máquinas esteja sincronizado.

Importante lembrar que o volume de mensagens pode ser realmente gigante. Conforme o negócio, entretanto, nada comparado com os volumes de aplicações suportados por empresas como  Whatsapp e Facebook Messenger.

Mensagens geralmente são acessadas, no servidor, conforme “idade”. Mensagens recentes são acessadas todo o tempo. Outro ponto importante, o volume de “leituras” e “gravações” se aproxima da proporção 1:1 (uma leitura para cada escrita).

Bancos de dados chave-valor parecem ser apropriados. Por referência, Facebook Messenger utiliza HBase e Discord utiliza Cassandra.

Armazenamento de “dados genéricos”

Dados genéricos, como dados de perfil, configurações e lista de amigos são fáceis de modelar e suportar com bases de dados relacionais tradicionais.

Eventualmente, para suportar escala, pode-se adotar técnicas como replicação e sharding.

Consolidação básica de componentes (servidores)

Há, dentro da solução de chat online até aqui, as seguintes naturezas de servidores:

  1. load balancers
  2. mecanismos de fila
  3. serviços stateless
    1. autenticação
    2. dados básicos de perfil
    3. serviço de descoberta
  4. serviços statefull
    1. chat
    2. presença
  5. bancos de dados
    1. NoSQL chave-valor
  6. servidores de push notifications

Fluxo básico para conexão de um cliente ao serviço

A conexão de um cliente a plataforma segue um processo genérico de apenas 3 etapas.

  1. Uma vez que um usuário possua acesso ao website ou ao aplicativo da plataforma, poderá iniciar conexão, mediante serviço de autenticação.
  2. Com cliente devidamente autorizado, conexão com serviço de chat deve ser iniciada com orquestração do serviço de descoberta. Uma vez, determinado o servidor, conexão websocket deve ser estabelecida.
  3. Aplicação cliente, então, deve solicitar lista de usuários presentes e atualização de mensagens.
Registre em ADRs fluxos elementares, em alto nível, para os sistemas que estiver modelando

Pontos que ainda necessitam reflexão

O processo de elaboração da arquitetura deve ser incremental. Tão importante quanto relacionar decisões realizadas é indicar claramente o que ainda precisa ser ponderado.
Com relação ao design de um chat online é importante, ainda, considerar:

  • Rate Limiting, visando mitigar o risco potencial de usuários mal-intencionados enviarem um número esmagadoramente grande de mensagens.
  • A opção por não utilizar um protocolo consolidado, como XMPP, reduz possibilidades de interoperabilidade.

Takeaways

A elaboração desse “início de trabalho arquitetural” para um “chat online” permite algumas lições importantes:

  1. Problemas comuns tem soluções bem estudadas que não devem ser ignoradas;
  2. A produção de modelos visuais ajuda a tangibilizar a arquitetura
  3. Mesmo padrões consolidados tem problemas para “suportar” o peso da escala
  4. Mesmo em escalas que podem ser atendidas por instâncias únicas, sempre considere replicas para garantir disponibilidade.

Dúvidas ou sugestões? Deixe suas contribuições nos comentários.

Compartilhe este capítulo:

Compartilhe:

Comentários

Participe da construção deste capítulo deixando seu comentário:

Inscrever-se
Notify of
guest
0 Comentários
Feedbacks interativos
Ver todos os comentários

Fundador e CEO da EximiaCo, atua como tech trusted advisor ajudando diversas empresas a gerar mais resultados através da tecnologia.

Mentoria

para arquitetos de software

Imersão, em grupo, supervisionada por Elemar Júnior, onde serão discutidos tópicos avançados de arquitetura de software, extraídos de cenários reais, com ênfase em systems design.

Consultoria e Assessoria em

Arquitetura de Software

EximiaCo oferece a alocação de um Arquiteto de Software em sua empresa para orientar seu time no uso das melhores práticas de arquitetura para projetar a evolução consistente de suas aplicações.

ElemarJúnior

Fundador e CEO da EximiaCo, atua como tech trusted advisor ajudando diversas empresas a gerar mais resultados através da tecnologia.

+55 51 99942-0609 |  contato@eximia.co

+55 51 99942-0609  contato@eximia.co

0
Quero saber a sua opinião, deixe seu comentáriox
()
x