Quando usar atores em vez de soluções de mensagens, como WebSphere MQ ou Tibco Rendezvous?

106

Já li a pergunta e as respostas para Quais decisões de design favoreceriam os atores de Scala em vez de JMS? .

Normalmente, usamos soluções de mensagens que já existem há anos: uma implementação JMS, como WebSphere MQ ou Apache ActiveMQ, é usada para comunicação ponto a ponto ou Tibco Rendevous para mensagens multicast.

Eles são muito estáveis, comprovados e oferecem alta disponibilidade e desempenho. No entanto, a configuração e instalação parecem muito mais complexas do que na Akka.

Quando e por que devo usar o Akka para alguns casos de uso em que os produtos mencionados - WebSphere MQ ou ActiveMQ - foram usados ​​com sucesso até agora? Por que devo considerar o uso de Akka em vez de WebSphere MQ ou Tibco RV em meu projeto futuro?

E quando devo evitar Akka? Oferece a mesma alta disponibilidade e desempenho que as outras soluções? Ou é uma má ideia comparar o Akka com outros middlewares de mensagens?

Talvez haja também outra solução de mensagens no ambiente JVM que eu deva considerar além de JMS (Point-to-Point), TibcoRV (Multicast) e Akka?

Kai Wähner
fonte

Respostas:

92

Em primeiro lugar, os sistemas de mensagens "mais antigos" (MQ) são mais antigos na implementação, mas são mais novos na ideia de engenharia de: filas persistentes transacionais . Scala Actors e Akka podem ser uma implementação mais recente, mas são construídos em um modelo de simultaneidade mais antigo de Actors.

Os dois modelos, entretanto, acabam sendo muito semelhantes na prática porque ambos são baseados em mensagens de evento: Veja minha resposta para RabbitMQ vs Akka .

Se você pretende codificar apenas para JVM, Akka provavelmente é uma boa escolha. Caso contrário, eu usaria RabbitMQ.

Além disso, se você é um desenvolvedor Scala, então Akka deve ser um acéfalo. No entanto, as ligações Java da Akka não são muito Java e requerem conversão devido ao sistema de tipos do Scala.

Também em Java as pessoas normalmente não fazem objetos imutáveis, o que eu recomendo que você faça para mensagens. Consequentemente, é muito fácil em Java fazer algo acidentalmente usando Akka que não será escalonado (usando objetos mutáveis ​​para mensagens, contando com um estado de retorno de chamada de encerramento estranho). Com o MQ, isso não é um problema porque as mensagens são sempre serializadas ao custo da velocidade. Com Akka eles geralmente não são.

Akka também se adapta melhor com uma grande quantidade de consumidores do que a maioria do MQ. Isso ocorre porque para a maioria dos clientes MQ (JMS, AMQP), cada conexão de fila requer um encadeamento ... portanto, muitas filas == muitos encadeamentos em execução permanente. No entanto, este é principalmente um problema do cliente. Acho que o ActiveMQ Apollo tem um despachante sem bloqueio que supostamente corrige esse problema para AMQP. O cliente RabbitMQ possui canais que permitem combinar vários consumidores, mas ainda existem problemas com um grande número de consumidores, potencialmente causando deadlocks ou conexões morrendo, então geralmente mais threads são adicionados para evitar esse problema.

Dito isso, o remoting da Akka é bastante novo e provavelmente ainda não oferece todas as garantias de mensagens confiáveis ​​e QoS que as filas de mensagens tradicionais fornecem (mas isso está mudando todos os dias). Também é geralmente ponto a ponto, mas eu acho que suporta servidor a ponto, que geralmente é o que a maioria dos sistemas MQ faz (ou seja, ponto único de falha), mas há sistemas MQ que são ponto a ponto (RabbitMQ é servidor to-peer).

Finalmente RabbitMQ e Akka realmente formam um bom par. Você pode usar o Akka como um wrapper para o RabbitMQ, especialmente porque o RabbitMQ não o ajuda a lidar com o consumo de mensagens e rotear as mensagens localmente (em uma única JVM).

