0.我的目錄結構
1.需要依賴的jar包
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-amqp</artifactId>
<version>1.5.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.5.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.1.2.RELEASE</version>
</dependency>
2.生產者配置文件 applicationContext-rabbitmq-send.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:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-4.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<!--導入配置文件 -->
<bean id="mysqlSource"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbcConfig.properties"/>
</bean>
<!-- 定義RabbitMQ的連接工廠 -->
<rabbit:connection-factory id="connectionFactory"
username="${rabbit.username}" password="${rabbit.password}" host="${rabbit.host}" port="${rabbit.port}"
virtual-host="${rabbit.virtual-host}"
publisher-confirms="true"
publisher-returns="true"
channel-cache-size="5"
/>
<!-- 定義Rabbit模板,指定連接工廠以及定義exchange
rabbitTemplate
-->
<!--定義rabbit template用於數據的接收和發送 -->
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"
exchange="${rabbit.direct-exchange}" confirm-callback="CallBackMQ"
mandatory="true"
return-callback="ReturnCall"/>
<!-- MQ的管理,包括隊列、交換器等 -->
<rabbit:admin connection-factory="connectionFactory" />
<!--<rabbit:fanout-exchange name=""></rabbit:fanout-exchange>-->
<!--定義queue -->
<rabbit:queue name="queueZhoa111" auto-declare="true"/>
<!-- 定義direct exchange,綁定queue -->
<rabbit:direct-exchange name="exchangeZhoa111" auto-declare="true" durable="true">
<rabbit:bindings>
<rabbit:binding queue="queueZhoa111" key="zhoa"/>
</rabbit:bindings>
</rabbit:direct-exchange>
<!--消息是否成功發送到Exchange 的回調-->
<bean id="CallBackMQ" class="com.util.rabbitmq.CallBackMQ"></bean>
<!--消息從Exchange路由到隊列的回調,注意這裏只有路由失敗的時候纔會調此方法-->
<bean id="ReturnCall" class="com.util.rabbitmq.ReturnCall"></bean>
</beans>
3.消費者配置文件 applicationContext-rabbitmq-receive.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:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-4.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<!--導入配置文件 -->
<bean id="mysqlSource"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbcConfig.properties"/>
</bean>
<!-- 定義RabbitMQ的連接工廠 -->
<rabbit:connection-factory id="connectionFactory"
username="${rabbit.username}" password="${rabbit.password}" host="${rabbit.host}" port="${rabbit.port}"
virtual-host="${rabbit.virtual-host}"
publisher-confirms="true"
publisher-returns="true"
channel-cache-size="5"
/>
<!-- MQ的管理,包括隊列、交換器等 -->
<rabbit:admin connection-factory="connectionFactory" />
<!--定義消息隊列queue -->
<rabbit:queue name="queueZhoa111" auto-declare="true"/>
<!-- 定義交換機,並且完成隊列和交換機的綁定 -->
<rabbit:direct-exchange name="exchangeZhoa111" auto-declare="true">
<rabbit:bindings>
<rabbit:binding queue="queueZhoa111" key="zhoa"/>
</rabbit:bindings>
</rabbit:direct-exchange>
<!-- 定義監聽 -->
<!--定義消費者監聽隊列 acknowledge設置消費者手動確認消息 原因是:rabbitmq默認是自動確認消息的,不管消費者有沒有消費成功
只要消費者收到消息後就直接確認了,確認後rabbitmq就會將隊列中的消息刪除掉 如果消費者收到消息後實際沒有消費成功,就會導致消息丟失
-->
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual">
<!-- 監聽一個隊列,當隊列中有消息,就會自動觸發類.方法,傳遞消息就作爲方法的參數,根據方法聲明的參數強轉 -->
<rabbit:listener ref="messageReceiver2" queue-names="queueZhoa111"/>
</rabbit:listener-container>
<bean id="messageReceiver2" class="com.util.rabbitmq.CatHandler2"/>
</beans>
4.引入的配置文件配置 jdbcConfig.properties (路徑錯誤的話自己修改)
rabbit.host=127.0.0.1
rabbit.port=5672
rabbit.username=zsq
rabbit.password=zsq
rabbit.virtual-host=/zhang
rabbit.direct-exchange=exchangeZhoa
5. applicationContext.xml 引入配置文件 ,添加線程池配置
<!--添加配置文件-->
<import resource="classpath*:applicationContext-rabbitmq-receive.xml" />
<import resource="classpath*:applicationContext-rabbitmq-send.xml" />
<!--添加線程池-->
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 核心線程數 -->
<property name="corePoolSize" value="10" />
<!-- 最大線程數 -->
<property name="maxPoolSize" value="20" />
<!-- 隊列最大長度 -->
<property name="queueCapacity" value="1800" />
<!-- 線程池維護線程所允許的空閒時間,默認爲60s -->
<property name="keepAliveSeconds" value="60" />
</bean>
<!-- 註解式 -->
<task:annotation-driven />
6.springmvcConfig.xml 配置文件,添加掃包範圍
<context:component-scan base-package="com.util.rabbitmq"/>
7,核心代碼
controller控制層,調用發送消息工具類
@RestController
@RequestMapping("/test")
public class RabbitMQTest {
@Autowired
private RabbitAckServiceImpl rabbitAckService;
@RequestMapping("/test2")
public void sendMessage2() {
for (int i = 1; i <=100 ; i++) {
Map<String,Object> map=new HashMap<>();
map.put("hh","zsq"+i);
System.out.println("to send message: "+ map);
String json = JSON.toJSONString(map);
boolean zhoa = rabbitAckService.sendMessage("exchangeZhoa111","zhoa", json);
if(zhoa){
System.out.println("發送成功1");
}else {
System.out.println("發送失敗");
}
}
}
}
工具類發送消息
@Service
public class RabbitAckServiceImpl {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* @param exchange 交換機
* @param routingKey 路由key
* @param message 消息
*/
public boolean sendMessage(String exchange,String routingKey , String message) {
boolean result=false;
if(!checkParam(exchange,routingKey,message)){
return false;
}
try {
rabbitTemplate.convertAndSend(exchange,routingKey,message);
result = true;
}catch (AmqpException e){
e.printStackTrace();
}
return result;
}
public boolean checkParam(String param1){
if(StringUtils.isEmpty(param1)){
return false;
}else {
return true;
}
}
public boolean checkParam(String param1,String param2){
if(StringUtils.isEmpty(param1)||StringUtils.isEmpty(param2)){
return false;
}else {
return true;
}
}
public boolean checkParam(String param1,String param2,String param3){
if(StringUtils.isEmpty(param1)||StringUtils.isEmpty(param2)||StringUtils.isEmpty(param3)){
return false;
}else {
return true;
}
}
}
實現接口 , 消息發送到exchang交換機 , 返回投遞結果
public class CallBackMQ implements RabbitTemplate.ConfirmCallback {
int i=1;
@Override //消息投遞到exchange是否成功
public void confirm(CorrelationData correlationData, boolean b, String s) {
if (b) {
//設置消息投遞成功
System.out.println("消息投遞成功"+i);
i++;
} else {
//消息投遞失敗
System.out.println(s);
System.out.println("消息投遞失敗");
}
}
}
實現接口, 消息從Exchange交換機 發送到 對列Queue 失敗時回調執行此方法
public class ReturnCall implements RabbitTemplate.ReturnCallback {
/**
*只有消息從Exchange路由到Queue失敗纔會回調這個方法
*/
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("消息從Exchage路由到Queue失敗");
}
}
消費者消費 ,採用多線程異步消費
//消費者 採用異步多線程消費
public class CatHandler2 implements ChannelAwareMessageListener {
@Autowired
private TaskExecutor taskExecutor;
@Override
public void onMessage(Message message, Channel channel) throws Exception {
taskExecutor.execute(new Consume(message,channel));
}
}
線程類方法
public class Consume implements Runnable{
private Message message;
private Channel channel;
public Consume(Message message, Channel channel){
this.message=message;
this.channel=channel;
}
@Override
public void run() {
boolean falg = false;
try {
// msg就是rabbitmq傳來的消息,需要的同學自己打印看一眼
// 使用jackson解析
Map map = JSON.parseObject(message.getBody(), Map.class);
System.out.println(Thread.currentThread().getName()+"收到消息:我是可愛的小豬,我的名字是" + map.get("hh"));
//Thread.sleep(2000);
falg = true;
} catch (Exception e) {
e.printStackTrace();
}finally {
MessageProperties properties = message.getMessageProperties();
long tag = properties.getDeliveryTag();
//消費成功後將手動確認消息
if (falg) {
//消息確認,發送成功
try {
channel.basicAck(tag, false);
} catch (IOException e) {
e.printStackTrace();
}
} else {
//如果發送失敗,消息會重新進入隊列,再次被這個消費者消費
//消息發送失敗後,也可以將消息發送到別的隊列,讓其他消費者進行消費
//第三個參數 true爲重新將消息放入隊列,如果設置爲false,則拋棄這條消息
try {
channel.basicNack(tag, false, true);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}