Press "Enter" to skip to content

Request – Reply com MassTransit

Estamos acostumados com alguns padrões para desenvolvimento, a sopa de letras é enorme, vamos de MVC, MVVM, passamos por SOLID, e temos o famoso livro do GOF, no mundo da integração de sistemas existem os Enterprise Integration Patterns. Vou mostrar aqui no blog 2 desses padrões, e começarei pelo mais simples que é o Request/Reply.

Porém, antes de continuar, recomendo a leitura dos 3 primeiros artigos que escrevi sobre mensageria.
Agora vou direto para a prática, assim ficará mais fácil definir e mostrar alguns conceitos.

Nos exemplos vou utilizar o framework MassTransit, e o MSMQ da Microsoft, já que é gratuito e está disponível no Windows. É só habilitar nos recursos do Windows.

 


O nome do padrão é auto explicativo, teremos uma aplicação que envia uma mensagem, e aguarda uma mensagem de retorno,  é por isso que utilizamos o MSMQ ou no caso do MassTransit temos também a opção de utilizar o RabbitMQ. Mas vamos com calma, vamos entender nossas mensagens.

No exemplo teremos duas mensagens RequisitarCalculoSalario e RespostaCalculoSalario, as mensagens não serão utilizadas para trafegar dados. Vou utilizá-las apenas para passar o identificador do funcionário que desejo saber o salário.  Então podemos falar que nossas mensagens seriam POCO´s simples, ou melhor o contrato que uma aplicação conhece, não importa quem solitica ou quem responde a mensagem, o que importa é receber a mensagem que estou interessado. Segue a implementação das mensagens.

[tabs]

[tab title=”SolicitarCalculosalario”]

public class SolicitarCalculoSalario : CorrelatedBy<Guid>
{
    public Guid CorrelationId { get; set; }
    public int IdFuncionario { get; set; }

    public SolicitarCalculoSalario()
    {
        CorrelationId = Guid.NewGuid();
    }
}

[/tab]
[tab title=”RespostaCalculoSalario”]

public class RespostaCalculoSalario : CorrelatedBy
{
    public Guid CorrelationId { get; set; }
    public decimal Salario { get; set; }
}

[/tab]
[/tabs]

O primeiro ponto que precisamos entender sobre o MassTransit, é que as mensagens devem implementar a Interface CorrelatedBy<TKey>, o CorrelationId pode ser entendido como um identificador entre as transações, e pensando em larga escala, é recomendado utilizar Guid para a implementação.

Então atenção para a primeira regra, toda mensagem utilizando o MassTransit deve implementar a Interface CorrelatedBy<TKey>.

Com as mensagens criadas, o próximo passo é implementar o Requester, nesse exemplo será um ConsoleApp. Um detalhe é que ao criar um projeto com .NET 4.0, o target framework nas configurações do projeto por padrão assume o valor .Net Framework 4 Client Profile, com o Client Profile o projeto não vai compilar, então é necessário alterar para .Net Framework 4, lembrando que o MassTransit também gera as .dlls para .Net 3.5.

Abaixo o código está separado em 3 partes: a configuração do ServiceBus, a publicação da requisição e o código completo do funcionamento do Requester.

[tabs]
[tab title=”ServiceBus”]

Bus.Initialize(sbc =>
   {
        sbc.UseMsmq();
        sbc.VerifyMsmqConfiguration();
        sbc.UseMulticastSubscriptionClient();
        sbc.ReceiveFrom("msmq://localhost/requester");
    });

[/tab]
[tab title=”publishrequest”]

Bus.Instance.PublishRequest(
    new SolicitarCalculoSalario { IdFuncionario = idFuncionario },
    callBack =>
        {
            callBack.Handle<RespostaCalculoSalario>(message =>
                {
                    Console.WriteLine("Resposta da Mensagem {0} recebida", message.CorrelationId);
                    Console.WriteLine("Salario: {0}", message.Salario);
                });
             callBack.SetTimeout(10.Seconds());
         });

[/tab]
[tab title=”requester”]

static void Main(string[] args)
{
    Bus.Initialize(sbc =>
        {
            sbc.UseMsmq();
            sbc.VerifyMsmqConfiguration();
            sbc.UseMulticastSubscriptionClient();
            sbc.ReceiveFrom("msmq://localhost/requester");
        });

    Console.WriteLine("Informe o ID do funcionário para calcular o salário:");
    var idFuncionario = 0;
    int.TryParse(Console.ReadLine(), out idFuncionario);

    while (idFuncionario != -1)
    {
        Bus.Instance.PublishRequest(
            new SolicitarCalculoSalario { IdFuncionario = idFuncionario },
                callBack =>
                {
                    callBack.Handle<RespostaCalculoSalario>(message =>
                        {
                            Console.WriteLine("Resposta da Mensagem {0} recebida", message.CorrelationId);
                            Console.WriteLine("Salario: {0}", message.Salario);
                        });
                    callBack.SetTimeout(10.Seconds());
                });

                int.TryParse(Console.ReadLine(), out idFuncionario);
     }
}

