何时使用参与者代替诸如WebSphere MQ或Tibco Rendezvous之类的消息传递解决方案?


106

我已经阅读了以下问题和答案:哪些设计决策会偏爱Scala的Actors而不是JMS?

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

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

在迄今已成功使用上述产品(WebSphere MQ或ActiveMQ)的某些用例中,何时以及为何使用Akka?为什么在我的未来项目中应该考虑使用Akka代替WebSphere MQ或Tibco RV?

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

也许除了JMS(点对点),TibcoRV(多播)和Akka之外,我还应该考虑JVM环境中的另一种消息传递解决方案?


Answers:


92

首先,“较旧的”消息系统(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的远程处理是相当新的,可能仍未提供传统消息队列提供的所有可靠消息保证和QoS(但每天都在变化)。它通常也是点对点的,但是我认为它支持服务器对点,这通常是大多数MQ系统所做的(即单点故障),但是有些MQ系统是点对点的(RabbitMQ是服务器对等的)。对等)。

最后,RabbitMQ和Akka实际上组成了很好的一对。您可以将Akka用作RabbitMQ的包装器,特别是因为RabbitMQ不能帮助您处理消息的使用和本地路由消息(在单个JVM中)。

何时选择Akka

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

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

何时选择MQ

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

系统示例:计划的事务批处理系统

根据相关评论进行编辑

我假设OP与Akka和Message Queues都可以处理的分布式处理有关。那就是我以为他在谈论分布式Akka与大多数消息队列相比,使用Akka进行本地并发是一个不二之选。我之所以这么说是因为您可以在本地将消息队列模型作为并发模型(即,主题,队列,交换)应用,而Reactor库和简单反应都可以这样做。

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

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


3
我认为对错误问题的答案不可能是正确的。您无法比较消息队列和并发模型。它们是为解决完全不同的任务而构建的,并且仅具有“消息”一词。
伊戈尔·S。

2
是的,不是。Akka支持分布式消息传递,您可以很容易地基于消息队列范例(Google Spring的Reactor)构建并发模型。现在真正的唯一区别是RabbitMQ具有持久的消息。等等Akka现在也支持它。他可能会在标题中说“演员”,但明确指出Akka与许多基于消息的系统(并发和分布式)确实有很多重叠。
亚当·根特

4
顺便说一句@IgorS。与消息队列一起使用的典型并发模型称为SEDA(分段事件驱动的体系结构)。除了使用队列,主题和交换之外,它本身就是一个并发模型(就像参与者模型一样,它也只是分布式模型。)当有人说“错误的问题”时,我也非常鄙视。说这样的话很狡猾和高贵。
亚当·根特

1
我从未说过它们是可互换的。我什至说他们在一起很好,为什么。但是他显然是在这里谈论分布式的akka​​,而不是演员库的akka​​。这就是我的阅读方式。只要您的观点是正确的,就可以随意编辑我的帖子,并且可能会使其他迷迷糊糊的人迷惑。
亚当·根特2015年

1
一种Akka Java API-现在非常干净,尤其是对于JDK 8 lambda。我怀疑它会得到更好的,如果/当他们推出的值对象JDK 10
罗布·克劳福德

4

我不是邮件系统方面的专家,但是您可以在应用程序中将它们与Akka结合使用,从而充分利用两者的优势。这是一个示例,可能对尝试使用Akka和消息传递系统(在本例中为ZeroMQ)有用:

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


6
ZeroMQ并不完全是一个消息传递系统。它是某种改进的套接字。完整的消息传递系统比ZeroMQ复杂得多。您所链接的项目似乎只是带有Akka的ZeroMQ的一个简单包装。
弗拉基米尔·马特维耶夫(Fladimir Matveev)

1

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

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

您可以放弃akka-zeromq并将Akka直接用于远程处理。我认为akka-zeromq已从核心库中删除,但是我们为akka建立了一个很好的zeromq库,名为scala-zeromq(https://github.com/mDialog/scala-zeromq

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

1)可变状态

通过将共享状态隐藏在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);

    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.