文章目录
1.Spring AMQP简介
spring-amqp是对AMQP的一些概念的一些抽象,spring-rabbit是对RabbitMQ操作的封装实
现。主要有几个核心类 RabbitAdmin
、 RabbitTemplate
、 SimpleMessageListenerContainer
等。
- RabbitAdmin 类完成对Exchange,Queue,Binding的操作,在容器中管理了 RabbitAdmin 类的
时候,可以对Exchange,Queue,Binding进行自动声明。 - RabbitTemplate 类是发送和接收消息的工具类。
- SimpleMessageListenerContainer 是消费消息的容器。
2.创建演示项目
使用Spring Boot快速创建一个项目(不使用Spring Boot RabbitMQ特性,后续章节会介绍RabbitMQ整合Spring Boot)。项目POM文件:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!---为了使用jackson而引入,也可以单独引入jackson-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
添加RabbitMQ配置文件 RabbitMQConfig
@Component
public class RabbitMQConfig {
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses("127.0.0.1:5672");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
return connectionFactory;
}
/**
* RabbitAdmin 注入
* @param connectionFactory
* @return
*/
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
/**
* RabbitTemplate
* @param connectionFactory
* @return
*/
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate=new RabbitTemplate(connectionFactory);
return rabbitTemplate;
}
}
3.RabbitAdmin
AMQP对四种交换机(fanout、topic、direct、headers
)类型进行了封装,AMQP封装了 Queue
DirectExchange
、TopicExchange
、FanoutExchange
、HeadersExchange
,可以使用RabbitAdmin声明Exchange和Queue,并且进行Binding。
以Direct交换机为例,代码如下:
@Autowired
private RabbitAdmin rabbitAdmin;
/**
* direct 交换机
*/
@Test
public void DirectTest(){
//声明direct交换机
rabbitAdmin.declareExchange(new DirectExchange("test_direct_spring",true,false));
//声明队列
rabbitAdmin.declareQueue(new Queue("test_direct_spring_queue",true,false,false,null));
//绑定
rabbitAdmin.declareBinding(new
Binding("test_direct_spring_queue",
Binding.DestinationType.QUEUE,
"test_direct_spring",
"test.direct",null));
}
运行代码,在RabbitMQ控制台中可以看到声明的交换机和队列,以及他们之间的绑定关系。
其余交换机类型的声明以及绑定与与上面代码类似,换一下类就可以了,就不贴代码了。如果对RabbitMQ交换机还不了解,可以看下之前的这几篇文章
- RabbitMQ Exchange类型之Direct Exchange
- RabbitMQ Exchange类型之Topic Exchange
- RabbitMQ Exchange类型之fanout Exchange
- RabbitMQ Exchange类型之headers Exchange
AMQP还提供了链式编程的方式在绑定的时候声明Exchange和Queue,使用起来比上面分别声明、绑定的代码要简练,以Direct交换机为例,如下代码:
/**
* direct 交换机
*/
@Test
public void DirectTest(){
//绑定声明一步到位
rabbitAdmin.declareBinding(BindingBuilder.bind(
new Queue("test_direct_spring_queue",
true,
false,
false,
null)
).to( new DirectExchange("test_direct_spring",true,false))
.with("test.direct")); // with 后面绑定的是routingKey
}
4.使用注解的方式声明Exchange、Queue并Binding
在实际项目当中,更多的是使用Spring注解的方式。
如下代码,声明了一个topic
交换机,并在交换机上绑定了queue001
和queue002
两个队列。添加到RabbitMQConfig
类中
@Bean
public TopicExchange topicExchange001(){
return new TopicExchange("topicExchange001",true,false);
}
@Bean
public Queue queue001(){
return new Queue("queue001",true,false,false);
}
@Bean
public Binding binding001(){
return BindingBuilder.bind(queue001()).to(topicExchange001()).with("spring.#");
}
@Bean
public Queue queue002(){
return new Queue("queue002",true);
}
@Bean
public Binding binding002(){
return BindingBuilder.bind(queue002()).to(topicExchange001()).with("springboot.#");
}
5.RabbitTemplate
RabbitTemplate封装了消息发送的模板以及setConfirmCallback
、和setReturnCallback等API。
使用RabbitTemplate发送一个消息:
@Test
public void sendMsgTest(){
//创建消息header
MessageProperties messageProperties=new MessageProperties();
messageProperties.getHeaders().put("desc","desc heelo");
messageProperties.getHeaders().put("type","rabbitmq spring topic test");
//消息类型
messageProperties.setContentType("text/plain");
Message message=new Message("spring rabbitmq test".getBytes(),messageProperties);
//发送消息
rabbitTemplate.convertSendAndReceive("topicExchange001", "spring.test", message);
// 当消息不可达时,执行ReturnListener回调
rabbitTemplate.setMandatory(true);
Message message2=new Message("hello spring boot message".getBytes(),messageProperties);
//发送消息
rabbitTemplate.convertSendAndReceive("topicExchange001","springboot.test",message2);
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (!ack){
// TODO 消息未被消费,执行一些操作
}
}
});
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
// TODO 当消息不可达时 执行一些操作
}
});
}
RabbitTemplate
类的convertSendAndReceive
提供了很多的重载,可以满足不同情况下的消息发送。
6.SimpleMessageListenerContainer(消息监听)
SimpleMessageListenerContainer
容器是对消费队列的监听,接下我们就使用它来进行队列的监听及消费。
@Bean
public SimpleMessageListenerContainer simpleMessageListenerContainer(ConnectionFactory connectionFactory){
SimpleMessageListenerContainer container=new SimpleMessageListenerContainer(connectionFactory);
//监听的消息队列,可以设置多个
container.setQueues(queue001(),queue002());
//消息签收模式
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
container.setConcurrentConsumers(1);//当前消费者数量
container.setMaxConcurrentConsumers(5);//最大消费者数量
container.setDefaultRequeueRejected(false);//消息失败,是否重回队列
//设置消费者的Tag唯一标识,AMQP封装了可以根据Tag进行消息的消费
container.setConsumerTagStrategy(new ConsumerTagStrategy() {
@Override
public String createConsumerTag(String s) {
return s+"_"+ UUID.randomUUID().toString();
}
});
/**
* 接收消息
*/
container.setMessageListener(new ChannelAwareMessageListener(){
/**
* 接受消息
* @param message
* @param channel
* @throws Exception
*/
@Override
public void onMessage(Message message, Channel channel) throws Exception {
System.out.println("-------------消费者接收消息:"+new String(message.getBody()));
}
});
return container;
}
7.MessageListenerAdapter(消息监听适配器)
上面使用了setMessageListener
对消息进行简单的监听,AMQP还提供了MessageListenerAdapter
适配器,允许自定义消息委托及消息类型的转化。
接下来自定义一个消息委托,新建一个MessageDelegate
类型,如下:
public class MessageDelegate {
/**
* 消息接受的默认方法,可以通过MessageListenerAdapter类的setDefaultListenerMethod修改
* @param messageBody
*/
public void handleMessage(Message messageBody){
System.out.println("接收到的消息:"+new String(messageBody.getBody()));
}
}
@Bean
public SimpleMessageListenerContainer simpleMessageListenerContainer(ConnectionFactory connectionFactory){
SimpleMessageListenerContainer container=new SimpleMessageListenerContainer(connectionFactory);
//监听的消息队列,可以设置多个
container.setQueues(queue001(),queue002());
//消息签收模式
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
container.setConcurrentConsumers(1);//当前消费者数量
container.setMaxConcurrentConsumers(5);//最大消费者数量
container.setDefaultRequeueRejected(false);//消息失败,是否重回队列
//设置消费者的Tag唯一标识,AMQP封装了可以根据Tag进行消息的消费
container.setConsumerTagStrategy(new ConsumerTagStrategy() {
@Override
public String createConsumerTag(String s) {
return s+"_"+ UUID.randomUUID().toString();
}
});
MessageListenerAdapter adapter=new MessageListenerAdapter(new MessageDelegate());
//adapter.setDefaultListenerMethod("customerMethod"); //可以修改消息委托类中的默认方法
container.setMessageListener(adapter);
return container;
}
在MessageDelegate
中类我们定义了一个handleMessage
的方法,这是消息接受的默认方法,这是MessageListenerAdapter中定义的默认方法public static final String ORIGINAL_DEFAULT_LISTENER_METHOD = "handleMessage";
,可以通过setDefaultListenerMethod自定义方法。
8、消息类型转化(MessageConverter)
AMQP支持消息自定义消息格式转化,我们只需要实现MessageConverter
接口,就可以把消息转化为我们需要的类型。
8.1. 定义一个把message转化为String的类型
public class TextMessageConvert implements MessageConverter {
/**
* Convert a Java object to a Message.
* @param o
* @param messageProperties
* @return
* @throws MessageConversionException
*/
@Override
public Message toMessage(Object o, MessageProperties messageProperties) throws MessageConversionException {
return new Message(o.toString().getBytes(),messageProperties);
}
/**
* Convert from a Message to a Java object.
* @param message
* @return
* @throws MessageConversionException
*/
@Override
public Object fromMessage(Message message) throws MessageConversionException {
String contentType = message.getMessageProperties().getContentType();
if (contentType!=null&&contentType.contains("text")){
return new String(message.getBody());
}
return message.getBody();
}
}
消息监听代码:
/**
* 消息监听
* @param connectionFactory
* @return
*/
@Bean
public SimpleMessageListenerContainer simpleMessageListenerContainer(ConnectionFactory connectionFactory){
SimpleMessageListenerContainer container=new SimpleMessageListenerContainer(connectionFactory);
//监听的消息队列,可以设置多个
container.setQueues(queue001(),queue002());
//消息签收模式
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
container.setConcurrentConsumers(1);//当前消费者数量
container.setMaxConcurrentConsumers(5);//最大消费者数量
container.setDefaultRequeueRejected(false);//消息失败,是否重回队列
//设置消费者的Tag唯一标识,AMQP封装了可以根据Tag进行消息的消费
container.setConsumerTagStrategy(new ConsumerTagStrategy() {
@Override
public String createConsumerTag(String s) {
return s+"_"+ UUID.randomUUID().toString();
}
});
MessageListenerAdapter adapter=new MessageListenerAdapter(new MessageDelegate());
//adapter.setDefaultListenerMethod("customerMethod"); //可以修改消息委托类中的默认方法
adapter.setMessageConverter(new TextMessageConvert());
container.setMessageListener(adapter);
return container;
消息监听委托类,由于使用了 adapter.setMessageConverter(new TextMessageConvert());
在handleMessage
方法中可以直接使用String类型接收。
```java
public class MessageDelegate {
/**
* 消息接受的默认方法,可以通过MessageListenerAdapter类的setDefaultListenerMethod修改
* @param messageBody
*/
public void handleMessage(String messageBody){
System.out.println("接收到的消息:"+messageBody);
}
}
发送消息的代码与上面的一样。运行代码测试一下吧!!!
8.2 发消息JSON格式的消息,转化为Map
- 新建一个实体类
Order
public class Order{
private String id;
private String title;
private String desc;
public Order() {
}
public Order(String id, String title, String desc) {
this.id = id;
this.title = title;
this.desc = desc;
}
..........省略get set
}
- 使用AMQP封装
Jackson2JsonMessageConverter
对消息进行转化
/**
* 消息监听
* @param connectionFactory
* @return
*/
@Bean
public SimpleMessageListenerContainer simpleMessageListenerContainer(ConnectionFactory connectionFactory){
SimpleMessageListenerContainer container=new SimpleMessageListenerContainer(connectionFactory);
//监听的消息队列,可以设置多个
container.setQueues(queue001(),queue002());
//消息签收模式
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
container.setConcurrentConsumers(1);//当前消费者数量
container.setMaxConcurrentConsumers(5);//最大消费者数量
container.setDefaultRequeueRejected(false);//消息失败,是否重回队列
//设置消费者的Tag唯一标识,AMQP封装了可以根据Tag进行消息的消费
container.setConsumerTagStrategy(new ConsumerTagStrategy() {
@Override
public String createConsumerTag(String s) {
return s+"_"+ UUID.randomUUID().toString();
}
});
MessageListenerAdapter adapter=new MessageListenerAdapter(new MessageDelegate());
// Jackson2JsonMessageConverter消息转化
Jackson2JsonMessageConverter jackson2JsonMessageConverter=new Jackson2JsonMessageConverter();
adapter.setMessageConverter(jackson2JsonMessageConverter);
container.setMessageListener(adapter);
return container;
- 消息监听委托类
/**
* JSON格式的消息 转化为Map
* @param messageBody
*/
public void handleMessage(Map messageBody){
System.out.println("JSON格式的消息 接收到的消息:"+messageBody.get("id")+":"+messageBody.get("title")+":"+messageBody.get("desc"));
}
- 消息发送
@Test
public void sendMsgTest2() throws JsonProcessingException {
Order order=new Order("1","订单title1","订单描述");
ObjectMapper objectMapper=new ObjectMapper();
String json = objectMapper.writeValueAsString(order);
MessageProperties messageProperties=new MessageProperties();
messageProperties.setContentType("application/json");
Message message=new Message(json.getBytes(),messageProperties);
// 当消息不可达时,执行ReturnListener回调
rabbitTemplate.setMandatory(true);
//消息的唯一主键,实际生产项目可根据业务逻辑设计
CorrelationData correlationData=new CorrelationData();
correlationData.setId(UUID.randomUUID().toString());
//发送消息
rabbitTemplate.convertSendAndReceive("topicExchange001", "spring.test", message,correlationData);
}
8.3 发消息JSON格式的消息,转化为实体对象
- AMQP提供了默认的
DefaultJackson2JavaTypeMapper
转化类,在上一步的基础上只需要设置Jackson2JsonMessageConverter
类的setJavaTypeMapper(Jackson2JavaTypeMapper javaTypeMapper)
方法。
PS:在转化之前需要添加实体类,所在包路径,DefaultJackson2JavaTypeMapper默认信任包名只有两个如下:
public class DefaultJackson2JavaTypeMapper extends AbstractJavaTypeMapper
implements Jackson2JavaTypeMapper, ClassMapper {
// 可以转化的类型
private static final List<String> TRUSTED_PACKAGES =
Arrays.asList(
"java.util",
"java.lang"
);
- 消息监听类
--------------------省略一些代码,与上面介绍的代码一致--------------------
MessageListenerAdapter adapter=new MessageListenerAdapter(new MessageDelegate());
Jackson2JsonMessageConverter jackson2JsonMessageConverter=new Jackson2JsonMessageConverter();
DefaultJackson2JavaTypeMapper javaTypeMapper=new DefaultJackson2JavaTypeMapper();
//添加信任包名称,不然会转化失败
javaTypeMapper.setTrustedPackages("com.warybee.mqspring.entity");
jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
adapter.setMessageConverter(jackson2JsonMessageConverter);
container.setMessageListener(adapter);
- 消息监听委托类
/**
* JSON格式的消息 转化为Java实体对象
* @param order
*/
public void handleMessage(Order order){
System.out.println("order 接收到的消息:"+order.getId()+"-"+order.getTitle()+"-"+order.getDesc());
}
- 发送消息
/**
* 发送JSON消息,转化为Java对象
* @throws JsonProcessingException
*/
@Test
public void sendMsgTest3() throws JsonProcessingException {
Order order=new Order("1","订单title2","订单描述");
ObjectMapper objectMapper=new ObjectMapper();
String json = objectMapper.writeValueAsString(order);
MessageProperties messageProperties=new MessageProperties();
messageProperties.setContentType("application/json");
Message message=new Message(json.getBytes(),messageProperties);
//发送消息
rabbitTemplate.convertSendAndReceive("topicExchange001", "spring.test", message);
}
本文示例代码下载:https://gitee.com/warybee/mqspring.git
RabbitMQ系列文章目录
1、RabbitMQ Windows/CentOS7平台安装手册
2、RabbitMQ中一些重要概念
3、RabbitMQ Exchange类型之Direct Exchange
4、RabbitMQ Exchange类型之Topic Exchange
5、RabbitMQ Exchange类型之fanout Exchange
6、RabbitMQ Exchange类型之headers Exchang
7、Confirm消息确认机制
8、RabbitMQ中ReturnListener的使用
9、RabbitMQ消费端限流
10、ACK确认机制与消息补偿
11、RabbitMQ队列/消息的生存时间(Time-To-Live)
12、RabbitMQ死信队列(Dead Letter Exchanges)
13、Spring AMQP API详解
14、Spring Boot整合RabbitMQ