segunda-feira, 10 de fevereiro de 2014

ZeroMQ: Request Síncrono/Reply Assíncrono

Agora é a vez de invertemos a situação em relação ao post anterior. Vamos criar um cliente síncrono e um servidor assíncrono.

Vamos lá, crie um projeto C# do tipo console, versão 4.5 do .NET com o nome zeromq.testes.sync-request. Instale o ZeroMQ via NuGet com install-package clrzmq -pre.

Vamos fazer o mesmo para o nosso servidor assíncrono. Crie um projeto com as mesmas características e dê o nome de zeromq.testes.async-reply. Instale o ZeroMQ como no projeto anterior.

Código


Use o trecho de código a seguir para o cliente -- request.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using ZeroMQ;

namespace zeromq.testes.sync_request
{
 class Program
 {
  static void Main(string[] args)
  {
   using (var context = ZmqContext.Create())
   using (var socket = context.CreateSocket(SocketType.REQ))
   {
    socket.Connect("tcp://127.0.0.1:5555");

    while (true)
    {
     string request_body = Guid.NewGuid().ToString();
     
     ZmqMessage request = new ZmqMessage(new List<byte[]>
     { 
      Encoding.Unicode.GetBytes(request_body),
     });

     socket.SendMessage(request);

     Console.WriteLine("{0} - requisição enviada: {1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), request_body);
     
     ZmqMessage resposta = socket.ReceiveMessage();
     
     Console.WriteLine("{0} - resposta recebida: {1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"), Encoding.Unicode.GetString(resposta[0]));
     
     Thread.Sleep(2000);
    }
   }
  }
 }
}

Use o trecho de código a seguir para o servidor -- reply.

using System;
using System.Collections.Generic;
using System.Text;
using ZeroMQ;

namespace zeromq.testes.async_reply
{
 class Program
 {
  static void Main(string[] args)
  {
   using (ZmqContext context = ZmqContext.Create())
   using (ZmqSocket router = context.CreateSocket(SocketType.ROUTER))
   {
    router.Bind("tcp://*:5555");

    router.ReceiveReady += (sender, eventArgs) =>
    {
     ZmqMessage request_msg = eventArgs.Socket.ReceiveMessage();
     
     string request_body = Encoding.Unicode.GetString(request_msg[2].Buffer);
     
     string reply_body = request_body.Replace("-", string.Empty).ToUpper();
     
     Console.WriteLine("{0} - resposta enviada: {1}", DateTime.Now, reply_body);
     
     ZmqMessage reply_msg = new ZmqMessage(new List<byte[]> 
     {
      request_msg[0].Buffer, // Id da requisição
      Encoding.Unicode.GetBytes(string.Empty), // truque necessário
      Encoding.Unicode.GetBytes(reply_body), // conteúdo da resposta
     });

     router.SendMessage(reply_msg);
    };

    Poller poller = new Poller(new List<ZmqSocket> { router });

    while (true)
    {
     poller.Poll();
    }
   }
  }
 }
}

Execute os dois projetos ao mesmo tempo e você deverá ver o resultado: os dois consoles exibindo trocas de mensagens.

Truques

Assim como no exemplo anterior, aqui também há truques que se você souber de antemão, sua vida terá mais qualidade ;)

Linha 21 do server: A requisição síncrona é composta por basicamente 3 frames: 1) O Id do componente de requisição (também chamado de endereço) 2) Um frame vazio delimitador e 3) O corpo propriamente dito da mensagem. Dessa forma, nessa linha obtemos o corpo da mensagem.

Linha 27 do servidor (29, 30, e 31 na verdade): Como explicado acima, a requisição vem com um frame indicando o endereço, um delimitador nulo e o corpo da mensagem. Sendo assim, nós temos que devolver a mensagem de resposta da mesma forma. Caso contrário, o componente da requisição vai descartar a mensagem de resposta não a reconhecendo como uma mensagem válida. Acredite, foi um porre descobrir isso... Eu queria ter visto isso nos exemplos, mas acabei tendo que ler a documentação.

Linha 41 do servidor: Como o socket ROUTER é assíncrono, nós temos que usar o Poller para que o evento registrado na linha 17 seja disparado.

Uma outra questão que ainda não mencionei em nenhum outro post sobre o ZeroMQ: o tal do Bind e Connect. Por padrão, o lado "servidor" da relação, geralmente usa Bind, e o lado "cliente" usa Connect. O Bind só pode ser feito em um endereço e o Connect não tem restrição; mais de um Connect pode ser feito em um endereço.

Se você se interessou pelo ZeroMQ, dê uma boa lida em seu guia em http://zguide.zeromq.org/page:all. É longo, mas é um material muito bom e tem exemplos em muitas linguagens (inclusive C#) para a maioria dos padrões explicados.

Nenhum comentário:

Postar um comentário