一、需求:
依据黑马商城 hmall 为例,用户下单创建订单后,交易服务 trade-service 向交换机 topic 发送消息,交换机 topic 路由到队列,购物车服务 cart-service 监听此队列,实现自动清空购物车。
改造下单功能,将基于OpenFeign的清理购物车同步调用,改为基于RabbitMQ的异步通知:
定义topic类型交换机,命名为
trade.topic
定义消息队列,命名为
cart.clear.queue
将
cart.clear.queue
与trade.topic
绑定,BindingKey
为order.create
下单成功时不再调用清理购物车接口,而是发送一条消息到
trade.topic
,发送消息的RoutingKey
为order.create
,消息内容是下单的具体商品、当前登录用户信息购物车服务监听
cart.clear.queue
队列,接收到消息后清理指定用户的购物车中的指定商品
二、思路及代码:
1. 发送消息时,使用匿名内部类实现 MessagePostProcessor 接口,将用户信息保存到消息头中。
// TODO 3.清理购物车商品
try {
rabbitTemplate.convertAndSend("trade.topic"
, "order.create"
, itemIds
, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
log.info("将用户信息写入消息头:{}",UserContext.getUser());
message.getMessageProperties().setHeader("user-info",UserContext.getUser());
return message;
}
});
} catch (AmqpException e) {
log.error("清理购物车失败:{}", itemIds, e);
2. 因为消息都要经过消息转换器 Jackson2JsonMessageConverter,所以在这里处理。重写 Jackson2JsonMessageConverter 中的 fromMessage 方法,将用户信息从消息头中取出,存入ThreadLocal
package com.hmall.common.config;
import cn.hutool.core.util.ObjectUtil;
import com.hmall.common.utils.CollUtils;
import com.hmall.common.utils.UserContext;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConversionException;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MqConfig {
/**
* 设置消息转换器
* @return
*/
/* @Bean
public MessageConverter messageConverter(){
return new Jackson2JsonMessageConverter();
}*/
@Bean
public MessageConverter messageConverter(){
return new Jackson2JsonMessageConverter(){
@Override
public Object fromMessage(Message message) throws MessageConversionException {
//判断消息不为空
if (ObjectUtil.isNotNull(message)){
//从消息头中取出用户信息
Long userId = message.getMessageProperties().getHeader("user-info");
//存入线程中
UserContext.setUser(userId);
}
return super.fromMessage(message);
}
};
}
}