JMS接收是如何在内部工作的?

37

我一直在研究各种通信技术/架构/模式/实现(也就是所谓的流行语),包括Web Services(WCF,Axis2),ESB,SOA,并且想更多地了解有关消息传递方面的JMS。

从概念上讲,JMS听起来很简单。我的理解是它是一个中间代理程序,用于管理发布者的消息并将其路由到适当的订阅者。这是通过排队发布的消息并在接收时出列来完成的。

问题1:我对JMS的基本理解正确吗?

在阅读有关技术的文章时,其中某些特性可能存在一定程度的(故意或无意的)夸大其词,这是令人困扰的事情之一。

根据我对JMS的基本理解,必须运行JMS提供程序才能发送或接收消息。我对发布的假设是,JMS提供程序只是等待消息被发布,然后将其存储在队列中(内存或数据库支持,具体取决于实现方式)。但是,我不太确定如何接收。

问题2:如果没有可用的消息,接收(通常)是否会阻塞?

问题2b:如果是,如何实现阻塞?客户端是否不断轮询消息?服务器是否仅在发布消息时才会响应(这样做如何避免超时问题?)?提供程序是否会发起对接收方的调用?

问题2c:如果不是,如何确保及时接收消息,而不影响性能?

基本描述似乎倾向于单个JMS提供程序,以确保消息得到集中管理而不会丢失。我可以看到缩放是一个问题。

问题3:JMS如何进行缩放?

在缩放时,我可以看到确保将单个消息传递给所有适当的订阅者的复杂性,无论哪个物理服务器接收该消息。

问题3b:JMS实现如何在缩放环境中确保可靠传递?

请注意,尽管这些问题与JMS相关,但它们可能适用于任何消息基础结构。我欢迎特定于JMS的答案,也欢迎更一般或甚至特定于其他技术的答案。


1
如果你想深入研究,HornetQ是开源的,并提供JMS实现。获取源代码的说明在此处(http://community.jboss.org/wiki/HornetQSourceLocation)。您可能还想阅读文档中的架构和概念部分(http://docs.jboss.org/hornetq/2.2.2.Final/user-manual/en/html_single/index.html),这可以帮助您缩小问题的范围。 - Matt
5个回答

21
我将根据我的JMS经验回答几个问题。 回答1: JMS是Java Message Service API,它为Java客户端提供统一的接口来访问消息框架。在JMS API的下面是符合JMS标准的消息提供者,例如WebSphere MQ提供者。JMS支持通过任何消息协议传输有效负载到目的地,即队列和主题。这些是JMS的基础知识。
接收消息的工作原理是什么?JMS规范提供了两个重要的类:MessageConsumer和MessageListener。MessageConsumer类允许JMS客户端通过调用其receive()方法之一同步接收JMS消息。此调用将阻塞线程直到收到消息。否则,可以通过向MessageConsumer注册MessageListener对象来进行异步接收。JMS提供者会知道消息已经到达其本地目的地,它的工作是将消息传递给轮询消息消费者线程或非轮询注册的消息侦听器线程。 回答2: MessageConsumer API有两个receive的变体:receive()和receive(long timeout)。后者允许MessageConsumer线程在特定超时期间内阻塞,直到消息到达或超时。
不同的消息框架可能以不同的方式实现阻止功能。由于JMS对象是JNDI管理的对象,并返回特定于提供程序的代理对象给JMS客户端,这意味着客户端不知道后台如何进行阻止。特定的消息框架可以选择在一定时间段后轮询消息消费者线程。或者,它可能选择阻塞直到发送通知。
我不确定您是否正在寻找特定的JMS兼容消息框架的答案? 回答3:我猜您所说的 JMS 扩展性指的是可以在多个物理机器上拥有多个发布者/订阅者和目的地。JMS 扩展性需要底层消息提供者支持某种形式的集群/故障转移。因此,JMS 规范不支持可扩展性。如果我理解有误,请纠正我。例如,我曾经使用符合 JMS 规范的 WebSphere MQ,该产品提供了集群支持。


我意识到JMS只是一个规范,所以我基本上把你的回答理解为“它取决于实现”(这完全可以接受)。我希望能够获得有关与轮询/等待相关的阻塞方面的更多技术细节。一个更一般的问题可能是,在没有负面性能影响的情况下,异步消息架构如何扩展(假设所有订阅者都在轮询、打开连接和阻塞或等待广播)? - Travis

5
问题1:我对JMS的基本理解正确吗?
首先让我们把术语搞清楚。你不能说“JMS提供者必须运行”,因为提供者是一个构建JMS服务器的实体,必须运行的是JMS服务器。因此,当我们说JMS时,我们指的是一组API(更准确地说是接口),这些API由提供者实现。因此,提供者基本上编写自己的JMS实现。例如,“Active MQ是由Apache(提供者)提供的JMS服务器”。
我的假设是发布消息时,JMS提供者只需等待消息发布,然后将其存储在队列中(内存或数据库支持,取决于实现)。
在某种程度上是正确的。有不同的模型可以遵循。JMS服务器保持一个套接字打开。每当发送方客户端必须发送消息时,它只需打开到套接字的连接并发送消息。接收方如何行动完全不同。你有拉和推。在推模型中,服务器将消息推送到实时接收方客户端,只要它收到消息即可。这也称为异步模式。在拉模型中,客户端接收器向服务器发送请求以获取消息(同步模式)。
如果没有可用的消息,receive(通常)会阻塞吗?
如我在前面提到的,这将取决于您使用的模型。在拉模型中,接收器将被阻塞(同步接收)。此外,这发生在会话线程而不是主线程中。
如果是这样,如何实现阻止?客户端是否不断轮询以获取消息?
是的,在拉模型中,客户端将不断轮询。一般情况下,会有一个超时时间,超时后客户端将被终止。
如果不是这样,如何确保及时接收消息,而不影响性能?
使用异步模式。只需注册一个MessageListener,当服务器上有可用消息时,它将接收消息的重写onMessage(Message msg)。
问题3:JMS如何扩展?
这真的是提供者需要担心的问题。当你说一个消息被所有订阅者接收时,你正在引用通信的PUBSUB模型(另一个是PTP)。在PUBSUB中,发送到主题的消息将传递给订阅该主题的所有订阅者。
问题3b:JMS实现如何确保在扩展环境中可靠地传递?

可靠性?并非总是如此。这取决于用例。您可以拥有持久非持久消息。对于持久性消息,消息存储在数据库(文件或其他)中,并确保传递。对于非持久性消息,没有这样的保证。服务器故障可能导致消息丢失。


你的答案基本上只是延续了我一般所遇到的问题(即,手摆手)。有一个接收消息的监听器描述了消息的/发生方式/,但并没有解释如何以高性能的方式异步传递消息。你提到使用拉模型会阻塞,但是异步提供程序是如何实现的呢?是的,可扩展性和可靠性显然是提供程序必须考虑的问题。但是它们通常如何工作? - Travis
1
在异步模型中,接收器将注册一个MessageListener。这是在session线程中完成的。注册后,Session线程可以继续进行处理。当队列或主题中有消息可用时,消息将被推送到客户端。客户端库将在SessionThread中调用MessageListener的回调函数onMessage(),并传递接收到的消息,您可以在其中编写消息处理逻辑。 - Aniket Thakur
2
我仍在寻找更多细节 - 例如,考虑您的客户端位于一个服务器上,您的JMS提供程序是一个扩展系统(即,不止一个服务器)。从网络角度来看,客户端连接到什么地方,响应如何在相反的方向上交付。我认为您试图说的是服务器会发起回到客户端的连接以传递消息。但是,如果客户端离线会发生什么?再次强调我关心可靠性(保证交付,持久化消息)和性能(交付时间)。 - Travis

2

JMS中有两种消息传递域。

  1. Point-To-Point(点对点,PTP)消息传递域
  2. 发布者/订阅者消息传递域

在 PTP 模型中,一条消息只被一个接收器接收。这里,队列被用作消息导向中间件MOM)。

