spring-boot & spring 实现 rabbitMq rpc模式的消息发送与接收

简介:

      本篇文章是我个人最近学习rabbitMq的一次总结,通过实际框架中的项目配置和代码,来实现rpc风格的rabbitMq的使用,主要分为两个项目,分别实现发送,回复和接受回复的功能。

     本篇文章不是rabbitMq的入门篇,所有对于新入门的同学,对exchange,queue,routingKey这些概念还不懂的话,不建议看这篇文章。

主要分为两个项目:

1、spring框架的主要实现发送消息和接受回复消息的功能,以下简称生产者

2、spring boot框架的项目主要实现接受消息和回复消息的功能,以下简称消费者

 

废话不多说,直接上代码

1、生产者pom.xml片段,主要在原有框架中,添加spring-rabbit的依赖

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

2、生产者配置文件,在resources下面新建rabbitMq.properties,内容如下

#rabbitMq
mq.ip=192.168.1.100
mq.port=5672
mq.userName=rabbitmq_producer
mq.password=123456
mq.virtualHost=test_vhosts

3、rabbit的spring配置文件,在resources下面新建spring-rabbitMq.xml,同时在web.xml中,像加载别的spring

配置文件一样配置一下。具体内容如下

<?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/>

    <!-- 引入配置文件 ps:在别的配置文件中引入了,所有这里注释掉了
    <bean id="propertyConfigurer"
          class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:rabbitMq.properties" />
    </bean>
-->

    <!--从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.virtualHost}" />
    </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"/>
    </bean>

    <!--构建Rabbit Template对象,用于发送RabbitMQ消息,本程序使用它发送消息,并接收返回消息-->
    <bean id="rabbitTemplate" class="org.springframework.amqp.rabbit.core.RabbitTemplate">
        <constructor-arg name="connectionFactory" ref="connectionFactory" />
        <property name="useTemporaryReplyQueues" value="false"/>
        <property name="messageConverter" ref="serializerMessageConverter"/>
        <property name="messagePropertiesConverter" ref="messagePropertiesConverter"/>
        <property name="replyAddress" value="springReplyMessageQueue"/>
        <property name="receiveTimeout" value="60000"/>
        <property name="replyTimeout" value="1000000000" />

    </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.sime.rabbit.listener.RabbitMQConsumer" />

    <!--创建侦听springMessageQueue队列的Message Listener Container ,ps:也可以理解为,用来创建connection和通道以及绑定消费者到queue -->
  <bean id="messageListenerContainer"
          class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">
        <property name="messageConverter" ref="serializerMessageConverter" />
        <property name="connectionFactory" ref="connectionFactory" />
        <property name="messageListener" ref="rabbitTemplate" />
        <property name="queues" ref="springReplyMessageQueue" />
        <!--  设置消息确认方式为手动确认  自动确认为AUTO 自动是 MANUAL-->
        <property name="acknowledgeMode" value="AUTO" />

   
</bean>

<bean id="consumerTagStrategy" class="com.sime.rabbit.bean.CustomConsumerTagStrategy"/>


<!--         ################################### rpc 模式   ######################################################################   -->

    <!--定义AMQP Reply Queue-->
    <bean id="springReplyMessageQueue" class="org.springframework.amqp.core.Queue">
        <constructor-arg name="name" value="springReplyMessageQueue" />
        <constructor-arg name="autoDelete" value="false" />
        <constructor-arg name="durable" value="true" />
        <constructor-arg name="exclusive" value="false" />
        <property name="adminsThatShouldDeclare" ref="rabbitAdmin" />
        <property name="shouldDeclare" value="true" />
    </bean>


    <!--定义AMQP Reply Exchange-->
    <bean id="springReplyMessageExchange" class="org.springframework.amqp.core.DirectExchange">
        <constructor-arg name="name" value="springReplyMessageExchange" />
        <constructor-arg name="durable" value="true" />
        <constructor-arg name="autoDelete" value="false" />
        <!--定义AMQP Queue创建所需的RabbitAdmin对象-->
        <property name="adminsThatShouldDeclare" ref="rabbitAdmin" />
        <property name="shouldDeclare" value="true" />
    </bean>

    <!--创建Reply Exchange和Reply Queue之间的Bind-->
    <bean id="springReplyMessageBind" class="org.springframework.amqp.core.Binding">
        <constructor-arg name="destination" value="springReplyMessageQueue" />
        <constructor-arg name="destinationType" value="QUEUE" />
        <constructor-arg name="exchange" value="springReplyMessageExchange" />
        <constructor-arg name="routingKey" value="springReplyMessage" />
        <constructor-arg name="arguments" ref="emptyMap" />
    </bean>





