SSM整合RabbitMQ,Spring4.x整合RabbitMQ

前言

SSM框架整合RabbitMQ【比较简单,复制粘贴可用】
本人使用的Spring版本是4.x

版本

RabbitMQ相关
erl10.0.1
RabbitMQ3.7.9
安装步骤参考:https://www.cnblogs.com/saryli/p/9729591.html

相关依赖
spring4.0.2.RELEASE
spring-rabbit1.3.5.RELEASE

实现

目录参考

这是我整合时的项目结构
关键:rabbitmq文件包和rabbitmq.properties、spring-rabbitmq.xml、spring-mvc.xml
在这里插入图片描述

pom.xml依赖

在现成的SSM项目中整合

	<!--rabbitmq依赖 -->
	<dependency>
	    <groupId>org.springframework.amqp</groupId>
	    <artifactId>spring-rabbit</artifactId>
	    <version>1.3.5.RELEASE</version>
	</dependency>

rabbitmq.properties配置文件

将 rabbitmq.properties配置文件添加到resources目录下

mq.host=127.0.0.1
mq.username=guest
mq.password=guest
mq.port=5672
mq.virtual-host=/

spring-rabbitmq.xml

将spring-rabbitmq.xml添加到resources目录下

<?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:rabbit="http://www.springframework.org/schema/rabbit"
       	xsi:schemaLocation="http://www.springframework.org/schema/beans
						   	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
							http://www.springframework.org/schema/context 
							http://www.springframework.org/schema/context/spring-context-4.0.xsd
						    http://www.springframework.org/schema/rabbit
						    http://www.springframework.org/schema/rabbit/spring-rabbit-1.3.xsd">

	<!-- 引入连接配置文件 -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">    
        <property name="location" value="classpath:rabbitmq.properties" />
    </bean> 
    
    <!-- 定义rabbitmq connectionFactory连接工厂 -->
    <rabbit:connection-factory id="connectionFactory"
    		username="${mq.username}"
			password="${mq.password}" 
			host="${mq.host}" 
			port="${mq.port}"
      		virtual-host="${mq.virtual-host}" />

    <!--通过指定下面的admin信息,当前producer中的exchange和queue会在rabbitmq服务器上自动生成 -->
    <rabbit:admin id="connectAdmin" connection-factory="connectionFactory" />

    <!--定义queue队列 -->
    <rabbit:queue name="queueTest" durable="true" auto-delete="false" exclusive="false" declared-by="connectAdmin" />
    <rabbit:queue name="queueTest1" durable="true" auto-delete="false" exclusive="false" declared-by="connectAdmin" />

    <!-- 定义direct exchange(也就是交换机),绑定queueTest队列(queueTest名称可以自定义) -->
    <rabbit:direct-exchange name="exchangeTest" durable="true" auto-delete="false" declared-by="connectAdmin">
        <rabbit:bindings>
            <rabbit:binding queue="queueTest" key="queueTestKey"></rabbit:binding>
            <rabbit:binding queue="queueTest1" key="queueTestKey1"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:direct-exchange>

    <!--定义rabbit template用于数据的接收和发送 将amqpTemplate对象绑定exchange中交换机-->
    <rabbit:template id="amqpTemplate" connection-factory="connectionFactory" exchange="exchangeTest" />

    <!-- 消息接收处理 -->
    <bean id="messageReceiver" class="com.rabbitmq.MessageConsumer"></bean>
    <bean id="messageReceiver1" class="com.rabbitmq.MessageConsumer2"></bean>

    <!-- queue litener 观察 监听模式 当有消息到达时会通知监听在对应的队列上的监听对象(监听),acknowledge="manual"设置消息手动确认(手动确认需要配合ack,不设置默认自动确认),prefetch="100"设置队列并发处理数,未确认消息会造成堵塞  -->
    <rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" prefetch="100">
        <rabbit:listener queues="queueTest" ref="messageReceiver" />
        <rabbit:listener queues="queueTest1" ref="messageReceiver1" />
    </rabbit:listener-container>

	<!-- 扫描注入使用注解实例对象 -->
	<context:component-scan base-package="com.rabbitmq" />

</beans>

spring-mvc.xml或applicationContext.xml

我这里使用的spring-mvc.xml,根据自己配置文件使用

<import resource="classpath:spring-rabbitmq.xml" />

将这个import引入添加到 spring-mvc.xml 里的最前面,如果不添加到前面可能会报错
在这里插入图片描述

rabbitmq目录下

这个目录下的java文件已在spring-rabbitmq.xml中进行扫描注入

MessageConsumer.java

说明:MessageConsumer和MessageConsumer2其实都可以使用同一个类,修改xml指向即可,但是分开明了些

package com.rabbitmq;

import java.nio.charset.Charset;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

/**
 * @Title消息消费者
 * @date 2023/10/8
 */
public class MessageConsumer implements MessageListener {

    @Override
    public void onMessage(Message message) {
    	// 逻辑处理
        System.out.println("message------->:" + new String(message.getBody(), Charset.forName("utf-8")));
    }
    
}

MessageConsumer2.java

package com.rabbitmq;

import java.nio.charset.Charset;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

/**
 * @Title消息消费者2
 * @date 2023/10/8
 */
public class MessageConsumer2 implements MessageListener {

    @Override
    public void onMessage(Message message) {
    	// 逻辑处理
        System.out.println("message2------->:" + new String(message.getBody(), Charset.forName("utf-8")));
    }
    
}

MessageProducer.java

package com.rabbitmq;

import javax.annotation.Resource;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Service;