队列负责保存消息直到接收器准备好接收。

在 PTP 模型中,发送者和接收者之间没有时间依赖关系。

enter image description here


在 Pub/Sub 模型中,一条消息被所有订阅者接收。就像广播一样。这里,主题被用作消息导向中间件,负责保存和传送消息。

在 Pub/Sub 模型中,发布者和订阅者之间存在时间依赖关系。

enter image description here


JMS 编程模型

enter image description here

来源


消息驱动 Bean(MDB)

  • MDB 是包含业务逻辑的 Bean。但是,它通过传递消息来调用。因此,它就像 JMS 接收器。
  • MDB 异步接收并处理消息。
  • MDB 从队列或主题接收消息。
  • MDB 就像无状态会话 Bean,封装了业务逻辑,并不维护 Bean 的状态。

enter image description here


2
我认为应该提到队列和主题之间的差异,因为消息传递方式存在重要差异。
队列:只有一个客户端会收到消息。为了扩展,您可以例如有10个客户端都连接到同一个队列,但只有其中一个客户端会收到特定的消息。如果没有客户端连接,消息将保留在队列中,直到有人连接或消息超时。
主题:所有客户端都将接收每个消息的副本。通常用于订阅者场景,在这种场景中,许多端点可能对每个消息感兴趣。持久订阅者甚至可以暂停一段时间;消息将保留,直到订阅者再次上线或消息超时。如果没有客户端连接且没有持久订阅者,则消息将被丢弃。

2
JMS支持使用同步方法(带或不带超时阻塞线程的接收)或事件驱动回调(异步消息监听器)来消费消息。
您可以决定哪种方法更适合您的需求,但您可能需要查看实际实现。例如,一些JMS实现对receive()进行网络往返,因此最好使用超时或监听器。
使用消息监听器时,线程行为和暂停消息接收不像使用阻塞接收调用那样容易控制。通常,大多数控制是通过拥有自己的具有超时的阻塞接收(调度到您的工作线程池)来实现的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接