16 pontos por GN⁺ 2024-10-24 | 1 comentários | Compartilhar no WhatsApp
  • O Lichess é uma plataforma de xadrez gratuita e de código aberto com milhões de jogadores no mundo todo
  • Usando a aba Network do Chrome DevTools para monitorar a comunicação entre cliente e servidor

Conexão WebSocket

  • O primeiro comportamento de rede digno de nota é uma conexão WebSocket para uma URL semelhante a esta:
wss://socket2.lichess.org/play/H5uHz0egyvIA/v6?sri=bt6QzcyOiZg5&v=0  
  • O protocolo wss indica uma conexão WebSocket criptografada usando TLS
  • WebSocket permite comunicação full-duplex, possibilitando atualizações em tempo real entre cliente e servidor sem requisições HTTP repetidas

Vez do jogador local

  • Quando uma ação é executada, pacotes de dados são trocados:
// Enviado às 22:51:35.280  
{  
  "t": "move",   
  "d": {  
    "u": "d2d4",  
    "l": 32,  
    "a": 1  
  }  
}  
  • Mensagem recebida do servidor:
// Recebido às 22:51:35.312  
{  
  "t": "ack",  
  "d": 1  
}  
  • Isso informa que o servidor recebeu nossa ação
// Recebido às 22:51:35.312  
{  
  "t": "move",  
  "v": 1,  
  "d": {  
    "uci": "d2d4",  
    "san": "d4",  
    "fen": "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR",  
    "ply": 1,  
    "clock": {  
      "white": 300,  
      "black": 300  
    }  
  }  
}  
  • Essa mensagem fornece informações detalhadas sobre a ação que realizamos e o estado atualizado da partida

Vez do oponente

  • Quando o oponente joga, um pacote semelhante é recebido do servidor:
// Recebido às 22:51:43.489  
{   
  "t": "move",  
  "v": 2,  
  "d": {  
    "uci": "d7d5",  
    "san": "d5",  
    "fen": "rnbqkbnr/ppp1pppp/8/3p4/3P4/8/PPP1PPPP/RNBQKBNR",  
    "ply": 2,  
    "dests": {  
      "c2": "c3c4",  
      "g2": "g3g4"  
      // Movimentos adicionais possíveis  
    },  
    "clock": {   
      "white": 300,  
      "black": 300  
    }  
  }  
}   
  • O parâmetro dests lista todos os movimentos disponíveis a partir da posição atual

Arquitetura do Lichess

  • O sistema de jogo em tempo real do Lichess é composto principalmente por dois serviços centrais (ambos escritos em Scala):
    1. lila: serviço principal que gerencia lógica do jogo, estado, interações do usuário e outras funções centrais
    2. lila-ws: serviço especializado no tratamento de WebSocket, atuando como ponte entre o cliente e o lila

Visão geral da arquitetura

lila <-> redis <-> lila-ws <-> websocket <-> client  
  • O lila se comunica com o lila-ws via Redis, e este gerencia as conexões WebSocket com os clientes

Comunicação com Redis Pub/Sub

  • Eventos de jogada são publicados em canais Redis Pub/Sub, aos quais o lila se inscreve para processar os movimentos
  • Redis Pub/Sub oferece entrega at-most-once. Perda de mensagens é possível, mas o uso de memória é reduzido

Persistência final dos dados com MongoDB

  • O lila salva o estado da partida no MongoDB, mas não persiste cada jogada individual imediatamente
  • Em vez disso, ele faz buffer das jogadas e salva periodicamente para reduzir a carga no banco de dados
  • Quando ocorre um evento importante, o estado da partida é descarregado

Entrando em uma partida em andamento

  • Quando um jogador se conecta, ele fornece o parâmetro v para informar ao sistema a versão mais recente da partida que conhece
  • O lila-ws usa ConcurrentHashMap para rastrear e gerenciar todos os eventos das partidas em andamento

Encerramento

