前言
本文结合springboot学习rabbitmq(springboot版本2.2.2)
demo地址:https://github.com/foxiaotao/springboot-rabbitmq-demo
rabbitmq就是生产者把消息放到Exchange,Exchange根据规则把消息路由到Queue,消费者从Queue消费消息。
一、RabbitMq 构成
1、Exchange(交换器)
生产者将消息发布出去,消息首先就得Exchange来接受,发送消息时,不用考虑Queue,消息一经生产者发送,那么消息就在Exchange中(Exchange并不具备储存消息,这里便于理解才这样说)。
1)、Exchange主要类型介绍
direct(默认)
headers
fanout
topic
其中headers交换器允许你匹配AMQP消息的header而非路由键,除此之外headers交换器和direct交换器完全一致,但性能却很差,几乎用不到,所以我们本文也不做讲解。
注意:fanout、topic交换器是没有历史数据的,也就是说对于中途创建的队列,获取不到之前的消息。
2)、direct 模式
Exchange 和 Queue 关联就叫做路由键(RoutingKey),需要申明Exchange和Queue,以及Exchange和Queue的关联RoutingKey。申明绑定关系。一个RoutingKey唯一确定一个Queue。

@Bean("dlxExchangeBean")
public DirectExchange dlxExchangeBean() {
return new DirectExchange(dlxExchange);
}
@Bean("dlxQueueBean")
public Queue dlxQueueBean() {
return new Queue(dlxQueue);
}
@Bean
public Binding binding(@Qualifier("dlxExchangeBean") DirectExchange dlxExchange, @Qualifier("dlxQueueBean") Queue dlxQueue) {
return BindingBuilder.bind(dlxQueue).to(dlxExchange).with(dlxRoutingKey);
}
这种方式只是定义在java 代码中,如果在rabbitmq上没有创建过,还需要通过后台手动创建Exchange和Queue,并通过RoutingKey绑定。
每次发送消息需要指定Exchange 和 RoutingKey
例如:图1:中发送消息指定Exchange 和 RoutingKey=rk1,那么就只有Queue1收到消息,Queue2和Queue3不会有消息。只有Consumer1 消费到了消息。
3)、fanout模式
这种模式(可以参考广播模式),只需要将Exchange与Queue申明绑定,Exchange会把消息发布到所有Queue,这种不需要路由键。

@Bean(name="userExchange")
public FanoutExchange userFanoutExchange() {
return new FanoutExchange(userRegisterExchange, true, false);
}
@Bean(name = "userRegisterQueue")
public Queue userRegisterQueue() { //还款的queue
return new Queue(userRegisterQueue);
}
首次需要在rabbitmq后台创建并绑定,绑定不需要RoutingKey。如图2,消息只有发送Exchange,所有Queue都会用相同消息。
4)、topic模式
简单的说是前置匹配模式。路由键必须必须包含xx.* 或者xx.#
*表示一个字符
#表示任意个字符

