Spring集成RabbitMQ

一、pom.xml依赖

<!-- rabbitMQ -->

    <dependency>
           <groupId>com.rabbitmq</groupId>
           <artifactId>amqp-client</artifactId>
           <version>3.5.1</version>
     </dependency>
     <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
            <version>1.4.5.RELEASE</version>
    </dependency>

二、生产者

 

1、spring-rabbitmq.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" 
   xmlns:rabbit="http://www.springframework.org/schema/rabbit"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context" 
   xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd"
   default-lazy-init="true">
   
   <description>rabbitmq 连接服务配置</description>
<!-- 加载配置属性文件 -->
<context:property-placeholder ignore-unresolvable="true" location="classpath:weijinrong.properties" />
   
<!-- 消息对象json转换类 -->
    <bean id="jsonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter" /> 
   
<!-- 连接配置 -->
    <rabbit:connection-factory id="connectionFactory" host="${mq.host}"  username="${mq.username}" password="${mq.password}" port="${mq.port}"/>
    
    <rabbit:admin connection-factory="connectionFactory"/>
   
<!-- spring template声明 -->
    <rabbit:template id="amqpTemplate" exchange="test-mq-exchange"  connection-factory="connectionFactory"  message-converter="jsonMessageConverter" />
    
     <!-- 消息队列Queue 
        durable:是否持久化
        exclusive: 仅创建者可以使用的私有队列,断开后自动删除
        auto_delete: 当所有消费客户端连接断开后,是否自动删除队列 -->
    
    <rabbit:queue id="test_queue_id" name="test_queue_name" durable="true" auto-delete="false" exclusive="false" />   

<!-- 声明一个Exchange
rabbit:direct-exchange:定义exchange模式为direct,意思就是消息与一个特定的路由键完全匹配,才会转发。
    rabbit:binding:设置消息queue匹配的key -->
<rabbit:direct-exchange name="test-mq-exchange" durable="true" auto-delete="false" id="test-mq-exchange">
        <rabbit:bindings>
            <rabbit:binding queue="test_queue_name" key="test_queue_routing_key"/>
        </rabbit:bindings>
    </rabbit:direct-exchange>
   
</beans>

2、测试类

/**

 * rabbitMQ测试类
 */
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;



@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/spring-context-rabbitMQ.xml"})
public class TestQueue {
@Autowired
    private AmqpTemplate amqpTemplate;

private final String queue_key = "test_queue_routing_key";

    @Test
    public void send(){
//        Map<String,Object> msg = new HashMap<String,Object>();
//        msg.put("data","hello,rabbmitmq!");
        amqpTemplate.convertAndSend(queue_key, "123");
    }
}

三、消费者

spring-rabbitmq.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" 
   xmlns:rabbit="http://www.springframework.org/schema/rabbit"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context" 
   xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd"
   default-lazy-init="true">
   
   <description>rabbitmq 连接服务配置</description>
<!-- 加载配置属性文件 -->
<context:property-placeholder ignore-unresolvable="true" location="classpath:weijinrong.properties" />
   
<!-- 消息对象json转换类 -->
    <bean id="jsonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter" /> 
   
<!-- 连接配置 -->
    <rabbit:connection-factory id="connectionFactory" host="${mq.host}"  username="${mq.username}" password="${mq.password}" port="${mq.port}"/>
    
    <rabbit:admin connection-factory="connectionFactory"/>
   
<!-- spring template声明 -->
    <rabbit:template id="amqpTemplate" exchange="test-mq-exchange"  connection-factory="connectionFactory"  message-converter="jsonMessageConverter" />
    
     <!-- 消息队列Queue 
        durable:是否持久化
        exclusive: 仅创建者可以使用的私有队列,断开后自动删除
        auto_delete: 当所有消费客户端连接断开后,是否自动删除队列 -->
    
    <rabbit:queue id="test_queue_id" name="test_queue_name" durable="true" auto-delete="false" exclusive="false" />   

<!-- 声明一个Exchange
rabbit:direct-exchange:定义exchange模式为direct,意思就是消息与一个特定的路由键完全匹配,才会转发。
    rabbit:binding:设置消息queue匹配的key -->
<rabbit:direct-exchange name="test-mq-exchange" durable="true" auto-delete="false" id="test-mq-exchange">
        <rabbit:bindings>
            <rabbit:binding queue="test_queue_name" key="test_queue_routing_key"/>
        </rabbit:bindings>
    </rabbit:direct-exchange>
    
   <!--  监听器 -->
    <bean id="queueListenter" class="com.autoserve.weijinrong.rytc.QueueListenter"/>


   <!--  配置监听queue -->
    <rabbit:listener-container connection-factory="connectionFactory" acknowledge="auto">
            <rabbit:listener queues="test_queue_name" ref="queueListenter"/>
    </rabbit:listener-container>
