Skip to content

Latest commit

 

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Relatório Técnico – Trabalho 4: Comunicação Indireta

PixelHub – Quadro Colaborativo em Tempo Real


1. Introdução

O PixelHub é um quadro colaborativo distribuído em que múltiplos usuários podem pintar pixels em um canvas compartilhado de 1000×1000 células. Toda alteração realizada por qualquer participante é propagada em tempo real para todos os demais conectados, garantindo uma visão consistente e sincronizada do estado do quadro.

A arquitetura do sistema é composta por um servidor Spring Boot (back-end Java) e clientes conectados via WebSocket. A evolução da arquitetura neste trabalho consistiu na adoção da Opção A – Comunicação em Grupo (Multicast), substituindo qualquer comunicação ponto a ponto direta entre clientes por um modelo de disseminação intermediado pelo servidor.


2. Justificativa da Escolha: Comunicação em Grupo (Multicast)

A natureza do PixelHub exige que uma única ação de um usuário (pintar um pixel ou usar o balde de tinta) seja imediatamente visível para todos os demais participantes do quadro. Esse requisito mapeia diretamente para o modelo de Comunicação em Grupo: o remetente (o cliente que realizou a pintura) não precisa conhecer a identidade, o endereço IP ou a porta de nenhum outro cliente — ele simplesmente envia seu evento ao servidor, que age como coordenador do grupo e propaga a mensagem a todos os membros registrados.

As demais opções foram descartadas pelos seguintes motivos:

  • Pub-Sub (Opção B): Adequada para cenários com múltiplos tópicos distintos e assinantes seletivos. No PixelHub, há um único "canal" de eventos de desenho, e todos os usuários são assinantes universais, tornando a sobrecarga de um broker de tópicos desnecessária.
  • Filas de Mensagens (Opção C): O modelo de fila é orientado ao consumo individual de mensagens (cada mensagem é retirada da fila por um consumidor). No PixelHub, cada evento de pixel deve ser entregue a todos os clientes simultaneamente, o que contradiz a semântica de fila.
  • Espaço de Tuplas (Opção D): Útil para coordenação assíncrona baseada em padrões de dados. O PixelHub requer propagação de eventos em tempo real com baixa latência, não consultas associativas a um repositório compartilhado.

A Comunicação em Grupo via WebSocket é, portanto, a abordagem que melhor se encaixa no problema: oferece disseminação eficiente a múltiplos destinatários com desacoplamento espacial completo entre os pares de clientes.


3. Descrição da Implementação

3.1 Componentes Principais

A arquitetura é organizada em quatro serviços e um controlador WebSocket:

Componente Responsabilidade
ClientService Mantém o conjunto de sessões WebSocket ativas (membros do grupo)
WebSocketService Implementa as primitivas de unicast e broadcast (multicast)
BoardService Gerencia o estado global do quadro (1000×1000 pixels)
UserService Controla login/logout e notifica todos sobre mudanças na lista de usuários
SignalingHandler Ponto de entrada WebSocket; roteia eventos de desenho e balde

3.2 Gestão de Membros do Grupo

O ClientService utiliza um ConcurrentHashMap.newKeySet() para armazenar as sessões WebSocket ativas. Essa estrutura é thread-safe e garante que adições e remoções de membros (conexão e desconexão de clientes) ocorram sem condições de corrida:

// ClientService.java
private final Set<WebSocketSession> clients = ConcurrentHashMap.newKeySet();

public void addClient(WebSocketSession session) { clients.add(session); }
public void removeClient(WebSocketSession session) { clients.remove(session); }

Quando um novo cliente conecta (afterConnectionEstablished), ele é adicionado ao grupo e recebe imediatamente o estado atual completo do quadro via unicast, garantindo consistência na entrada:

// SignalingHandler.java
@Override
public void afterConnectionEstablished(WebSocketSession session) {
    clientService.addClient(session);
    webSocketService.unicast(boardService.getBoard().raw(), session);
}

3.3 Primitiva de Multicast (Broadcast)

O método broadcast do WebSocketService implementa a disseminação de grupo. Ele itera sobre todos os membros registrados no ClientService, excluindo o próprio remetente (quando aplicável), e entrega a mensagem serializada em JSON:

// WebSocketService.java
public void broadcast(Object data, WebSocketSession sender) {
    String json = objectMapper.writeValueAsString(data);
    TextMessage message = new TextMessage(json);
    for (WebSocketSession client : clientService.getClients()) {
        if (client.equals(sender)) continue;   // não reflete ao remetente
        if (!client.isOpen()) continue;         // ignora sessões encerradas
        synchronized (client) {
            client.sendMessage(message);
        }
    }
}

O uso de synchronized (client) garante que mensagens concorrentes enviadas para a mesma sessão não se entrelacem, preservando a integridade das mensagens WebSocket.

3.4 Propagação de Eventos de Desenho

O fluxo de um evento de pintura segue as etapas abaixo:

  1. O cliente envia um frame WebSocket com payload {"type": "draw", "x": ..., "y": ..., "color": ...}.
  2. O SignalingHandler deserializa o payload e aplica a mudança ao Board compartilhado via boardService.getBoard().applyDraw(draw).
  3. O mesmo payload é disseminado a todos os outros clientes via webSocketService.broadcast(data, session).

