Spring Boot集成RabbitMQ。如何在Spring Boot中使用RabbitMQ,超详细说明。

如何在Spring中使用RabbitMQ?

RabbitMQ相关概念介绍

RabbitMQ示意图。
在这里插入图片描述

当消息抵达RabbitMQ代理时,它会进入为其设置的Exchange上。Exchange负责将它路由到一个或多个队列中,这个过程会根据Exchange的类型,Exchange和队列之间的binding以及消息的routing key进行路由。

有多种不同的Exchange

  • Default:它会将消息路由至名字与消息routing key相同的队列,所有的队列都会自动绑定至Default Exchange

  • Direct:如果消息的routing key与队列的binging key相同,那么消息就会路由到该队列上

  • Topic:如果消息的routing key与队列的binging key(可能包含通配符)匹配,那么消息会被路由到一个或多个这样的队列上

  • Fanout:不管routing key和binging key是什么,消息都将会路由到所有绑定队列上。

  • Headers:与Topic Exchange类似,只不过要基于消息的头消息进行路由,而不是routing key。

  • Dead letter:捕获所有无法投递(也就是无法匹配所有已定义的Exchange和队列的binging关系)的消息

添加依赖

添加AMQP starter到构建文件中以后,将会触发Spring Boot的自动配置功能,这样会为我们创建一个AMQP连接工厂和RabbitTemplate Bean

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

在使用RabbitMQ的时候,我们需要关注RabbitMQ的相关属性配置

RabbitMQ相关属性配置

属性描述
spring.rabbitmq.addresses一个逗号分隔的 RabbitMQ Broker 地址列表
spring.rabbitmq.hostBroker 主机(默认为 localhost)
spring.rabbitmq.portBroker 端口(默认为 5672)
spring.rabbitmq.username访问 Broker 的用户名(可选)
spring.rabbitmq.password访问 Broker 的密码(可选)

在本机运行时这些属性是没有太大用处的,本机默认会监听5672端口。

部署到生产环境时,可以这么配置

spring:
  profiles: prod
  rabbitmq:
    host: rabbit.tacocloud.com
    port: 5673
    username: user
    password: password

默认的exchangerouting-key都是""(空字符串),可以进行相应的配置来进行设置。

spring:
  rabbitmq:
    template:
      exchange: tacocloud.orders
      routing-key: kitchens.central

使用RabbitMQ发送消息

RabbitTemplate中发送消息的方法

Spring对RabbitMQ的支持的核心是RabbitTemplateRabbitTemplateJmsTemplate类似

发送消息时主要使用两类方法send(...)sendAndConvert(...)

// 发送原始消息
//routingKey就相当于是指定了消息发送的目的地址一样
void send(Message message) throws AmqpException;
void send(String routingKey, Message message) throws AmqpException;
//若未指明消息发送的Exchange则会发送到默认的Exchange,默认的Exchange可以到配置文件中进行设置
void send(String exchange, String routingKey, Message message) throws AmqpException;

// 发送从对象转换过来的消息
void convertAndSend(Object message) throws AmqpException;
void convertAndSend(String routingKey, Object message) throws AmqpException;
void convertAndSend(String exchange, String routingKey, Object message) throws AmqpException;

// 发送经过处理后从对象转换过来的消息,并且再通过MessagePostProcessor添加对消息的处理
void convertAndSend(Object message, MessagePostProcessor mPP) throws AmqpException;
void convertAndSend(String routingKey, Object message, MessagePostProcessor messagePostProcessor) throws AmqpException;
void convertAndSend(String exchange, String routingKey, Object message, MessagePostProcessor messagePostProcessor) throws AmqpException;

如下的代码使用send(String routingKey, Message message)方法来发送消息,该send方法传入的参数是Message对象,默认使用的是消息转换器是SimpleMessageConverter(消息转换器用来将要发送的对象与Message对象进行转化,不管我们传入的参数是什么,最后都会转化为Message对象来进行传输)。由于需要将JSON和Message进行转换,所以下面将会讲解如何使用Jackson2JsonMessageConverter

private RabbitTemplate rabbit;

@Autowired
public RabbitOrderMessagingService(RabbitTemplate rabbit) {
    this.rabbit = rabbit;
}

@Override
public void sendOrder(Order order) {
    //org.springframework.amqp.support.converter.MessageConverter下的MessageConverter
    MessageConverter converter = rabbit.getMessageConverter();
    //org.springframework.amqp.core.MessageProperties下的MessageProperties
    MessageProperties props = new MessageProperties();
    Message message = converter.toMessage(order, props);
    rabbit.send("tacocloud.order.queue", message);
}

配置消息转换器

转化JSON和Message使用Jackson2JsonMessageConverter

只需要配置一个Jackson2JsonMessageConverter的Bean即可得到一个MessageConverter的Bean

@Bean
public Jackson2JsonMessageConverter messageConverter() {
    return new Jackson2JsonMessageConverter();
}

最终会发现Jackson2JsonMessageConverter继承自MessageConverter,所以才会得到一个MessageConverter的bean

public class Jackson2JsonMessageConverter extends AbstractJackson2MessageConverter {
	//......
}
public abstract class AbstractJackson2MessageConverter extends AbstractMessageConverter
		implements BeanClassLoaderAware, SmartMessageConverter {
	//......
}
public abstract class AbstractMessageConverter implements MessageConverter {
	//......
}

在这里插入图片描述

RabbitTemplate中默认是有一个MessageConverter转换器的,不过是默认使用的是SimpleMessageConverter