如图3关系所示:
如果发送RoutingKey=odd.a,那么收到消息的有Queue1、Queue2;
如果发送RoutingKey=odd.age,那么收到消息的有Queue1;
2、Queue(队列)
队列就是消息的载体,车行驶在公路上,那么消息就放在队列中。通过Exchange的介绍,已经引入Queue,Queue是消息存放和通行的地方。Queue和Exchange的需要通过绑定建立关系。这种关系是长久存在的,不会因消息的改变而变化。就是定义Exchange和Queue的路由规则,消息携带的规则在总的Exchange中匹配,找到匹配的就将消息路由到Queue。
消费者监听指定队列,只要队列中有消息,监听程序获取到消息。供消费者使用。
3、broker(消息体)
就是Exchange 和Queue 全部的集合
二、消息的发送与接收
1、消息发送
消息发送到mq,在springboot中引入import org.springframework.amqp.rabbit.core.RabbitTemplate类,
rabbitTemplate.convertAndSend完成发送消息。
定义RabbitTemplate:
@Bean("delayCachingConnectionFactory")
public CachingConnectionFactory delayCachingConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host, port);
connectionFactory.setCacheMode(CachingConnectionFactory.CacheMode.CONNECTION);
connectionFactory.setChannelCacheSize(1024);
connectionFactory.setConnectionCacheSize(1024);
connectionFactory.setUsername(user);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(kaLoanVirtualHost);
connectionFactory.setPublisherReturns(false);
connectionFactory.setPublisherConfirms(false);
return connectionFactory;
}
@Bean(name = "delayRabbitTemplate")
public RabbitTemplate delayRabbitTemplate(@Qualifier("delayCachingConnectionFactory") ConnectionFactory connectionFactory) {
//RabbitTemplate rabbitTemplate = techRabbitBuilder.createRabbitTemplate(connectionFactory);
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
// 指定默认exchange
rabbitTemplate.setExchange(delayExchange);
// 指定默认路由键
rabbitTemplate.setRoutingKey(delayRoutingKey);
return rabbitTemplate;
}
/**
* 申明exchange
* @return
*/
@Bean("delayExchange")
public DirectExchange delayExchangeBean() {
return new DirectExchange(delayExchange);
}
@Bean("delayQueue")
public Queue orderQueueBean() {
return new Queue(delayQueue, true, false, false, null);
}
@Bean
public Binding orderBinding(@Qualifier("delayExchange") DirectExchange delayExchange, @Qualifier("delayQueue") Queue delayQueue) {
return BindingBuilder.bind(delayQueue).to(delayExchange).with(delayRoutingKey);
}
在config中申明RabbitTemplate delayRabbitTemplate,指定Bean.name,要是在程序中没有多个RabbitTemplate可以不用指定。@Bean的方法名称也同样是Bean的默认name。
定义完RabbitTemplate,就可以发送消息到mq 了。
package cn.quantgroup.clf.api.delayretry.service.senddelay;
import cn.quantgroup.clf.api.delayretry.model.DelayEventBody;
import cn.quantgroup.clf.util.JSONTools;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @description: 消息发送
* @author:tao
* @create: 2019-12-18 14:20
*/
@Slf4j
@Service
public class SendDelayEventMqServiceImpl implements SendDelayEventService {
@Resource(name = "delayRabbitTemplate")
private RabbitTemplate rabbitTemplate;
@Override
public void sendDelayEvent(DelayEventBody delayEventBody) {
rabbitTemplate.convertAndSend(JSONTools.serialize(delayEventBody));
}
}
显然可以看出Exchange类型是Direct,这里发送消息的时候并没有指定exchange和routingKey,是因为在RabbitTemplate定义的时候设置了默认的exchange和routingKey,当然也可以在每次发送消息的时候指定Exchange和routingKey,指定方法为,对应的exchange和routingKey的name。那么这样消息就发送到mq中了。
如图所示,在队列ka.dlx.queue中, 有5条消息。Features:D消息持久化,DLX、DLK死信;State:idle空闲、running运行。Ready、Unacked是消息的状态,Ready准备好的消息数量,Unacked:消费者还没有ack确认的消息数量。
当消息还在队列中的时候,可以通过Get Message 查询消息。
消息的发送只与Exchange和routingKey相关,不需要定义Queue。
2、消息接受
消息的接受,如果只是纯粹的接受消息,与Exchange和routingKey无关,消费者监听Queue即可。
package cn.quantgroup.cashloanflow.config.mq;
import cn.quantgroup.clf.api.delayretry.service.consumer.ConsumeDelayEventService;
import cn.quantgroup.tech.brave.service.ITechRabbitBuilder;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerEndpoint;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* @author: amen
* @date: 2019-01-08
*/
@Configuration
public class DelayMqConfig {
@Value("${ka.rabbitmq.connection.host}")
private String host;
@Value("${ka.rabbitmq.connection.port}")
private Integer port;
@Value("${ka.rabbitmq.connection.user}")
private String user;
@Value("${ka.rabbitmq.connection.password}")
private String password;
@Value("${ka.rabbitmq.connection.virtual-host}")
private String kaLoanVirtualHost;
@Value("${ka.rabbitmq.delay.queue:ka.delay.queue}")
private String delayQueue;
@Value("${ka.rabbitmq.delay.exchange:ka.delay.exchange}")
private String delayExchange;
@Value("${ka.rabbitmq.delay.routingKey:ka.delay.routingKey}")
private String delayRoutingKey;
@Value("${ka.rabbitmq.dlx.queue:ka.dlx.queue}")
private String dlxQueue;
@Value("${ka.rabbitmq.dlx.exchange:ka.dlx.exchange}")
private String dlxExchange;
@Value("${ka.rabbitmq.dlx.routingKey:ka.dlx.routingKey}")
private String dlxRoutingKey;
@Autowired
@Qualifier( "techRabbitBuilder" )
private ITechRabbitBuilder techRabbitBuilder;
@Bean("delayCachingConnectionFactory")
public CachingConnectionFactory delayCachingConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host, port);
connectionFactory.setCacheMode(CachingConnectionFactory.CacheMode.CONNECTION);
connectionFactory.setChannelCacheSize(1024);
connectionFactory.setConnectionCacheSize(1024);
connectionFactory.setUsername(user);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(kaLoanVirtualHost);
connectionFactory.setPublisherReturns(false);
connectionFactory.setPublisherConfirms(false);
return connectionFactory;
}
/**
* 申明死信队列
* @return
*/
@Bean("dlxExchangeBean")
public DirectExchange dlxExchangeBean() {
return new DirectExchange(dlxExchange);
}
@Bean("dlxQueueBean")
public Queue dlxQueueBean() {
return new Queue(dlxQueue);
}
@Bean
public Binding binding(@Qualifier("dlxExchangeBean") DirectExchange dlxExchange, @Qualifier("dlxQueueBean") Queue dlxQueue) {
return BindingBuilder.bind(dlxQueue).to(dlxExchange).with(dlxRoutingKey);
}
/**
* 申明延时队列
* @return
*/
@Bean("delayExchange")
public DirectExchange delayExchangeBean() {
return new DirectExchange(delayExchange);
}
@Bean("delayQueue")
public Queue orderQueueBean() {
Map<String, Object> arguments = new HashMap<>(4);
// 绑定该队列到死信交换机
arguments.put("x-dead-letter-exchange", dlxExchange);
arguments.put("x-dead-letter-routing-key", dlxRoutingKey);
return new Queue(delayQueue, true, false, false, arguments);
}
@Bean
public Binding orderBinding(@Qualifier("delayExchange") DirectExchange delayExchange, @Qualifier("delayQueue") Queue delayQueue) {
return BindingBuilder.bind(delayQueue).to(delayExchange).with(delayRoutingKey);
}
/**
* 监听死信队列
* @param consumeDelayEventService
* @param cachingConnectionFactory
* @param dlxQueue
* @return
*/
@Bean("delaySimpleMessageListenerContainer")
public SimpleMessageListenerContainer delaySimpleMessageListenerContainer(@Qualifier("consumeMqDelayEventService") ConsumeDelayEventService consumeDelayEventService,
@Qualifier("delayCachingConnectionFactory") CachingConnectionFactory cachingConnectionFactory,
@Qualifier("dlxQueueBean") Queue dlxQueue) {
SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory = techRabbitBuilder
.createSimpleRabbitListenerContainerFactory(cachingConnectionFactory);
SimpleRabbitListenerEndpoint simpleRabbitListenerEndpoint = new SimpleRabbitListenerEndpoint();
simpleRabbitListenerEndpoint.setQueues(dlxQueue);
simpleRabbitListenerEndpoint.setMessageListener(new MessageListenerAdapter(consumeDelayEventService, "consumeMessage"));
SimpleMessageListenerContainer container = simpleRabbitListenerContainerFactory.createListenerContainer(simpleRabbitListenerEndpoint);
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
container.setConcurrentConsumers(10);
container.start();
return container;
}
@Bean(name = "delayRabbitTemplate")
public RabbitTemplate delayRabbitTemplate(@Qualifier("delayCachingConnectionFactory") ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = techRabbitBuilder.createRabbitTemplate(connectionFactory);
rabbitTemplate.setExchange(delayExchange);
rabbitTemplate.setRoutingKey(delayRoutingKey);
return rabbitTemplate;
}
}
package cn.quantgroup.clf.api.delayretry.service.consumer;
/**
* @description: 消费延迟队列消息
* @author:tao
* @create: 2019-12-23 11:49
*/
public interface ConsumeDelayEventService {
void consumeMessage(String message);
}
实现ConsumeDelayEventService接口,即可收到消息。