SignalR: resolvendo o problema da comunicação em tempo real

Por dti digital|
Atualizado: Mar 2018 |
Publicado: Set 2015

Nos últimos tempos, a demanda para aplicações em tempo real vem crescendo consideravelmente. Sites como Facebook e Twitter, jogos interativos, cotação de bolsa de valores, notificações de e-mail, chats, aplicações e-commerce, entre outros, são exemplos que utilizam a comunicação em tempo real.

Mas, afinal, o que é uma aplicação em tempo real?

É uma aplicação na qual a informação é compartilhada em tempo real, ao vivo. Nesta aplicação, o servidor é capaz de sensibilizar / informar todos os clientes conectados assim que uma informação chega, sem precisar esperar que o cliente faça alguma requisição por estes dados.

Como isso pode ser feito?

Uma das primeiras alternativas apontadas para tentar resolver o problema da comunicação em tempo real foi o Ajax Polling, em que requisições são enviadas do cliente para o servidor de tempos em tempos. Quando o servidor responde, a conexão é fechada. Esta alternativa traz diversas desvantagens, como a perda de atualizações (caso o tempo de atualização não seja suficientemente pequeno) e a sobrecarga de requisições no servidor, principalmente quando se diminui o tempo entre requisições em busca de uma atualização mais confiável. Além disso, mesmo quando o servidor não possui respostas relevantes, o cliente continua fazendo requisições, o que deixa a comunicação mais lenta.

O Long Polling é outra tecnologia existente para suportar este tipo de comunicação. O cliente cria uma conexão com o servidor, que permanece aberta até que o servidor tenha uma nova atualização ou que o tempo da conexão expire. Em ambos os casos, o servidor responde ao cliente e ele, em seguida, faz uma nova requisição. Esta alternativa não tem atrasos, porém ainda existe a sobrecarga de requisições ao servidor.

Outra técnica existente é o Forever Frame, em que um iFrame escondido é criado no cliente e uma conexão duradoura é estabelecida com o servidor. Com isso, o servidor pode enviar trechos de código javascript para o cliente quando desejar notificá-lo de algo. A desvantagem nesse caso é que este método depende de particularidades de cada browser.

Na abordagem do Server Sent Event, o cliente estabelece uma conexão persistente com o servidor e as informações fluem somente no sentido servidor-cliente. Um objeto do tipo EventSource é criado no cliente com a url do servidor na qual haverão as atualizações. Sempre que uma atualização for percebida pelo servidor, um evento onMessage ocorre e a mensagem enviada pelo servidor pode ser interpretada pelo cliente. Essa técnica, no entanto, não é suportada no IE.

Finalmente, temos a técnica do Web Sockets, que é a melhor alternativa para tratar uma comunicação em tempo real. Uma conexão TCP é criada entre o cliente e o servidor e mantida enquanto necessário. A troca de mensagens é bidirecional, constante e rápida. Porém, é um protocolo que não é aceito em todos os navegadores web (Para o IE, é aceito somente na versão 10 em diante) e necessita estar instalado no IIS (oficialmente a partir do IIS 8).

E agora? Devo escolher entre as técnicas existentes? Ou devo implementar um código que suporte as particularidades browsers / servidores?

Nenhuma das duas alternativas! O SignalR é a resposta para esta pergunta. Trata-se de uma biblioteca open-source que veio para facilitar o gerenciamento e escolha dos protocolos de comunicação em tempo real, provendo atualizações / notificações de forma assíncrona em uma aplicação.

A biblioteca foi desenvolvida por dois funcionários da Microsoft, tendo sido, há algum tempo, incorporada pela plataforma ASP.net. Todo seu código fonte encontra-se no GitHub.

A tecnologia disponibiliza uma API para realização de uma chamada remota de procedimento, que permite a realização de chamadas em outros espaços de endereçamento, independente de como o procedimento está implementado. Isso permite a chamada de métodos do cliente no servidor e vice-versa. Além disso, o SignalR gerencia as conexões cliente-servidor, de forma que é possível a troca de mensagens entre servidor e todos os clientes, servidor e um cliente específico, servidor e um grupo de clientes.

Por baixo dos panos, o SignalR define o melhor tipo de comunicação entre aqueles já citados, de acordo com as configurações do cliente e do servidor.

Fernandinha1

No caso do cliente e do servidor permitirem o uso de um protocolo padronizado no HTML5, o melhor protocolo será usado. Caso não seja possível, o SignalR utilizará o modelo de programação Comet para estabelecer a comunicação em tempo real.

WebSockets é o transporte ideal para o SignalR pelas vantagens do mesmo. Por que então não implementar direto esta técnica? Porque utilizar o SignalR significa que várias implementações necessárias para o funcionamento da comunicação tempo real já vão estar feita para você. Mais importante, significa que você poderá usufruir das vantagens do WebSockets sem precisar se preocupar se o cliente ou servidor suportam esta tecnologia, sem encher o código de IF´s para tratar particularidades de clientes ou servidores. Com isso, apesar das formas de transporte serem variantes, o código fonte implementado é o mesmo.