</beans>

需要注意的是,springReplyMessageQueue是专门用来回复的队列

4、SysRabbitProductTestController.java.  是写的一个用来测试发送消息并接收回复的测试类,这里面需要注意的是

 rabbitTemplate.sendAndReceive() 这个方法是默认等待5秒钟,如果在5秒内没有接收到回复的话,会返回一个空的message,这个默认的时间,实在spring-rabbit.xml中的rabbittemplete的配置的replyTimeout 来设置.

另外:使用sendAndReceive 这个方法发送消息时,消息的correlationId会变成系统动编制的1,2,3这个样子,而不是我们想要的

uuid的格式。

 

 


import com.rabbitmq.client.AMQP;
import com.sime.controller.base.BaseController;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.MessagePropertiesConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.UUID;


/*
*
 *  rabbitMq 发送测试
*/


@Controller
@RequestMapping("rabbitTest/")
public class SysRabbitProductTestController extends BaseController {

    @Autowired
    private MessagePropertiesConverter messagePropertiesConverter;


    @Autowired
    private RabbitTemplate rabbitTemplate;


    @RequestMapping("send")
    @ResponseBody
    public  String send(){
        String correlationId = UUID.randomUUID().toString();
        Date sendTime = new Date();
        AMQP.BasicProperties props =
                new AMQP.BasicProperties("text/plain",
                        "UTF-8",
                        null,
                        2,
                        0, correlationId, "springReplyMessageExchange", null,
                        null, sendTime, null, null,
                        "SpringProducer", null);


        MessageProperties sendMessageProperties =
                messagePropertiesConverter.toMessageProperties(props, null,"UTF-8");
         sendMessageProperties.setReceivedExchange("springReplyMessageExchange");
        sendMessageProperties.setReceivedRoutingKey("springReplyMessage");
        sendMessageProperties.setRedelivered(true);
         String message = "fa song xiaoxi ------------ 走你"; 
     

        try {
         //发送并接收回执
        Message sendMessage = MessageBuilder.withBody(message.getBytes("UTF-8"))
                     .andProperties(sendMessageProperties)
                    .build();


        Message replyMessage =   rabbitTemplate.sendAndReceive("springMessageExchange",
                        "springMessage", sendMessage);

        String replyMessageContent = null;
        replyMessageContent = new String(replyMessage.getBody(),"UTF-8");
        System.out.println("接收到回执:  "+replyMessageContent);
        return replyMessageContent;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

}

 

6、下面是消费者的pom.xml 的重要片段

       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

 

7、application.yml 关于rabbitMq的配置

spring:  
    rabbitmq:
      username: rabbitmq_consumer
      host: 192.168.1.100
      port: 5672
      password: 123456
      virtual-host: test_vhosts

8、常量配置类AppConstants

public class AppConstants {
    public static final String SEND_QUEUE_NAME = "springMessageQueue";
    public static final String SEND_EXCHANGE_NAME = "springMessageExchange";
    public static final String SEND_MESSAGE_KEY = "springMessage"; 
 

}

 

 

9、配置类 RabbitConfig.java

 
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.sime.constants.AppConstants;

import com.sime.rabbit.FinancMessageListener;
import com.sime.service.UserService;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.AbstractRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.MessageListenerContainer;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpoint;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.support.DefaultMessagePropertiesConverter;
import org.springframework.amqp.rabbit.support.MessagePropertiesConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.amqp.support.converter.SimpleMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableConfigurationProperties(RabbitProperties.class)
public class RabbitConfig {
    @Autowired
    private RabbitProperties rabbitProperties;


    @Bean("connectionFactory")
    public ConnectionFactory getConnectionFactory() {
        com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory =
                new com.rabbitmq.client.ConnectionFactory();
        rabbitConnectionFactory.setHost(rabbitProperties.getHost());
        rabbitConnectionFactory.setPort(rabbitProperties.getPort());
        rabbitConnectionFactory.setUsername(rabbitProperties.getUsername());
        rabbitConnectionFactory.setPassword(rabbitProperties.getPassword());
        rabbitConnectionFactory.setVirtualHost(rabbitProperties.getVirtualHost());

        ConnectionFactory connectionFactory = new CachingConnectionFactory(rabbitConnectionFactory);
        return connectionFactory;

    }

    @Bean(name="rabbitAdmin")
    public RabbitAdmin getRabbitAdmin() {
        RabbitAdmin rabbitAdmin = new RabbitAdmin(getConnectionFactory());
        rabbitAdmin.setAutoStartup(true);
        return rabbitAdmin;

    }



    @Bean(name="serializerMessageConverter")
    public MessageConverter getMessageConverter(){
        return new SimpleMessageConverter();
    }

    @Bean(name="messagePropertiesConverter")
    public MessagePropertiesConverter getMessagePropertiesConverter()
    {
        return new DefaultMessagePropertiesConverter();
    }




    @Bean(name="rabbitTemplate")
    public RabbitTemplate getRabbitTemplate()
    {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(getConnectionFactory());
        rabbitTemplate.setUseTemporaryReplyQueues(false);
        rabbitTemplate.setMessageConverter(getMessageConverter());
        rabbitTemplate.setMessagePropertiesConverter(getMessagePropertiesConverter());
    
        return rabbitTemplate;
    }

  

    @Bean(name="springMessageQueue")
    public Queue createQueue(@Qualifier("rabbitAdmin")RabbitAdmin rabbitAdmin)
    {
         Queue sendQueue = new  Queue(AppConstants.SEND_QUEUE_NAME,true,false,false);
        rabbitAdmin.declareQueue(sendQueue);
        return sendQueue;
    }

    @Bean(name="springMessageExchange")
    public Exchange createExchange(@Qualifier("rabbitAdmin")RabbitAdmin rabbitAdmin)
    {
        DirectExchange sendExchange = new DirectExchange(AppConstants.SEND_EXCHANGE_NAME,true,false);
        rabbitAdmin.declareExchange(sendExchange);
        return sendExchange;
    }

    @Bean(name="springMessageBinding")
    public Binding createMessageBinding(@Qualifier("rabbitAdmin")RabbitAdmin rabbitAdmin)
    {
        Map<String,Object> arguments = new HashMap<String,Object>();
        Binding sendMessageBinding =
                new Binding(AppConstants.SEND_QUEUE_NAME, Binding.DestinationType.QUEUE,
                        AppConstants.SEND_EXCHANGE_NAME, AppConstants.SEND_MESSAGE_KEY, arguments);
        rabbitAdmin.declareBinding(sendMessageBinding);
        return sendMessageBinding;
    }


}
 

 

10.消息接收Listener类


public class FinancMessageListener implements ChannelAwareMessageListener  {

    @Autowired
    private MessagePropertiesConverter messagePropertiesConverter;

  @Autowired
  private UserService userService;

  @Autowired
  private RabbitTemplate rabbitTemplate;

  @Override
  public void onMessage(Message message, Channel channel) throws Exception {
    System.out.println("+++++++++++++++++++++++++++++接收到消息了+++++++++++++++++++++++++++"+userService);
    System.out.println(new String(message.getBody(),"UTF-8"));
      try
      {

          MessageProperties messageProperties = message.getMessageProperties();
          AMQP.BasicProperties rabbitMQProperties =
                  messagePropertiesConverter.fromMessageProperties(messageProperties, "UTF-8");
          String messageContent = null;
          messageContent = new String(message.getBody(),"UTF-8");
          String consumerTag = messageProperties.getConsumerTag();
          System.out.println(consumerTag+ " get The message content is:" + messageContent);


          /**************************返回消息,5秒以内返回*************************************************/
            //RPC回复 ,发送消息
            String replyMessageContent = "Consumer1 返回的信息 '" + messageContent +" consumerTag:  "+consumerTag+ "'";


            String cId = rabbitMQProperties.getCorrelationId();
          AMQP.BasicProperties replyRabbitMQProps =
              new AMQP.BasicProperties("text/plain",
                      "UTF-8",
                      null,
                      2,
                      0, cId, null, null,
                      null, null, null, null,
                      consumerTag, null);
          Envelope replyEnvelope =
                  new Envelope(messageProperties.getDeliveryTag(), true,
                          "springReplyMessageExchange", "springReplyMessage");

          MessageProperties replyMessageProperties =
                  messagePropertiesConverter.toMessageProperties(replyRabbitMQProps,
                          replyEnvelope,"UTF-8");

          Message replyMessage = MessageBuilder.withBody(replyMessageContent.getBytes("UTF-8"))
                  .andProperties(replyMessageProperties)
                  .build();


          rabbitTemplate.send("springReplyMessageExchange","springReplyMessage", replyMessage);
          channel.basicAck(messageProperties.getDeliveryTag(), false);



      }
      catch (Exception e) {
            e.printStackTrace();
      }


  }
 }

 

 

截止到此,关键的部分的代码就完成了,仅做记录

参考:https://blog.csdn.net/u011126891/article/details/54376179

 

 

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值