Quando escolher Akka

  • Tenha muitos consumidores (pense em milhões).
  • Necessita de baixa latência
  • Aberto ao modelo de simultaneidade do ator

Sistema de exemplo: um sistema de bate-papo interativo em tempo real

Quando escolher MQ

  • Precisa se integrar com muitos sistemas diferentes (ou seja, não JVM)
  • A confiabilidade da mensagem é mais importante do que a latência
  • Gostaria de mais ferramentas e IU de administrador
  • Por causa dos pontos anteriores, é melhor para tarefas de longa duração
  • Gostaria de usar um modelo de simultaneidade diferente do Actors

Sistema de exemplo: um sistema de processamento em lote transacional programado

EDITAR com base em comentários preocupados

Presumi que o OP se preocupava com o processamento distribuído que tanto o Akka quanto o Message Queues podem controlar. Presumi que ele estava falando sobre a Akka distribuída . Usar o Akka para simultaneidade local é uma comparação comparativa com a maioria das filas de mensagens . Digo mais porque você pode aplicar o modelo de fila de mensagens localmente como um modelo de simultaneidade (ou seja, tópico, filas, trocas) que a biblioteca Reactor e a reação simples fazem.

Escolher o modelo / biblioteca de simultaneidade certo é muito importante para aplicativos de baixa latência. Uma solução de processamento distribuído, como uma fila de mensagens, geralmente não é ideal porque o roteamento é quase sempre feito através da rede, que é obviamente mais lento do que dentro do aplicativo e, portanto, Akka seria uma escolha superior. No entanto, acredito que algumas tecnologias proprietárias do MQ permitem o roteamento local. Além disso, como mencionei anteriormente, a maioria dos clientes MQ são muito estúpidos sobre threading e não dependem de IO sem bloqueio e têm um thread por conexão / fila / canal ... ironicamente, io sem bloqueio nem sempre é de baixa latência, mas geralmente é mais recurso eficiente.

Como você pode ver, o tópico de programação distribuída e programação simultânea é bastante extenso e muda todos os dias, então minha intenção original não era confundir, mas sim focar em uma área particular de processamento de mensagem distribuída que é o que eu pensei que o OP estava preocupado. Em termos de simultaneidade, pode-se querer focar suas pesquisas na programação "reativa" (RFP / streams), que é um modelo "mais novo", mas semelhante ao modelo de ator e modelo de fila de mensagens, dos quais todos esses modelos podem ser geralmente combinados porque eles são baseados em eventos.

Adam Gent
fonte
3
Acho que uma resposta a uma pergunta errada não pode estar certa. Você não pode comparar uma fila de mensagens e um modelo de simultaneidade. Eles são construídos para resolver tarefas COMPLETAMENTE diferentes e têm em comum apenas a palavra "mensagem".
Igor S.
2
Bem, sim e não. A Akka oferece suporte a mensagens distribuídas e você pode facilmente construir um modelo de simultaneidade fora do paradigma da fila de mensagens (google Spring's Reactor). Na verdade, a única diferença agora é que o RabbitMQ tem mensagens duráveis ​​... oh, espere, a Akka também suporta isso agora. Ele pode dizer "Ator" no título, mas aponta explicitamente o Akka, que tem uma sobreposição massiva com muitos sistemas baseados em mensagens (simultâneos e distribuídos).
Adam Gent
4
BTW @IgorS. o modelo de simultaneidade tipicamente usado com filas de mensagens é chamado de SEDA (staged event driven architecture). Além de usar Filas, Tópicos e Trocas, é um modelo de simultaneidade em si (que também é um modelo distribuído .. assim como o modelo de ator). Eu também detesto quando alguém diz "pergunta errada" .. além de perguntas inadequadas, quando uma pergunta pode estar errada? É sarcástico e elitista dizer algo assim.
Adam Gent
1
Eu nunca disse que eles eram intercambiáveis. Eu até digo que eles funcionam muito bem juntos e por quê. Mas ele claramente está falando sobre akka distribuída aqui e não sobre a biblioteca de atores akka. É assim que eu li. Sinta-se à vontade para editar minha postagem, pois seu ponto é válido e pode confundir outras pessoas que tropeçarem na postagem.
Adam Gent
1
Uma, a Akka Java API - é muito limpa agora, especialmente com JDK 8 lambdas. Eu suspeito que ficará melhor se / quando eles introduzirem objetos de valor com JDK 10.
Rob Crawford
4

Não sou especialista em sistemas de mensagens, mas você pode combiná-los com o Akka em seus aplicativos, obtendo o melhor dos dois mundos. Aqui está um exemplo que você pode achar útil para experimentar os sistemas Akka e de mensagens, neste caso ZeroMQ:

https://github.com/zcox/akka-zeromq-java

Dean Wampler
fonte
6
ZeroMQ não é exatamente um sistema de mensagens. É antes algum tipo de encaixe melhorado. Sistemas de mensagens completos são muito mais complexos do que ZeroMQ. O projeto em seu link parece ser apenas um envoltório fino em torno do ZeroMQ com Akka.
Vladimir Matveev
1

Akka-Camel seria um exemplo melhor do que ZeroMQ - ZeroMQ é uma comunicação tcp para tcp direta (portanto, zero - não há fila de mensagens).

Com o AkkaCamel você pode abstrair a fila e produzir / consumir mensagens diretamente de um ator sem nenhum código para lidar com o push / pull de mensagens da fila de mensagens.

Você pode ignorar o akka-zeromq e usar o Akka diretamente com o remoting. Acho que akka-zeromq está sendo removido da biblioteca principal, mas construímos uma boa biblioteca zeromq para akka chamada scala-zeromq ( https://github.com/mDialog/scala-zeromq )

A Akka tem alguns casos de uso principais:

1) Estado mutável

É mais fácil lidar com o estado compartilhado ocultando-o em um ator. Como os atores lidam com as mensagens de forma síncrona, você pode manter o estado em um ator e expor esse campo com alta consistência por meio da API do ator

2) Distribuição

A simultaneidade é gratuita no Akka, então você diz que realmente trata de resolver problemas de distribuição. Distribuição entre máquinas e núcleos. Akka incorporou "transparência de localização" para o envio de mensagens pela rede. Ele também possui clustering e padrões associados para aumentar a escala de um único serviço. Isso o torna uma solução muito boa para distribuição (por exemplo, arquitetura de micro-serviços)

Aqui está um exemplo de uso de Akka com ActiveMQ com Akka-Camel (usando Java8)

import akka.actor.Props;
import akka.camel.Camel;
import akka.camel.CamelExtension;
import akka.testkit.TestActorRef;
import akka.testkit.TestProbe;
import org.junit.Ignore;
import org.junit.Test;
import akka.camel.javaapi.UntypedProducerActor;
import akka.camel.javaapi.UntypedConsumerActor;
import static com.rogers.totes.TotesTestFixtures.*;
import org.apache.activemq.camel.component.*;

public class MessagingTest {
    @Test @Ignore
    public void itShouldStoreAMessage() throws Exception{
        String amqUrl = "nio://localhost:61616";
        Camel camel = (Camel) CamelExtension.apply(system);
        camel.context().addComponent("activemq", ActiveMQComponent.activeMQComponent(amqUrl));

        TestProbe probe = TestProbe.apply(system);
        TestActorRef producer = TestActorRef.create(system, Props.create((Producer.class)));
        TestActorRef consumer = TestActorRef.create(system, Props.create((Consumer.class)));
        producer.tell("Produce", probe.ref());

        Thread.sleep(1000);
    }
}

class Producer extends UntypedProducerActor{

    @Override
    public String getEndpointUri() {
        return "activemq:foo.bar";
    }
}

class Consumer extends UntypedConsumerActor{

    @Override
    public String getEndpointUri() {
        return "activemq:foo.bar";
    }

    @Override
    public void onReceive(Object message) throws Exception {
        System.out.println("GOT A MESSAGE!" + message);

    }
}
JasonG
fonte