/**
 * @Title 消息生产者
 * @date 2023/10/8
 */
@Service
public class MessageProducer {

    @Resource
    private AmqpTemplate amqpTemplate;

    
    public void sendMessage(String key, Object message){
        amqpTemplate.convertAndSend(key, message);
    }
    
}

MessageConstant.java

package com.rabbitmq;

/**
 * @Title 消息队列常量
 * @date 2023/10/8
 */
public class MessageConstant{

	public static String queueTestKey = "queueTestKey";
	public static String queueTestKey1 = "queueTestKey1";
    
	
}

测试调用

比如这个下面在某个类里作为接口调用测试

	@Autowired
    private MessageProducer messageProducer;

	@RequestMapping(value = "/testMq")
    @ResponseBody
    public Result testMq(HttpServletRequest request) throws IOException {
        messageProducer.sendMessage(MessageConstant.queueTestKey, "登录");
        messageProducer.sendMessage(MessageConstant.queueTestKey1, "退出");
        return Result.success("测试成功");
    }

调用接口后打印结果
在这里插入图片描述
连接结果
在这里插入图片描述
以上即可!

扩展

包括消息手动确认,消息失败重新加入队列处理

并发限制

比如:同一个时间发了5000条记录,队列需要排队处理消息;rabbitmq1.x版本默认是队列长度1,rabbitmq2.x版本默认长度是250,我这里使用了1.x的,可以看到处理起来是比较慢的一条一条排队处理,所以要手动设置队列长度,如下

在spring-rabbitmq.xml中加入prefetch属性值设置

    <!-- queue litener 观察 监听模式 当有消息到达时会通知监听在对应的队列上的监听对象(监听),acknowledge="manual"设置消息手动确认(手动确认需要配合ack,不设置默认自动确认),prefetch="100"设置队列并发处理数,未确认消息会造成堵塞  -->
    <rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" prefetch="100">
        <rabbit:listener queues="queueTest" ref="messageReceiver" />
        <rabbit:listener queues="queueTest1" ref="messageReceiver1" />
    </rabbit:listener-container>

消息重发

SpringBoot版可在配置文件中设置,且异常后直接抛出即可

方式一

package com.rabbitmq;

import java.nio.charset.Charset;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;

import com.rabbitmq.client.Channel;

/**
 * @Title 消息消费者
 * @date 2023/10/8
 */
public class MessageConsumer2 implements ChannelAwareMessageListener {
	
	private int aa = 1;

	@Override
	public void onMessage(Message message, Channel channel) throws Exception {
		try {
		
			// 逻辑处理
			if(aa == 1) {
				aa = 2;
				int a = 1/0;
			}
			
			System.out.println("成功处理确认message2------->:" + new String(message.getBody(), Charset.forName("utf-8")));
			// 消费者ack确认【消息处理成功确认】
	        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
		}catch (Exception e) {
			System.out.println("失败重新入队message2------->:" + new String(message.getBody(), Charset.forName("utf-8")));
			// 消费者reject确认【消息失败重新加入队列-重发】
			channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
		}
	}
    
}

方式二

MessageConstant.java中加入

	/** 重试次数 3 */
	public static Integer RETRY_COUNT = 3;

消息接收处理类

package com.rabbitmq;

import java.nio.charset.Charset;

import org.apache.log4j.Logger;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.fastjson.JSONObject;
import com.bean.ConsumptionRequest;
import com.rabbitmq.client.Channel;
import com.service.ReceiveDormitoryService;

/**
 * 宿舍mq消息处理
 * 
 * @author Administrator
 */
public class MessageConsumerSuShe implements ChannelAwareMessageListener {
	
	private final Logger logger = Logger.getLogger(MessageConsumerSuShe.class);
	
	@Autowired
	private ReceiveDormitoryService service;
	

	@Override
	public void onMessage(Message message, Channel channel) throws Exception {
		int retryCount = 0; // 重试机制
		long deliveryTag = message.getMessageProperties().getDeliveryTag();
		while(retryCount < MessageConstant.RETRY_COUNT) {
			retryCount ++;
			try {
				// 逻辑处理
				String s = new String(message.getBody(), Charset.forName("utf-8"));
				ConsumptionRequest bean = JSONObject.parseObject(s, ConsumptionRequest.class);
				//service.uploadData(bean, bean.getPath());
				
				//logger.info("【SUSHE_QUEUE_KEY宿舍队列成功】:" + new String(message.getBody(), Charset.forName("utf-8")));
				// 消费者ack确认【消息处理成功确认】
		        channel.basicAck(deliveryTag, false);
		        return;
			}catch (Exception e) {
				logger.error("【SUSHE_QUEUE_KEY宿舍队列错误,重试"+retryCount+"】:" + new String(message.getBody(), Charset.forName("utf-8")));
				// 0.5s重试一次
				Thread.sleep(500);
			}
		}
		// 重试3次后直接处理(这里设置为死信消息)
		if(retryCount >= MessageConstant.RETRY_COUNT) {
			channel.basicNack(deliveryTag, false, false);
		}
	}
    
}

多部署Tomcat下问题

本人使用单个RabbitMQ服务
测试两个Tomcat服务连接同一个交换机和队列进行发送消息,并没有造成两个Tomcat服务都推送处理这条消息,而是单个Tomcat处理了这条消息
所以未造成多部署下一条消息多服务处理问题

其他
参考类似博客1:https://blog.csdn.net/u012988901/article/details/89499634
参考类似博客2:https://blog.csdn.net/weixin_42654295/article/details/109006276

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值