</beans>

 

监听器实体类

/**
 * rabbitMQ监听器
 * @author lzp
 *
 */
public class QueueListenter implements MessageListener{

    @Override
    public void onMessage(Message message) {
                try{
             String str = new String(message.getBody(), "UTF-8");
                    System.out.print("=====获取消息"+str);
     
                }catch(Exception e){
                    e.printStackTrace();
                }
    }

}

三、如何保证消息不被重复消费?(如何保证消息消费时的幂等性)

其实还是得结合业务来思考,几个思路:

  1. 比如你拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入了,update一下好吧

   2.比如你是写redis,那没问题了,反正每次都是set,天然幂等性。

3.比如你不是上面两个场景,那做的稍微复杂一点,你需要让生产者发送每条数据的时候,里面加一个全局唯一的id,类似订单id之类的东西,然后你这里消费到了之后,先根据这个id去比如redis里查一下,之前消费过吗?如果没有消费过,你就处理,然后这个id写redis。如果消费过了,那你就别处理了,保证别重复处理相同的消息即可。

四、如何保证消息不丢失

如果说你这个是用 MQ 来传递非常核心的消息,比如说计费、扣费的一些消息,那必须确保这个 MQ 传递过程中绝对不会把计费消息给弄丢。消息丢失怎么办?如何保证消息的可靠性传输?或者说,如何处理消息丢失的问题?

4.1 生产者弄丢了数据

生产者将数据发送到 RabbitMQ 的时候,可能数据就在半路给搞丢了,因为网络问题啥的,都有可能。

RabbitMQ提供事务机制confirm模式来确保生产者不丢消息。

RabbitMQ 事务机制(同步)就是说,发送消息前开启事务,发送过程中如果出现什么异常,事务就会回滚,如果发送成功则提交事务,它的缺点是吞吐量会下降,因为是同步的,提交一个事务之后会阻塞在哪儿,太耗性能。

事务的实现主要是对信道(Channel)的设置,主要的方法有三个:

channel.txSelect()声明启动事务模式;

channel.txComment()提交事务;

channel.txRollback()回滚事务;

用confirm模式的居多。在生产者那里设置开启confirm模式之后,每次写的消息都会分配一个唯一的id,然后如果写入了RabbitMQ中,RabbitMQ会告诉你这个消息是否接受成功,如果接收失败,你可以重试。confirm机制是异步的,你发送消息之后就可以发送下一个消息,然后RabbitMQ接收之后会异步回调你的一个接口通知你这个消息接收到了。

Confirm的三种实现方式:

方式一:channel.waitForConfirms()普通发送方确认模式;

看代码可以知道,我们只需要在推送消息之前,channel.confirmSelect()声明开启发送方确认模式,再使用channel.waitForConfirms()等待消息被服务器确认即可。

方式二:channel.waitForConfirmsOrDie()批量确认模式;

以上代码可以看出来channel.waitForConfirmsOrDie(),使用同步方式等所有的消息发送之后才会执行后面代码,只要有一个消息未被确认就会抛出IOException异常。

方式三:channel.addConfirmListener()异步监听发送方确认模式;

代码是异步执行的,消息确认有可能是批量确认的,是否批量确认在于返回的multiple的参数,此参数为bool值,如果true表示批量执行了deliveryTag这个值以前的所有消息,如果为false的话表示单条确认。

4.2 消息队列弄丢数据

这个一般是开启 RabbitMQ 的持久化,写入消息之后会自动持久化到磁盘,哪怕RabbitMQ 挂了,恢复之后会自动读取之前存储的数据,一般数据不会丢。如果RabbitMQ 还没持久化就挂了,这个可以配合confirm模式,你可以在消息持久化后发送给生产者一个Ack确认信号,告诉生产者已经持久成功,如果生产者没有收到确认信号,生产者会自动重发。

         如何进行持久化?

         1.创建队列时,将持久化标识durable设置为true。

         2. 第二个是发送消息的时候将消息的 deliveryMode设置为 2

       这个就是将消息设置为持久化的。

       必须要同时设置这两个持久化才行,RabbitMQ 哪怕是挂了,再次重启,也会从磁盘上重启恢复 queue,恢复这个 queue 里的数据。

4.3 消费者丢数据

启用手动确认模式可以解决这个问题

参考:https://www.cnblogs.com/pypua/articles/7519213.html

https://blog.csdn.net/u010899985/article/details/109150280

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值