RabbitMQ的Java應用(2) -- 使用Spring AMQP開發消費者應用__RabbitMQ

來源:互聯網
上載者:User

前一篇中我們介紹了使用RabbitMQ Java Client訪問RabbitMQ的方法。但是使用這種方式訪問RabbitMQ,開發人員在程式中需要自己管理Connection,Channel對象,Consumer對象的建立,銷毀,這樣會非常不方便。我們下面介紹使用Spring AMQP串連RabbitMQ,進行訊息的接收和發送。

Spring AMQP是一個Spring子項目,它提供了訪問基於AMQP協議的Message Service器的解決方案。它包含兩部分,spring-ampq是基於AMQP協議的訊息發送和接收的高層實現,spring-rabbit是基於RabbitMQ的具體實現。這兩部分我們下面都會使用到。

Spring-AMQP中的基礎類/介面 spring-amqp中定義了幾個基礎類/介面,Message,Exchange,Queue,Binding

Message

public class Message implements Serializable {  private final MessageProperties messageProperties;   private final byte[] body;

spring-amqp中的Message類類似於javax的Message類,封裝了訊息的Properties和訊息體。

Exchange spring-amqp定義了Exchange介面

 
public interface Exchange extends Declarable {        //Exchange名稱String getName();        //Exchange的類型String getType();        //Exchange是否持久化boolean isDurable();        //Exchange不再被使用時(沒有任何綁定的情況下),是否由RabbitMQ自動刪除boolean isAutoDelete();        //Exchange相關的參數Map<String, Object> getArguments();


這個介面和RabbitMQ Client中的Exchange類相似。 spring-amqp中的Exchange繼承關係如下圖所示


AbstractExchange類是所有Exchange類的父類,實現Exchange介面的具體方法。 CustomExchange針對使用者自訂的Exchange對象。其他四個Exchange類,分別對應四種Exchange。 我們在Spring設定檔中配置Exchange對象時,使用的就是這幾種Exchange類。

Queue spring-amqp定義了Queue類,和RabbitMQ Client中的Queue相似,對應RabbitMQ中的訊息佇列。

public class Queue extends AbstractDeclarable { private final String name; private final boolean durable; private final boolean exclusive; private final boolean autoDelete; private final java.util.Map<java.lang.String, java.lang.Object> arguments;         public Queue(String name, boolean durable, boolean exclusive, boolean autoDelete) {this(name, durable, exclusive, autoDelete, null);} 

Binding Binding類是對RabbitMQ中Exchange-Exchange以及Exchange-Queue綁定關係的抽象。

public class Binding extends AbstractDeclarable { public enum DestinationType {QUEUE, EXCHANGE;} private final String destination; private final String exchange; private final String routingKey; private final Map<String, Object> arguments; private final DestinationType destinationType; public Binding(String destination, DestinationType destinationType, String exchange, String routingKey,Map<String, Object> arguments) {this.destination = destination;this.destinationType = destinationType;this.exchange = exchange;this.routingKey = routingKey;this.arguments = arguments;}

對照RabbitMQ Java Client中Channel介面的queueBind和ExchangeBind方法

Exchange.BindOk exchangeBind(String destination, String source, String routingKey, Map<String, Object> arguments)  Queue.BindOk queueBind(String queue, String exchange, String routingKey, Map<String, Object> arguments)

我們可以看出Binding類實際是對底層建立的Exchange-Queue和Exchange-Exchange綁定關係的高層抽象記錄類,它使用枚舉類型DestinationType區分Exchange-Queue和Exchange-Exchange兩種綁定。

Spring AMQP搭建消費者應用 消費者應用程式架構搭建 我們接下來使用spring-amqp搭建一個RabbitMQ的消費者Web應用,我們先建立一個maven webapp應用程式,再添加一個dependency。

<dependency>    <groupId>org.springframework.amqp</groupId>    <artifactId>spring-rabbit</artifactId>    <version>1.6.5.RELEASE</version> </dependency> 

spring-rabbit庫的引入是為了使用它裡面的RabbitAdmin類,建立Exchange,Queue和Binding對象,在匯入這個庫的時候同時引入了 spring-ampq和rabbitmq-client的庫,不需要另行匯入。

在src/main/resources目錄下建立application.properties檔案,用於記錄RabbitMQ的配置資訊。

mq.ip=localhostmq.port=5672mq.userName=rabbitmq_consumermq.password=123456mq.virutalHost=test_vhosts
在src/main/resource目錄下建立applicationContext.xml檔案:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns:context="http://www.springframework.org/schema/context"       xmlns:util="http://www.springframework.org/schema/util"       xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd    http://www.springframework.org/schema/context    http://www.springframework.org/schema/context/spring-context-4.0.xsd" >     <context:annotation-config/>     <context:property-placeholder            ignore-unresolvable="true" location="classpath*:/application.properties" />     <!--從RabbitMQ Java Client建立RabbitMQ串連工廠對象-->    <bean id="rabbitMQConnectionFactory" class="com.rabbitmq.client.ConnectionFactory">        <property name="username" value="${mq.userName}" />        <property name="password" value="${mq.password}" />        <property name="host" value="${mq.ip}" />        <property name="port" value="${mq.port}" />        <property name="virtualHost" value="${mq.virutalHost}" />    </bean>     <!--基於RabbitMQ串連工廠對象構建spring-rabbit的串連工廠對象Wrapper-->    <bean id="connectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">     <constructor-arg name="rabbitConnectionFactory" ref="rabbitMQConnectionFactory" />    </bean>     <!--構建RabbitAmdin對象,它負責建立Queue/Exchange/Bind對象-->    <bean id="rabbitAdmin" class="org.springframework.amqp.rabbit.core.RabbitAdmin">        <constructor-arg name="connectionFactory" ref="connectionFactory" />        <property name="autoStartup" value="true"></property>    </bean>     <!--構建Rabbit Template對象,用於發送RabbitMQ訊息,本程式使用它發送返回訊息-->    <bean id="rabbitTemplate" class="org.springframework.amqp.rabbit.core.RabbitTemplate">        <constructor-arg name="connectionFactory" ref="connectionFactory" />    </bean>     <!--RabbitMQ訊息轉化器,用於將RabbitMQ訊息轉換為AMQP訊息,我們這裡使用基本的Message Converter -->    <bean id="serializerMessageConverter"          class="org.springframework.amqp.support.converter.SimpleMessageConverter" />     <!--Message Properties轉換器,用於在spring-amqp Message對象中的Message Properties和RabbitMQ的     Message Properties對象之間互相轉換 -->          <bean id="messagePropertiesConverter"          class="org.springframework.amqp.rabbit.support.DefaultMessagePropertiesConverter" />           <!--定義AMQP Queue-->    <bean id="springMessageQueue" class="org.springframework.amqp.core.Queue">        <constructor-arg name="name" value="springMessageQueue" />        <constructor-arg name="autoDelete" value="false" />        <constructor-arg name="durable" value="true" />        <constructor-arg name="exclusive" value="false" />        <!--定義AMQP Queue建立所需的RabbitAdmin對象-->        <property name="adminsThatShouldDeclare" ref="rabbitAdmin" />        <!--判斷是否需要在串連RabbitMQ後建立Queue-->        <property name="shouldDeclare" value="true" />    </bean>     <!--定義AMQP Exchange-->    <bean id="springMessageExchange" class="org.springframework.amqp.core.DirectExchange">        <constructor-arg name="name" value="springMessageExchange" />        <constructor-arg name="durable" value="true" />        <constructor-arg name="autoDelete" value="false" />        <!--定義AMQP Queue建立所需的RabbitAdmin對象-->        <property name="adminsThatShouldDeclare" ref="rabbitAdmin" />        <!--判斷是否需要在串連RabbitMQ後建立Exchange-->        <property name="shouldDeclare" value="true" />    </bean>     <util:map id="emptyMap" map-class="java.util.HashMap" />     <!--建立Exchange和Queue之間的Bind-->    <bean id="springMessageBind" class="org.springframework.amqp.core.Binding">        <constructor-arg name="destination" value="springMessageQueue" />        <constructor-arg name="destinationType" value="QUEUE" />        <constructor-arg name="exchange" value="springMessageExchange" />        <constructor-arg name="routingKey" value="springMessage" />        <constructor-arg name="arguments" ref="emptyMap" />    </bean>     <!--偵聽springMessageQueue隊列訊息的Message Listener-->    <bean id="consumerListener"     class="com.qf.rabbitmq.listener.RabbitMQConsumer" />     <!--建立偵聽springMessageQueue隊列的Message Listener Container-->    <bean id="messageListenerContainer"          class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">        <property name="messageConverter" ref="serializerMessageConverter" />        <property name="connectionFactory" ref="connectionFactory" />        <property name="messageListener" ref="consumerListener" />        <property name="queues" ref="springMessageQueue" />        <!--設定訊息確認方式為自動確認-->        <property name="acknowledgeMode" value="AUTO" />    </bean></beans>
我們定義了偵聽訊息佇列的Message Listener類RabbitMQConsumer

public class RabbitMQConsumer implements MessageListener{    @Autowired    private MessagePropertiesConverter messagePropertiesConverter;     @Override    public void onMessage(Message message)    {        try         {             //spring-amqp Message對象中的Message Properties屬性             MessageProperties messageProperties = message.getMessageProperties();                          //使用Message Converter將spring-amqp Message對象中的Message Properties屬性             //轉換為RabbitMQ 的Message Properties對象             AMQP.BasicProperties rabbitMQProperties =             messagePropertiesConverter.fromMessageProperties(messageProperties, "UTF-8");                          System.out.println("The message's correlationId is:" + rabbitMQProperties.getCorrelationId());             String messageContent = null;             messageContent = new String(message.getBody(),"UTF-8");             System.out.println("The message content is:" + messageContent);        }         catch (UnsupportedEncodingException e) {            e.printStackTrace();        }    }}
上面的Listener類是實現了MessageListener介面的類,當容器接收到訊息後,會自動觸發onMessage方法。 如果我們想使用普通的POJO類作為Message Listener,需要引入org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter類

public class MessageListenerAdapter extends AbstractAdaptableMessageListener {   public MessageListenerAdapter(Object delegate) {doSetDelegate(delegate);}}
這裡的delegate對象就是我們的POJO對象。 假設我們定義一個Delegate類ConsumerDelegate

public class ConsumerDelegate{    public void processMessage(Object message)    {       //這裡接收的訊息對象僅是訊息體,不包含MessageProperties       //如果想擷取帶MessageProperties的訊息對象,需要在Adpater中       //定義MessageConverter屬性。       String messageContent = message.toString();       System.out.println(messageContent);    }}
在applicationContext.xml中定義Adapter對象,引用我們的Delegate對象。