Mas é complicada a implementação?

Não! Como exemplo, faremos uma aplicação simples de um chat. A intenção deste exemplo é apenas mostrar uma utilização simples e útil do SignalR.

A imagem a seguir resume a comunicação cliente-servidor estabelecida pelo SignalR:

Fernandinha2

O primeiro passo é incluir as bibliotecas do SignalR no seu projeto. Uma forma fácil de fazer isso é pelo gerenciador de componentes Nuget do Visual Studio. Vá em Tools | Library Package Manager | Package Manager Console e insira SignalR na busca.

O próximo passo é criar um arquivo de configuração do SignalR no projeto. Clique com o botão direito no projeto e adicione um novo Owin Startup Class. Caso sua versão do Visual Studio não possua essa opção, crie uma classe Startup da seguinte forma na raiz do projeto:

Startup.cs

f1

 

A interface da nossa aplicação é bem simples: Uma tela de troca de mensagens que recebe o nome do usuário que está conectando à aplicação, registra esse usuário e o disponibiliza para troca de mensagens com os outros usuários conectados.

HomeController.cs

f2

 

Chat.cshtml

f3

f4

chat.js

f5

Na master page da aplicação, _Layout.cshtml, foi incluído um bundle contendo a biblioteca do SignalR, jquery.signalR-2.2.0.js.

Como podemos ver, na view mostrada, incluímos também um script signalr/hubs. Este script é necessário para o SignalR funcionar corretamente e é gerado automaticamente na execução da aplicação. Ele é responsável por fornecer todas as informações necessárias para a comunicação com o servidor.

Na parte do servidor, devemos criar uma classe que herda da classe Hub. O Hub é a forma de conexão alto nível entre cliente e servidor utilizada pelo SignalR; nessa classe, portanto, estarão implementados os métodos disponíveis para serem chamados pelo cliente e também a chamada de funções que estão declaradas no cliente.

ChatHub.cs

f6

Nessa classe, podemos ver a implementação de um dicionário cuja chave e valor são strings para controle dos usuários que estão logados nesta aplicação. Essa implementação é simplista, por usar uma variável estática. Em uma aplicação mais complexa deve-se tratar a concorrência, múltiplos servidores, etc.

No código mostrado, temos algumas implementações bastante importantes:

1) Declaração de um proxy no cliente que se referencia ao hub no servidor

f7

 

Quando fazemos esta implementação, estamos declarando o hub ChatHub (implementado no ChatHub.cs) no cliente, para estabelecer a comunicação.

2) Declaração das funções usuarioConectado e enviarMensagem no cliente para serem acessadas pelo servidor

f8

3) Declaração das funções AdicionarUsuario e EnviarMensagem no servidor para serem acessadas pelo cliente

f9

4) Iniciação da conexão do hub declarado no cliente e realização de ações após abertura da conexão.

f10

5) Chamada de funções declaradas no servidor pelo cliente

f11

6) Chamada de funções declaradas no cliente pelo servidor.

f12

O objeto Clients é dinâmico, de forma que é possível alterar propriedades ou chamar métodos que podem não existir fisicamente. O SignalR interpreta todas estas chamadas em mensagens para serem enviadas aos clientes que estão conectados no servidor.

Nos dois primeiros métodos mostrados, chamamos Clients.All, que significa que queremos realizar a chamada pretendida em todos os clientes conectados no servidor; Já na implementação de Clients.User(connectionId), estamos realizando a chamada do método enviarMensagem somente para um cliente especificado.

Simples não é? Conexão bidirecional cliente-servidor e você não precisou especificar o transporte em nenhum momento; o SignalR simplesmente escolhe o melhor para você.

Ok, Entendi. Mas e se eu possuo uma aplicação com múltiplos servidores?

Como a conexão do SignalR é entre um servidor e um cliente específico, caso existam múltiplos servidores na arquitetura do sistema, se a atualização for percebida por um servidor e o cliente a ser notificado não tenha se conectado nele, haverá a perda de mensagem. Para solucionar este problema, o SignalR possibilita a implementação de um Backplane, que é um barramento para distribuição de mensagens. O servidor comunica com o backplane e ele reenvia as mensagens para os outros servidores, sincronizando-os e evitando a perda de mensagem.

Saiba mais sobre a implementação do backplane aqui.

Então devo sempre utilizar o SignalR para comunicação em tempo real?

Sim! Com ele você tem:

  • Funcionalidades encapsuladas e prontas.
  • Pode-se tirar vantagens dos transportes mais complexos sem se preocupar com clientes antigos.
  • Não é necessária a preocupação com a atualização dos diferentes transportes e criação de uma nova forma de transporte
  • Não é preciso fazer a escolha da melhor opção de transporte
  • Fácil implementação

Agora que você sabe as vantagens, já pode usar o SignaIR nas suas aplicações. Se tiver alguma dúvida, entre em contato com a gente.

 

Por: Fernanda Vieira
Revisão: Jéssica Saliba.

Quer saber mais?