O processo de uma jogada no Lichess pode ser resumido assim:

  1. O cliente estabelece uma conexão WebSocket com o lila-ws
  2. Quando o jogador faz uma jogada, o cliente envia um evento de movimento para o lila-ws
  3. O lila-ws responde com um ack confirmando o recebimento do movimento
  4. O evento da jogada é publicado em um canal Redis Pub/Sub e processado pelo lila
  5. O lila recebe a jogada, atualiza o estado da partida e finalmente a salva no MongoDB. O estado atualizado da partida é então enviado de volta ao cliente por meio do lila-ws
  6. O cliente recebe o estado atualizado da partida, refletindo a nova jogada e as mudanças no estado do jogo

Opinião do GN⁺

  • Este post analisa em detalhes a arquitetura de backend e o processo que tornam possível o gameplay em tempo real no lichess.org, uma popular plataforma de xadrez de código aberto
  • Ele apresenta os principais elementos técnicos a considerar ao construir aplicações web em tempo real, como comunicação em tempo real com WebSocket, entrega escalável de mensagens via Redis Pub/Sub e armazenamento final de dados com MongoDB
  • A arquitetura do Lichess é muito adequada para jogos multiplayer em tempo real, mas padrões e tecnologias semelhantes também podem ser aplicados a outros tipos de apps web em tempo real, como chats, ferramentas colaborativas e feeds de redes sociais
  • Recursos em tempo real podem melhorar a experiência e a interação do usuário, mas também trazem desafios técnicos próprios, como escalabilidade, confiabilidade e consistência de dados. Este post oferece estratégias para lidar com esses desafios
  • Projetos open source com stack semelhante incluem Socket.IO (framework para aplicações em tempo real baseado em Node.js) e RethinkDB (banco de dados NoSQL otimizado para apps web em tempo real)
  • Esta análise do post não se baseia em uma revisão direta do código-fonte do Lichess, então a implementação real pode ser diferente. Ainda assim, os conceitos básicos e os padrões de arquitetura descritos continuam válidos
  • Ao projetar sistemas em tempo real, é preciso considerar cuidadosamente se at-most-once (possibilidade de perda de mensagens) ou at-least-once (possibilidade de duplicação de mensagens) é mais adequado. Isso depende dos requisitos e trade-offs da aplicação

1 comentários

 
GN⁺ 2024-10-24
Comentários do Hacker News
  • Há reclamações sobre a estrutura de tempo do Chess.com. Parece que o servidor rastreia o tempo e ignora o tempo de transmissão e a latência. Isso é especialmente incômodo ao jogar partidas com limite de tempo no cliente móvel

    • Pode ser um problema do código de rede, e erros acontecem com frequência nos puzzles
    • A tecnologia do Chess.com parece meio tosca
  • O Lichess escolheu a abordagem do StackOverflow e usa servidores robustos

    • O estado do jogo é salvo periodicamente, mas não está claro onde ele é armazenado
    • O custo por jogo é muito baixo: $0.00027, 1 dólar a cada 3.671 partidas
    • Já houve uma interrupção de 10 horas por causa da dependência de um único datacenter
  • Calcular os movimentos no lado do servidor garante consistência e otimiza o desempenho para clientes com capacidade de processamento ou energia limitada

    • Isso pode servir para reduzir a barreira de implementação de clientes de software open source em novas plataformas
    • Implementar as regras do xadrez pode ser trabalhoso, e o Lichess também já teve erros de lógica
  • Falta explicação sobre como a perda de mensagens é tratada nos canais pub/sub do Redis

  • O parâmetro "l" pode indicar a latência observada pelo servidor

  • Surpreende que o servidor enumere e envie todos os próximos movimentos legais

    • Isso pode ser vantajoso para clientes limitados, mas fica a dúvida se é mais barato do que calcular no lado do cliente
  • Há perguntas sobre como proteger o servidor de WebSocket

    • Usar o plano gratuito da Cloudflare introduz latência
    • Existe curiosidade sobre soluções gratuitas
  • Há dúvida sobre por que o protocolo precisa de ack

    • WebSockets encapsulados em TLS podem garantir a integridade das mensagens
  • FEN codifica apenas o estado do tabuleiro, e não o estado da partida

    • O projeto scalachess, escrito em Scala, continua sendo mantido com sucesso