Para o evento de balde (bucket), o comportamento é ligeiramente diferente: após aplicar o flood-fill no estado do quadro, o servidor realiza um broadcast do estado bruto completo (board.raw()) com sender = null, garantindo que todos os clientes (incluindo o originador) recebam a nova representação consistente do quadro.

3.5 Desacoplamento Espacial

O desacoplamento espacial é comprovado pela ausência de qualquer referência entre clientes. O cliente A que pinta um pixel não conhece o endereço IP, porta ou identificador de nenhum outro cliente B, C ou D. Toda a comunicação ocorre exclusivamente entre o cliente e o servidor:

Cliente A  ──────► Servidor (Grupo) ──────► Cliente B
                        │               └──► Cliente C
                        │               └──► Cliente D
                        └── [Board State atualizado]

O servidor age como o coordenador do grupo, e cada cliente interage apenas com ele.


4. Análise: Overhead e Estratégias de Mitigação

4.1 Overhead Introduzido pelo Intermediário

A introdução do servidor como intermediário de grupo traz as seguintes sobrecargas em relação a uma hipotética comunicação direta ponto a ponto:

  • Latência adicional: Cada mensagem percorre o caminho Cliente → Servidor → Clientes. Em comunicação direta entre dois clientes na mesma rede local, esse salto extra pelo servidor acrescenta latência de rede (tipicamente de 1 a 10 ms em LANs), que é perceptível em cenários de alta frequência de pintura.
  • Gargalo no servidor: O broadcast é implementado com um laço sequencial sobre todas as sessões abertas. Com N clientes conectados e alta taxa de eventos, o servidor precisa serializar e enviar a mesma mensagem N-1 vezes. Para N grande (centenas de usuários), isso pode saturar a thread de I/O.
  • Uso de memória: O estado completo do quadro (1000×1000 pixels com cor em string hexadecimal) é mantido em memória no Board. O broadcast do raw() no evento de balde pode transmitir um payload significativamente maior do que um evento de pixel individual.
  • Complexidade de gerenciamento: O servidor precisa detectar e remover sessões encerradas (client.isOpen()), além de sincronizar o acesso concorrente às sessões com synchronized.

4.2 Estratégias de Mitigação

As seguintes estratégias foram adotadas ou podem ser adotadas para mitigar o overhead:

Adotadas no projeto atual:

  • Serialização única da mensagem: O WebSocketService serializa o objeto para JSON apenas uma vez por broadcast (String json = objectMapper.writeValueAsString(data)), reutilizando o mesmo TextMessage para todos os destinatários. Isso evita N serializações independentes.
  • Estrutura thread-safe sem locks globais: O ConcurrentHashMap para o conjunto de clientes e o CopyOnWriteArrayList para usuários permitem leituras concorrentes sem bloqueio, reduzindo a contenção.
  • Delta de eventos (draw vs. raw): Eventos de pintura pixel a pixel transmitem apenas o delta ({type, x, y, color}), evitando o envio do estado completo a cada pincelada. O estado completo só é enviado em eventos de balde ou ao entrar no quadro.

Possíveis melhorias futuras:

  • Throttling e batching: Agrupar múltiplos eventos de pixels em um único frame WebSocket por intervalo de tempo (ex: 50ms) reduziria o número de mensagens enviadas em sessões de pintura intensa.
  • Compressão WebSocket (permessage-deflate): Habilitar a extensão de compressão do protocolo WebSocket reduziria o volume de dados transmitidos, especialmente para o payload raw() do balde.
  • Persistência do estado do Board: Atualmente o estado reside exclusivamente em memória. Uma persistência periódica (ex: Redis ou banco de dados) permitiria recuperação após reinício do servidor sem perda do quadro colaborativo.
  • Horizontal scaling com broker de mensagens: Para suportar múltiplas instâncias do servidor (escalabilidade horizontal), seria necessário introduzir um broker externo (ex: Redis Pub/Sub, RabbitMQ) para sincronizar o broadcast entre instâncias — neste caso, a Opção B (Pub-Sub) complementaria a Opção A em uma arquitetura híbrida.

5. Comportamento Diante de Falhas

O sistema apresenta as seguintes características de resiliência:

  • Desconexão de cliente: Quando uma sessão encerra (afterConnectionClosed), ela é removida do ClientService. O broadcast ignora sessões com client.isOpen() == false, evitando erros de envio para conexões mortas. O quadro continua operacional para todos os demais participantes.
  • Exceções individuais de envio: Exceções lançadas ao enviar para um cliente específico são capturadas com catch (Exception ignored) dentro do laço de broadcast, garantindo que uma falha em um destinatário não interrompa a entrega aos demais.
  • Consistência na reconexão: Ao reconectar, o cliente recebe o estado atual completo do quadro via unicast no afterConnectionEstablished, reestabelecendo sua visão consistente sem necessidade de replay de eventos anteriores.

6. Conclusão

O PixelHub demonstra com sucesso a adoção de Comunicação em Grupo como mecanismo de comunicação indireta. O servidor atua como coordenador do grupo, eliminando qualquer acoplamento ponto a ponto entre os clientes e garantindo o desacoplamento espacial exigido: nenhum cliente precisa conhecer a identidade ou localização de rede dos demais. A implementação é funcional, thread-safe e estruturalmente adequada ao modelo de grupo. As análises de overhead e as estratégias de mitigação propostas demonstram maturidade na compreensão dos trade-offs inerentes à comunicação indireta em sistemas distribuídos.

About

Servidor de Pixel Art Colaborativo

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages