何时使用actor而不是WebSphere MQ或Tibco Rendezvous等消息传递解决方案?

时间:2011-04-17 11:51:13

标签: java scala jms actor akka

我已经阅读了What design decisions would favour Scala's Actors instead of JMS?的问题和答案。

通常,我们使用已经存在多年的消息传递解决方案:使用JMS实现(如WebSphere MQ或Apache ActiveMQ)进行点对点通信,或使用Tibco Rendevous进行多播消息传递。

它们非常稳定,经过验证,可提供高可用性和高性能。然而,配置和设置似乎比Akka复杂得多。

到目前为止,在上述产品(WebSphere MQ或ActiveMQ)成功使用的一些用例中,我何时以及为何应该使用Akka?为什么我应该考虑在未来的项目中使用Akka而不是WebSphere MQ或Tibco RV?

我什么时候应该避开Akka?它是否提供与其他解决方案相同的高可用性和性能?或者将Akka与其他消息传递中间件进行比较是不是一个坏主意?

除了JMS(点对点),TibcoRV(多播)和Akka之外,JVM环境中还有其他消息传递解决方案吗?

3 个答案:

答案 0 :(得分:85)

首先,“较旧”的消息系统(MQ)在实现方面较旧,但它们是以下工程理念中较新的:事务持久性队列。 Scala Actors和Akka可能是一个较新的实现,但建立在Actors的旧并发模型之上。

然而,这两个模型在实践中最终非常相似,因为它们都是基于事件消息的:请参阅我对RabbitMQ vs Akka的回答。

如果你只想为JVM编码,那么Akka可能是个不错的选择。否则我会使用RabbitMQ。

此外,如果您是Scala开发人员,那么Akka应该是一个明智的选择。然而,由于Scala的类型系统,Akka的Java绑定不是非常Java并且需要进行转换。

同样在Java中,人们通常不会创建我建议您用于消息传递的不可变对象。因此,Java很容易使用Akka意外地做一些无法扩展的事情(使用可变对象进行消息,依赖于奇怪的闭包回调状态)。使用MQ这不是问题,因为消息总是以速度为代价进行序列化。对于Akka,他们通常不会。

与大多数MQ相比,Akka的消费者数量也更多。这是因为对于大多数MQ(JMS,AMQP)客户端,每个队列连接都需要一个线程......因此有很多队列==许多永久运行的线程。这主要是客户问题。我认为ActiveMQ Apollo有一个非阻塞调度程序,据称可以解决AMQP的问题。 RabbitMQ客户端具有允许您组合多个使用者的通道,但仍存在大量消费者可能导致死锁或连接死亡的问题,因此通常会添加更多线程以避免此问题。

据说Akka's remoting相当新,可能仍然没有提供传统消息队列提供的所有可靠的消息保证和QoS(但这种情况每天都在变化)。它也通常是点对点但我认为支持服务器到对等,这通常是大多数MQ系统所做的事情(即单点故障),但有一些MQ系统是点对点的(RabbitMQ是服务器 - 对等)。

最后,RabbitMQ和Akka实际上是一对。你可以使用Akka作为RabbitMQ的包装器,特别是因为RabbitMQ无法帮助你处理消息的消耗并在本地路由消息(在单个JVM)。

何时选择Akka

  • 有很多消费者(想想数百万)。
  • 需要低延迟
  • 打开Actor并发模型

示例系统:交互式实时聊天系统

何时选择MQ

  • 需要与许多不同的系统集成(即非JVM)
  • 消息可靠性比延迟更重要
  • 想要更多工具和管理员用户界面
  • 由于以前的观点更适合长时间运行的任务
  • 想要使用与Actors
  • 不同的并发模型

示例系统:预定的事务处理批处理系统

根据相关评论进行编辑

我假设OP涉及Akka和消息队列都可以处理的分布式处理。那是我以为他在谈论distributed Akka使用Akka进行本地并发是与大多数消息队列的橙色比较。我之所以大多说是因为你可以在本地应用消息队列模型作为Reactor库和simple-react所做的并发模型(即主题,队列,交换)。

选择正确的并发模型/库对于低延迟应用程序非常重要。诸如消息队列之类的分布式处理解决方案通常并不理想,因为路由几乎总是通过线路完成,这显然比应用程序内部慢,因此Akka将是一个更好的选择。但是我相信一些专有的MQ技术允许本地路由。另外正如我前面提到的,大多数MQ客户端对于线程非常愚蠢,并且不依赖于非阻塞IO并且每个连接/队列/通道都有一个线程......具有讽刺意味的是,非阻塞io并不总是低延迟,但通常是更多的资源高效。

正如您所看到的,分布式编程和并发编程的主题相当大并且每天都在变化,所以我的初衷并不是混淆,而是专注于分布式消息处理的一个特定领域,这就是我所关注的OP。在并发性方面,人们可能希望将他们的搜索重点放在“反应性”编程(RFP /流)上,这是一个“更新”但与演员模型和消息队列模型类似的模型,所有这些模型通常可以组合在一起,因为它们是基于事件的。

答案 1 :(得分:4)

我不是消息系统方面的专家,但您可以在应用程序中将它们与Akka结合使用,从而实现两全​​其美。这是一个您可能会发现用于试验Akka和消息系统的示例,在本例中为ZeroMQ:

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

答案 2 :(得分:1)

Akka-Camel将是一个比ZeroMQ更好的例子 - ZeroMQ是直接tcp到tcp通信(因此为零 - 没有消息队列)。

使用AkkaCamel,您可以抽象出队列并直接从actor生成/使用消息,而无需任何代码来处理推送/拉取消息队列消息。

您可以放弃akka-zeromq并直接使用Akka进行远程处理。 我认为akka-zeromq正在从核心库中删除,但我们为akka构建了一个名为scala-zeromq的良好zeromq库(https://github.com/mDialog/scala-zeromq

Akka有几个关键的核心用例:

1)可变状态

通过将其隐藏在actor中来处理共享状态更容易。当actor同步处理消息时,您可以在actor中保持状态,并通过actor API

以高一致性公开该字段

2)发行

并发在akka中是免费的,所以你说它真正解决分发问题。跨机器和核心的分布。 Akka已经建立了位置透明度"通过网络发送消息。它还具有与扩展单个服务相关联的聚类和模式。这使其成为一个非常好的分发解决方案(例如微服务架构)

以下是Akka与ActiveMQ一起使用Akka-Camel(使用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);

    }
}