Green Beans:企业消息传递和 Spring 入门
在本文中,我们将介绍消息传递的核心概念,以及 Spring 框架及其姐妹项目提供的丰富消息传递支持。
什么是消息传递?为了最好地解释这一点,我将转述 Gregor Hohpe 和 Bobby Woolf 的开创性著作《企业集成模式》(Addison Wesley, 2004)中提供的示例。当你打电话时,你会尝试将信息传达给另一方。只有当对方在你打电话时能够接听,这才会起作用。由于并不总能接到电话,因此我们使用语音邮箱来排队消息。呼叫者将消息留在语音邮箱中,然后被呼叫者可以稍后异步地检索消息(或实际上,许多消息)。
在这个例子中,语音邮箱位于双方之间。它存储消息,然后在被呼叫者(接收者)检索时将其传递。在企业消息传递领域,情况非常相似:一方将消息发送到消息代理(也称为消息导向中间件 - MOM),而另一方——当该方能够——在稍后接收或显式查询消息代理中的任何消息。
这正是类比停止有用的地方。与语音邮箱相比,消息代理具有许多选项。消息代理非常适合提供附加服务,例如路由,并保证消息传递。消息代理可以针对不同的用例进行优化,例如,你可以权衡速度与持久性。消息代理可能会将消息持久化到外部存储以确保持久性,尽管这通常是可以通过配置来切换以提高速度的选项。
在语音邮箱示例中,消息由一方发送,然后传递给另一方——通信是*点对点*的。消息代理支持这一点,以及另一种称为*发布/订阅*的通信类型,其中消息会传递给多个客户端。
消息代理的一个常见用途是解决两个不同系统之间的集成问题。发送到消息代理的数据通常是发送方和接收方都通用的格式。两个系统在使用消息代理时需要就数据合同达成一致。消息通常包含消息正文,其中存储了消息本身的内容,以及消息头,它们是键/值对,提供了有关消息正文的元数据,可用于帮助消息的消费者处理消息。消息头可以是任何你想要的内容,但它们通常与消息本身或消息的处理器相关。
Java Message Service
Java 消息服务 (JMS) API 规定了与消息代理交互的客户端接口。每个消息代理都提供自己的 API 实现,非常类似于 JDBC 驱动程序对 JDBC API 的作用。这意味着 JMS 客户端通常应该使用与服务器相同版本的客户端。有许多优秀的 JMS 代理实现可供选择。其中一个原因是消息传递一直是应用程序开发的重要组成部分,并且今天仍然如此。自 1.1 版本以来,JMS 一直是 J2EE(现为 Java EE)规范的一部分。在过去十年的大部分时间里,JMS 规范都停留在 1.1 版本。
在 JMS 中,客户端使用 javax.jms.ConnectionFactory 创建 javax.jms.Connection。然后可以使用 Connection 创建 javax.jms.Session。Session 代表客户端与代理的交互,并允许发送和接收消息以及其他不太明显的。操作。
该接口上最有用的方法与创建 javax.jms.Destination 的消息生产者和消息消费者有关。Destination 映射了消息代理上“地址”的 JMS 概念。它还映射了代理存储消息的位置。在 JMS 中,消息从同一位置发送、存储和消费,所有这些都由 javax.jms.Destination 实例表示。
[/caption] Destination 是一个接口,有两个更具体的子接口 javax.jms.Queue 和 javax.jms.Topic。Queue 代表标准的队列,这是之前描述的点对点构造。Topic 提供发布/订阅消息传递,并将一条消息传递给多个接收者。
要将消息发送到 Destination,您必须创建一个 javax.jms.MessageProducer。然后可以使用 MessageProducer 发送 javax.jms.Message。
JMS 支持两种不同的接收消息的机制。第一种方式是使用 javax.jmx.MessageConsumer#receive() 方法来请求消息,该方法以*同步*方式返回 Destination 中的单个消息;默认情况下,该方法会阻塞直到收到消息。而不是使用 MessageConsumer,客户端可以通过调用 javax.jms.Session#setMessageListener(MessageListener) 来安装 javax.jms.MessageListener。MessageListener 是一个接口,只有一个方法 public void onMessage(javax.jms.Message),每当 Destination 上有可供消费的 javax.jms.Message 时,就会调用该方法。MessageListener 提供*异步*消息处理:当消息到达时,它们会被处理。
JMS API 中还有更多内容需要学习,但这些类和概念将在我们讨论 Spring 对 JMS 消息传递的支持时对您最有帮助。第一层支持是 org.springframework.jms.core.JmsTemplate,它提供了简化的方法,将我们刚刚讨论的内容减少到一行代码。JmsTemplate 需要一个 javax.jms.ConnectionFactory 实例来完成其工作。JmsTemplate 可以为您完成大量工作。例如,要发送消息,JmsTemplate 会建立一个 javax.jms.Session,设置一个 javax.jms.MessageConsumer 或 javax.jms.MessageProducer,设置所有事务的机制,并为您提供对当前 javax.jms.Session 的引用,以便您可以创建您选择的消息并发送它。考虑到所有错误处理和构建逻辑,这很容易节省数十行代码。一旦您的消息已发送,它就会销毁或关闭其中大部分对象。这是应用程序服务器(如 Java EE 服务器)中的标准做法,因为 ConnectionFactory 实例由服务器创建、由服务器管理并进行池化。它们在使用后缓存实例。在这些环境中,关闭资源只是将它们返回到池中。因此,JmsTemplate 在标准情况下会做正确的事情,假设 ConnectionFactory 缓存或池化实例。
在应用程序服务器等托管环境中,您通常需要从 JNDI 获取 javax.jms.ConnectionFactory。您可以使用 Spring 来为您查找该引用并配置 JmsTemplate。在我们的示例中,我们希望更宽松地操作,因此我们将使用独立的 ActiveMQ 消息代理。ActiveMQ 是一个流行的、开源消息代理。要使用它,请下载它,然后在 bin 文件夹中运行适合您操作系统的启动脚本。在您的应用程序中,您需要客户端库来连接到相应版本的 ActiveMQ。在撰写本文时,ActiveMQ 的最新版本是 5.4.2。如果您使用 Maven,请将以下依赖项添加到您的 Maven pom 文件中
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>${activemq.version}</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-optional</artifactId>
<version>${activemq.version}</version>
</dependency>
请确保为 ${activemq.version} 创建一个 Maven 属性,或者手动将字符串替换为相应的版本。还有一个 activemq-all 依赖项,但它会拉下许多可能不必要的 jar。对于我们的应用程序,上述两个依赖项就足够了。
将 Spring 与 JMS 结合使用
让我们检查一个基本 JMS 应用程序的配置。首先,让我们检查基本的 Spring XML 配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/200…