此版本仍在开发中,目前尚不稳定。如需最新稳定版本,请使用 Spring AMQP 4.0.2spring-doc.cadn.net.cn

AMQP 抽象

Spring AMQP 包含两个模块(每个模块在分发包中由一个 JAR 文件表示):spring-amqpspring-rabbit。'spring-amqp' 模块包含 org.springframework.amqp.core 包。在该包中,您可以找到代表核心 AMQP “模型”的类。我们的目标是提供通用抽象,这些抽象不依赖于任何特定的 AMQP 代理实现或客户端库。终端用户代码可更易于跨提供商实现进行移植,因为它只需针对抽象层进行开发即可。这些抽象随后由面向具体代理的模块实现,例如 'spring-rabbit'。目前仅提供 RabbitMQ 实现。然而,这些抽象已在 .NET 中通过 Apache Qpid 和 RabbitMQ 进行了验证。由于 AMQP 在协议层面运行,原则上您可使用 RabbitMQ 客户端与任何支持相同协议版本的代理配合使用,但目前我们并未对其他代理进行测试。spring-doc.cadn.net.cn

本概述假设您已经熟悉 AMQP 规范的基本知识。如果不熟悉,可参考 其他资源 中列出的相关资料。spring-doc.cadn.net.cn

Message

0-9-1 AMQP 规范并未定义 Message 类或接口。相反,当执行诸如 basicPublish() 的操作时,内容以字节数组参数的形式传递,而附加属性则作为独立参数传入。Spring AMQP 将 Message 类定义为更通用的 AMQP 领域模型表示的一部分。Message 类的目的是将主体和属性封装到单一实例中,从而使 API 更加简洁。以下示例展示了 Message 类的定义:spring-doc.cadn.net.cn

public class Message {

    private final MessageProperties messageProperties;

    private final byte[] body;

    public Message(byte[] body, MessageProperties messageProperties) {
        this.body = body;
        this.messageProperties = messageProperties;
    }

    public byte[] getBody() {
        return this.body;
    }

    public MessageProperties getMessageProperties() {
        return this.messageProperties;
    }
}

接口 MessageProperties 定义了若干常见属性,例如 'messageId'、'timestamp'、'contentType' 等更多属性。您还可以通过调用 setHeader(String key, Object value) 方法,以用户自定义的 'headers' 扩展这些属性。spring-doc.cadn.net.cn

从版本 1.5.71.6.111.7.42.0.0 开始,如果消息正文是一个序列化的 Serializable Java 对象,则在执行 toString() 操作(例如在日志消息中)时,将不再默认进行反序列化。此举旨在防止不安全的反序列化。默认情况下,仅对 java.utiljava.lang 类进行反序列化。若要恢复到之前的处理行为,可以通过调用 Message.addAllowedListPatterns(…​) 添加允许的类/包模式。支持一个简单的 * 通配符,例如 com.something.*, *.MyClass。无法反序列化的正文在日志消息中将以 byte[<size>] 表示。

交换

接口 Exchange 表示一个 AMQP 交换机(Exchange),这是消息生产者发送消息的目标。
每个代理(Broker)的虚拟主机内的每个交换机都具有唯一的名称以及若干其他属性。
以下示例展示了 Exchange 接口:spring-doc.cadn.net.cn

public interface Exchange extends Declarable {

    String getName();

    String getType();

    boolean isDurable();

    boolean isAutoDelete();

    Map<String, Object> getArguments();

}

如您所见,Exchange 也具有由 ExchangeTypes 中定义的常量表示的 'type'(类型)。
基本类型包括:directtopicfanoutheaders
在核心包中,您可以找到针对这些类型中的每一种的 Exchange 接口实现。
这些 Exchange 类型在如何处理与队列的绑定方面表现出不同的行为。
例如,Direct 交换机允许队列通过固定路由键(通常为队列名称)进行绑定。
Topic 交换机则支持使用路由模式进行绑定,该模式可包含 '*' 和 '#' 通配符,分别代表“恰好一个”和“零个或多个”。
Fanout 交换机会将消息发布到所有已绑定到它的队列,而无需考虑任何路由键。
有关这些以及其他交换机类型的更多信息,请参阅 AMQP 交换机spring-doc.cadn.net.cn