private MessageConverter messageConverter = new SimpleMessageConverter();

如果我们需要替代这个MessageConverter,只需要像上面显示声明一个MessageConverter类型的的Bean即可。

设置消息属性

如果我们想要对发送的消息额外增加相应的属性,则应该使用如下的方法来发送消息。

使用void convertAndSend(String exchange, String routingKey, Object message, MessagePostProcessor messagePostProcessor)发送消息并添加对消息的处理 。

postProcessor中,从Message中获得MessageProperties对象,然后通过setHeader方法设置"X_ORDER_SOURCE"头信息。

public void sendOrder(Order order) {
    rabbit.convertAndSend("tacocloud.order.queue", order,
        new MessagePostProcessor() {
          @Override
          public Message postProcessMessage(Message message)
              throws AmqpException {
            MessageProperties props = message.getMessageProperties();
            props.setHeader("X_ORDER_SOURCE", "WEB");//为消息增加一个X_ORDER_SOURCE字段
            return message;
          } 
        });
  }

接收来自RabbitMQ的消息

接收来自RabbitMQ的消息与使用JMS也很相似,有两种方案:

  1. 使用RabbitTemplate从队列拉取消息。采用该模式,在代码中请求消息时该线程会一直阻塞在那里,直到等待着消息到达为止。
  2. 将消息推送至带有@RabbitListener注解的方法。采用这种方式时,线程不会一直阻塞在原地。而是在消息到达时开始执行相应的处理逻辑,在消息没有到达时该线程可以执行其它任务,这样就不会造成线程阻塞。

RabbitTemplate中接收消息的方法

接收消息时主要使用如下方法

// 接收消息
Message receive() throws AmqpException;
Message receive(String queueName) throws AmqpException;
Message receive(long timeoutMillis) throws AmqpException;
//long类型的参数用来指定接收消息的超时时间,默认情况下接收消息的超时时间为0毫秒,也就是说调用receive方法就会立即返回结果,如果没有可用消息,则返回null。
Message receive(String queueName, long timeoutMillis) throws AmqpException;

// 接收从消息转换过来的对象
Object receiveAndConvert() throws AmqpException;
Object receiveAndConvert(String queueName) throws AmqpException;
Object receiveAndConvert(long timeoutMillis) throws AmqpException;
Object receiveAndConvert(String queueName, long timeoutMillis) throws AmqpException;

// 接收从消息转换过来的类型安全的对象
<T> T receiveAndConvert(ParameterizedTypeReference<T> type) throws AmqpException;
<T> T receiveAndConvert(String queueName, ParameterizedTypeReference<T> type) throws AmqpException;
<T> T receiveAndConvert(long timeoutMillis, ParameterizedTypeReference<T> type) throws AmqpException;
<T> T receiveAndConvert(String queueName, long timeoutMillis, ParameterizedTypeReference<T> type) throws AmqpException;

receive(...)方法对应着send(…)方法,用于接收来自队列的原始Message对象,并返回Message对象

receiveAndConvert(...)对应着sendAndConvert(…)方法,用于接收Message对象,并返回一个Object。

示例:

使用Message receive(String queueName, long timeoutMillis)方法接收消息

public Order receiveOrder() {
    MessageConverter converter = rabbit.getMessageConverter();
    Message message = rabbit.receive("tacocloud.order.queue", 20000);
    return message != null
        ? (Order) converter.fromMessage(message)
        : null;
}

使用Object receiveAndConvert(String queueName)方法接收消息

public Order receiveOrder() {
    return (Order) rabbit.receiveAndConvert("tacocloud.order.queue");
}

使用<T> T receiveAndConvert(String queueName, long timeoutMillis, ParameterizedTypeReference<T> type)接收消息

public Order receiveOrder() {
    return rabbit.receiveAndConvert("tacocloud.order.queue", new ParameterizedTypeReference<Order>() {});
}

使用 receiveAndConvert() 的ParameterizedTypeReference的惟一要求是消息转换器必须是 SmartMessageConverter 的实现;Jackson2JsonMessageConverter 是唯一可以选择的开箱即用的实现。因此如果采用这种方法得声明一个Jackson2JsonMessageConverter的Bean。

使用监听器来处理RabbitMQ的消息

@RabbitListener@JmsLinstener及其类似,不过一个是用来处理来自RabbitMQ代理的消息,一个是处理来自JMS代理的消息。两者都是被动地接收消息,并不是主动地去请求消息。

使用@RabbitListener注解可以实现消息驱动的RabbitMQ的bean。当消息到达队列时,声明了该注解的方法就会被自动调用。声明了该注解的方法不会被你的应用程序显示调用,当消息到达时,Spring会自动调用该方法,并会将消息中传递的对象作为参数。

比如说我在send(…)方法中传递的是一个Order对象

public void sendOrder(Order order) {
    rabbit.convertAndSend("tacocloud.order.queue", order,
                          new MessagePostProcessor() {
                              @Override
                              public Message postProcessMessage(Message message)
                                  throws AmqpException {
                                  MessageProperties props = message.getMessageProperties();
                                  props.setHeader("X_ORDER_SOURCE", "WEB");
                                  return message;
                              }
                          });
}

那么使用@RabbitListener接收时就可以这么接收。

@Profile("rabbitmq-listener")
@Component
public class OrderListener {
  //......
  @RabbitListener(queues = "tacocloud.order.queue")
  public void receiveOrder(Order order) {
    //do sth
  }
  //......
}

参考
《Spring 实战》第五版,作者:Craig Walls

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值