 <bean id="consumerDelegate"          class="com.qf.rabbitmq.listener.ConsumerDelegate" />  <bean id="consumerListenerAdapter"          class="org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter">        <property name="delegate" ref="consumerDelegate" />        <!--指定delegate處理訊息的預設方法 -->        <property name="defaultListenerMethod" value="processMessage" /> </bean>
最後將Message Listener Container中的Message Listener指向Adapter對象。

 
<bean id="messageListenerContainer"          class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">        <property name="messageConverter" ref="serializerMessageConverter" />        <property name="connectionFactory" ref="connectionFactory" />        <!--設定Message Listener為Adapter對象 -->        <property name="messageListener" ref="consumerListenerAdapter"/>        <property name="queues" ref="springMessageQueue" />        <property name="acknowledgeMode" value="AUTO" /> </bean>
啟動Web應用後,我們從開機記錄資訊可以看出應用串連上了RabbitMQ伺服器



從RabbitMQ的管理介面(用rabbitmq_consumer使用者登入)可以看到springMessageExchange和springMessageQueue已經建立,綁定關係也已經建立。







Consumer Tag自訂 串連springMessageQueue的消費者Tag是RabbitMQ隨機產生的Tag名


如果我們想設定消費者Tag為指定Tag,我們可以在Message Listener Container中 設定自訂consumer tag strategy。首先我們需要定義一個Consumer Tag Strategy類,它實現了ConsumerTagStrategy介面。

public class CustomConsumerTagStrategy implements ConsumerTagStrategy{    @Override    public String createConsumerTag(String queue) {        String consumerName = "Consumer1";        return consumerName + "_" + queue;    }}
在applicationContext.xml中設定自訂ConsumerTagStrategy

<bean id="consumerTagStrategy" class="com.qf.rabbitmq.strategy.CustomConsumerTagStrategy" /> <!--建立偵聽springMessageQueue隊列的Message Listener Container--> <bean id="messageListenerContainer"          class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">     <property name="messageConverter" ref="serializerMessageConverter" />     <property name="connectionFactory" ref="connectionFactory" />     <property name="messageListener" ref="consumerListener" />     <property name="queues" ref="springMessageQueue" />     <property name="acknowledgeMode" value="AUTO" />     <property name="consumerTagStrategy" ref="consumerTagStrategy" />  </bean>

再次啟動Web應用,查看RabbitMQ管理介面,我們可以看到Consumer Tag已經變成“Consumer1_springMessageQueue”,正如我們在CustomConsumerTagStrategy中設定的那樣。



消費者應用接收訊息驗證 我們編寫了一個生產者程式,向springMessageExchange發送訊息。 生產者的主要代碼如下,由於Exchange,Queue,Bind已經由消費者Web應用建立,因此生產者程式不再建立。

ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");factory.setPort(5672);factory.setUsername("rabbitmq_producer");factory.setPassword("123456");factory.setVirtualHost("test_vhosts"); //建立與RabbitMQ伺服器的TCP串連connection  = factory.newConnection();channel = connection.createChannel(); String message = "First Web RabbitMQ Message"; String correlationId = UUID.randomUUID().toString();AMQP.BasicProperties props = new AMQP.BasicProperties                    .Builder()                    .correlationId(correlationId)                    .build(); channel.basicPublish("springMessageExchange","springMessage", props, message.getBytes());

啟動消費者Web應用,從控制台輸出資訊可以看到消費者接收到了生產者發送的訊息。

設定訊息手動確認模式

到目前為止,消費者端的Web應用對訊息的確認是自動確認模式,如果我們想改為手動確認方式,需要做以下兩點改動:

1)修改applicationContext.xml檔案中Message Listener Container的acknowledgeMode屬性的值為MANUAL。

<bean id="messageListenerContainer"          class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">    ......    <property name="acknowledgeMode" value="MANUAL" /> </bean>

2)將自訂的Message Listener類從實現org.springframework.amqp.core.MessageListener介面,改為實現 org.springframework.amqp.rabbit.core.ChannelAwareMessageListener介面,實現它的 onMessage(Message,Channel)方法。

public class RabbitMQConsumer implements ChannelAwareMessageListener{    ...........     @Override    public void onMessage(Message message, Channel channel)     {        try         {             //spring-amqp Message對象中的Message Properties屬性             MessageProperties messageProperties = message.getMessageProperties();                          //使用Message Converter將spring-amqp Message對象中的Message Properties屬性         //轉換為RabbitMQ 的Message Properties對象             AMQP.BasicProperties rabbitMQProperties =             messagePropertiesConverter.fromMessageProperties(messageProperties, "UTF-8");                          System.out.println("The message's correlationId is:" + rabbitMQProperties.getCorrelationId());             String messageContent = null;             messageContent = new String(message.getBody(),"UTF-8");             System.out.print

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.