从版本 3.2 开始,在应用程序配置阶段引入了 ConsistentHashExchange 类型,以方便配置。它提供了诸如 x-consistent-hash 这样的交换类型选项,可用于配置 hash-headerhash-property 交换定义参数。相应的 RabbitMQ rabbitmq_consistent_hash_exchange 插件必须在代理(broker)上启用。关于一致性哈希交换(Consistent Hash Exchange)的目的、逻辑与行为的更多详情,请参阅官方 RabbitMQ 文档spring-doc.cadn.net.cn

AMQP 规范还要求任何代理必须提供一个名为“默认”的直连交换机(direct exchange),该交换机没有名称。所有声明的队列都会绑定到该默认 Exchange 交换机上,其绑定键(routing key)即为队列的名称。您可以在 AmqpTemplate 中了解 Spring AMQP 中默认交换机的使用方式。

队列

Queue 表示消息消费者从中接收消息的组件。与各种 Exchange 类似,我们的实现旨在作为此核心 AMQP 类型的抽象表示。以下列表显示了 Queue 类:spring-doc.cadn.net.cn

public class Queue  {

    private final String name;

    private final boolean durable;

    private final boolean exclusive;

    private final boolean autoDelete;

    /**
     * The queue is durable, non-exclusive and non auto-delete.
     *
     * @param name the name of the queue.
     */
    public Queue(String name) {
        this(name, true, false, false);
    }

    // Getters and Setters omitted for brevity

}

请注意,构造函数接受队列名称。spring-doc.cadn.net.cn

spring-doc.cadn.net.cn

根据具体实现,管理模板可能会提供用于生成唯一命名队列的方法。spring-doc.cadn.net.cn

此类队列在作为“回复地址”或在其他临时场景中非常有用。spring-doc.cadn.net.cn

因此,自动生成队列的‘exclusive’(独占)和‘autoDelete’(自动删除)属性都将被设置为‘true’。spring-doc.cadn.net.cn

spring-doc.cadn.net.cn

请参阅《配置代理》(Configuring the Broker)一节中关于队列的部分,以了解如何使用命名空间支持声明队列,包括队列参数。

绑定

鉴于生产者向交换机发送消息,而消费者从队列接收消息,将队列与交换机连接起来的绑定(bindings)对于通过消息传递实现生产者与消费者之间的连接至关重要。在 Spring AMQP 中,我们定义了一个 Binding 类来表示这些连接。本节将回顾将队列绑定到交换机的基本选项。spring-doc.cadn.net.cn

我们提供一个BindingBuilder,以便于实现“流畅API”风格,如下例所示:spring-doc.cadn.net.cn

Queue queue = ...;
// bind a queue to a DirectExchange with a fixed routing key
Binding directBinding = BindingBuilder.bind(queue).to(new DirectExchange("someDirectExchange")).with("foo.bar");
// bind a queue to a TopicExchange with a routing pattern
Binding topicBinding = BindingBuilder.bind(queue).to(new TopicExchange("someTopicExchange")).with("foo.*");
// bind a queue to a FanoutExchange with no routing key
Binding fanoutBinding = BindingBuilder.bind(queue).to(new FanoutExchange("someFanoutExchange"));
为了清晰起见,前面的示例展示了 BindingBuilder 类,但这种风格在使用静态导入 'bind()' 方法时效果很好。

单独来看,Binding 类的一个实例仅保存连接的相关数据。换句话说,它并不是一个“活跃”的组件。然而,正如您将在配置代理一节中看到的那样,AmqpAdmin 类可以利用Binding 实例在代理上实际触发绑定操作。此外,如同一节所示,您还可以通过在@Configuration 类中使用 Spring 的@Bean 注解来定义Binding 实例。此外,还提供了一个便捷的基类,进一步简化了生成与 AMQP 相关 Bean 定义的方法,并能识别队列、交换机和绑定关系,以便在应用程序启动时将它们全部在 AMQP 代理上声明。spring-doc.cadn.net.cn

代码 AmqpTemplate 也定义在核心包中。作为实际 AMQP 消息传递中的主要组件之一,它在其独立章节中有详细讨论(参见 AmqpTemplate)。spring-doc.cadn.net.cn