[/tab]
[/tabs]

Agora uma explicação detalhada sobre a configuração do ServiceBus.

  •  sbc.UseMsmq(): A primeira escolha que precisamos fazer qual “transporte” vamos utilizar para as mensagens, assim nós configuramos que estamos utilizando o MSMQ.
  • sbc.VerifyMsmqConfiguration(): Já que estamos utilizando o MSMQ, com esse método validamos se a instalação do MSMQ está correta.
  • sbc.UseMulticastSubscriptionClient(): Aqui nós estamos habilitando a possibilidade do Bus “conversar” com outras instâncias de ServiceBus na rede, com isso não precisamos ter um ponto central de controle das instâncias existentes.
  • sbc.ReceiveFrom(“msmq://localhost/requester”): Configuração de a fila que o Requester vai receber as mensagens, o interessante é que se a fila não está criada ainda, o MassTransit faz todo o processo de criação da fila.

Outras opções são disponibilizadas na configuração do ServiceBus, mas para o exemplo de hoje está bom!

Depois que configuramos o ServiceBus, vamos publicar uma mensagem, no caso será publicada uma mensagem do tipo SolicitarCalculoSalario, passando o Id do Funcionário, para isso utilizamos o método PublishRequest.

No primeiro parâmetro do PublishRequest informamos a mensagem que vamos publicar, e no segundo parâmetro informamos o callBack, um detalhe para a configuração do callBack é para o método Handle<>, aqui estamos falando que o Requester está esperando mensagens do tipo RespostaCalculoSalario de retorno, e quando uma dessas mensagens for recebida temos a lógica de escrever na tela as informações da mensagem.

Logo após o método Handle<> precisamos definir um Timeout para o callBack, ou seja a thread que publicou a solicitação ficará bloqueada até receber a mensagem de retorno, ou até acabar o tempo configurado no timeout, se o tempo acabar uma Exception é disparada, vale ressaltar que não precisamos bloquear a thread, temos recursos de async no .Net que podemos utilizar, mas isso fica para outro artigo.

No código acima veja a aba Requester para ver toda a lógica do programa.

Agora que as mensagens e o Requester estão prontos, vamos ao código do Responser, o código é muito parecido com  o do Requester, então segue o código completo, logo abaixo tem a explicação dos detalhes diferentes.

[tabs]
[tab title=”Responser”]

static void Main(string[] args)
{
    Bus.Initialize(sbc =>
        {
            sbc.UseMsmq();
            sbc.VerifyMsmqConfiguration();
            sbc.UseMulticastSubscriptionClient();
            sbc.ReceiveFrom("msmq://localhost/responser");
            sbc.Subscribe(subs => subs.Handler<SolicitarCalculoSalario>(
                msg =>
                {
                    Console.WriteLine("Solicitação {0} recebida, iniciando calculo do funcionário {1}",
                                       msg.CorrelationId, msg.IdFuncionario);

                    var resposta = new RespostaCalculoSalario
                        {
                            CorrelationId = msg.CorrelationId,
                            Salario = (decimal) (msg.IdFuncionario*DateTime.Now.Millisecond * 3.14)
                        };

                        Console.WriteLine("Enviando resposta");
                        Bus.Instance.MessageContext().Respond(resposta);
                 }));
            });

        Console.WriteLine("Aguardando mensagens");
        Console.Read();
    }

[/tab]
[/tabs]

A principal diferença do Responser é que é utilizado o método Subscribe do ServiceBus para inscrever o Responser para receber mensagens do tipo SolicitarCalculoSalario, quando uma mensagem chega, é criada uma mensagem do tipo RespostaCalculoSalario, e é definido o CorrelationId da nova mensagem igual o da mensagem recebida, e ai faço uma lógica qualquer para descobrir o salário do Funcionário.

Feito isso através do método MessageContext() do ServiceBus, eu consigo informações sobre quem fez a requisição, e envio uma mensagem de resposta utilizando o método Respond().

Abaixo uma imagem dos programas funcionando e conversando utilizando as mensages.

 

O assunto não é dos mais simples, mas é muito interessante. Entre os benefícios posso destacar alto desacoplamento, a possibilidade de distribuir processamento, alta escalabilidade, esses itens vamos ver com mais detalhes quando implementarmos o exemplo de Grid/Distributor, mas ai já é avançar muito por enquanto!

Pegue o código fonte no Github e boa diversão!

Abraços.

 

  • Djonatas Tenfen

    Parabéns pelo post. Muito legal.

    • Márcio Fábio Althmann

      Valeu 🙂

  • José Filipe Neis

    Márcio, parabéns pela iniciativa de escrever sobre o assunto. Realmente faltam textos em PT-BR.

    Abs,

    JF