- AMQP抽象
Spring AMQP包含了很多模块,每一个模块在发布的时候都已一个Jar包的方式提供。这些模块包括:spring-amqp,spring-rabbit,spring-erlang。spring-amqp包含org.springframework.amqp.core包。在这个包里面你将发现代表着AMQP核心模型的类。我们的意图是提供高度的抽象,这个抽象不会依赖任何一个具体的AMQP代理实现或者客户端实现。每一个用户的代码将游离于不同的实现,因为它是针对抽象层开发的。这些抽象最终会被一些具体的消息代理实现为具体的模块,例如spring-rabbit。由于AMQP的操作是在协议层,所以理论上说,RabbitMQ可以被其他支持同样协议版本的代理使用,但是到目前为止没有进行测试。
- 消息
AMQP 0-8 0-9-1的规定没有定义Message类和接口。然而,当我们执行诸如‘
basicPublish’的操作,内容将作为字节数组进行传递,附加属性将使用额外的参数进行传递。Spring AMQP定义了消息类,作为更为广泛的AMQP领域模型中的一部分。消息类的目的是简单封装了内容与属性在一个实体中,从而API变得更简单。消息类的定义很直观。
public class Message implements Serializable {private static final long serialVersionUID = -7177590352110605597L;private static final String ENCODING = Charset.defaultCharset().name();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;}@Overridepublic String toString() {StringBuffer buffer = new StringBuffer();buffer.append("(");buffer.append("Body:'" + this.getBodyContentAsString() + "'");if (messageProperties != null) {buffer.append(messageProperties.toString());}buffer.append(")");return buffer.toString();}private String getBodyContentAsString() {if (body == null) {return null;}try {String contentType = (messageProperties != null) ? messageProperties.getContentType() : null;if (MessageProperties.CONTENT_TYPE_SERIALIZED_OBJECT.equals(contentType)) {return SerializationUtils.deserialize(body).toString();}if (MessageProperties.CONTENT_TYPE_TEXT_PLAIN.equals(contentType)|| MessageProperties.CONTENT_TYPE_JSON.equals(contentType)|| MessageProperties.CONTENT_TYPE_JSON_ALT.equals(contentType)|| MessageProperties.CONTENT_TYPE_XML.equals(contentType)) {return new String(body, ENCODING);}}catch (Exception e) {// ignore}// Comes out as '[B@....b' (so harmless)return body.toString()+"(byte["+body.length+"])";}@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + Arrays.hashCode(body);result = prime * result + ((messageProperties == null) ? 0 : messageProperties.hashCode());return result;}@Overridepublic boolean equals(Object obj) {if (this == obj) {return true;}if (obj == null) {return false;}if (getClass() != obj.getClass()) {return false;}Message other = (Message) obj;if (!Arrays.equals(body, other.body)) {return false;}if (messageProperties == null) {if (other.messageProperties != null) {return false;}}else if (!messageProperties.equals(other.messageProperties)) {return false;}return true;}
MessageProperties 类中定义了一些公共的属性,例如‘messageId’,‘timestamp’,‘contentType’,这些属性同样可以通过调用方法setHeader(String
key, Object value) 进行扩展。- 交换
Exchange 接口代表一个AMQP
Exchange,它是消息生产者消息所发送到的地方,一个消息代理中的虚拟主中的每一个Exchange的名称必须唯一,它还有其他一些属性。
public interface Exchange {String getName();String getExchangeType();boolean isDurable();boolean isAutoDelete();Map<String, Object> getArguments();}
可以看到,Exchange有一个类型'type',这个类型在ExchangeTypes中定义。基本的类型包括:Direct,Topic,Fanout,和Headers。在核心包中你会发现Exchange接口针对每一种类型的实现。这些不同类型的Exchange因为它们处理队列绑定的方式不一样而不同。例如,Direct Exchange允许通过固定的路由键来和队列进行绑定。Topic Exchange支持通过'*' '#'通配符分别代表一个或者零个或者多个进行绑定。Fanout Exchange将不考虑路由键,将消息发送到绑定的所有队列中。
AMQP规范要求任何一个消息代理必须提供一个’default‘ Direct Exchange,这个Exchange没有名称。所有声明的队列都和这个Exchange绑定,使用它们的名称作为路由键。
- 队列
队列类存储着消息的组件,消息消费者从队列中接收到消息。
public class Queue {private final String name;private volatile boolean durable;private volatile boolean exclusive;private volatile boolean autoDelete;private volatile Map<String, Object> arguments;/*** 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
注意构造函数接收队列的名称。取决于具体的实现,admin模板提供了方法来产生唯一名称的队列。像这类的队列可以作为’reply-to'地址或者一些临时的场景。由于这些原因,自动产生的队列的‘exclusive’和‘autoDelete’属性通常被设置为true。
- 绑定
我们知道生产者发送消息给到一个Exchange,消费者从队列中取到消息,队列和Exchange之间的绑定至关重要,它们通过消息来连接着生产者和消费者。在Spring AMQP中我们使用Binding这个类来代表这层连接关系。下面我们来看看Queue和Exchange之间的绑定:
Queue和DirectExchange之间通过国定的路由键进行绑定:
new Binding(someQueue, someDirectExchange, "foo.bar")
Queue和TopicExchange之间通过通配路由键进行绑定:
new Binding(someQueue, someTopicExchange, "foo.*")
Queue和FanoutExchange之间的绑定不需要路由键:
new Binding(someQueue, someFanoutExchange)
我们同样提供了
BindingBuilder 使用流式API风格来简化绑定:
Binding b = BindingBuilder.bind(someQueue).to(someTopicExchange).with("foo.*");
Binding实例自身仅仅持有关于连接的数据。换句话说,它不是一个活跃的组件。然而你在后面将会看到,Binding实例被AmqpAdmin类用来出发消息代理的行为。你同样可以看到Binding实例可以通过注解在@Configuration的类中使用@Bean进行定义。
还存在一些基础的类,这些类进一步简化了AMQP相关的Queue,Exchange,Binding的创建。AmqpTemplate这个类同样在核心包中定义,它将在后面章节中讨论。
本文深入解析SpringAMQP中的AMQP抽象概念,详细介绍了消息、交换、队列及绑定的相关实现。重点讨论了如何在SpringAMQP中封装AMQP领域模型,以及如何利用消息、交换、队列和绑定进行消息通信。
2297

被折叠的 条评论
为什么被折叠?



