模拟外卖案例,用户下单之后,调用订单服务,然后订单服务间消息发给派送服务通知外卖人员送餐,订单系统与派单系统采用MQ异步通讯。
配置交换机,可以配置多个
package com.gpdi.order.config;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author Lxq
* @Date 2020/3/7 13:59
* @Version 1.0
* 消息交换机配置,可以配置多个
*/
@Configuration
public class ExchangeConfig {
private final String EXCHANGE_ORDER_DISPATCH = "order-dispatch";
@Bean
public DirectExchange directExchange() {
DirectExchange directExchange = new DirectExchange(EXCHANGE_ORDER_DISPATCH, true, false);
return directExchange;
}
}
配置队列信息
package com.gpdi.order.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* @Author Lxq
* @Date 2020/3/7 14:06
* @Version 1.0
* 配置队列,这里只使用第一个
*/
@Configuration
public class QueueConfig {
/*对列名称*/
public static final String QUEUE_NAME1 = "dispatch-queue";
public static final String QUEUE_NAME2 = "second-queue";
public static final String QUEUE_NAME3 = "third-queue";
@Bean
public Queue DispatchQueue() {
/**
durable="true" 持久化消息队列 , rabbitmq重启的时候不需要创建新的队列
auto-delete 表示消息队列没有在使用时将被自动删除 默认是false
exclusive 表示该消息队列是否只在当前connection生效,默认是false
*/
return new Queue(QUEUE_NAME1,true,false,false);
}
@Bean
public Queue SecondQueue() {
return new Queue(QUEUE_NAME2,true,false,false);
}
@Bean
public Queue ThirdQueue() {
// 配置 自动删除
Map arguments = new HashMap<>();
arguments.put("x-message-ttl", 60000);//60秒自动删除
return new Queue(QUEUE_NAME3,true,false,true,arguments);
}
}
将配置的交换机和队列进行绑定
package com.gpdi.order.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author Lxq
* @Date 2020/3/7 14:10
* @Version 1.0
* 将交换机和队列进行绑定
*/
@Configuration
public class RabbitMqConfig {
/**
* key: queue在该direct-exchange中的key值,当消息发送给direct-exchange中指定key为设置值时,
* 消息将会转发给queue参数指定的消息队列
*/
/** 队列key*/
public static final String ROUTING_KEY_ORDER_DISPATCH = "order-dispatch-key";
@Autowired
private QueueConfig queueConfig;
@Autowired
private ExchangeConfig exchangeConfig;
/**
* 将消息队列和交换机进行绑定,指定队列ROUTING_KEY_ORDER_DISPATCH
*/
@Bean
public Binding bindingDispatch(){
return BindingBuilder.bind(queueConfig.DispatchQueue())
.to(exchangeConfig.directExchange())
.with(ROUTING_KEY_ORDER_DISPATCH);
}
}
新建两个表,一个订单表(存放下单信息),一个本地信息表(关联订单表,存放订单状态,是否发送给mq)
DROP TABLE IF EXISTS `tb_order`;
CREATE TABLE `tb_order` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单编号',
`user_id` int(11) DEFAULT NULL COMMENT '用户编号',
`order_content` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '订单内容',
`create_time` datetime(0) DEFAULT NULL COMMENT '订单时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 22 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '订单表' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
DROP TABLE IF EXISTS `tb_message`;
CREATE TABLE `tb_message` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '消息编号',
`msg_content` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '消息内容',
`msg_status` int(255) DEFAULT NULL COMMENT '消息状态',
`create_time` datetime(0) DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 22 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '本地消息表' ROW_FORMAT = Dynamic;
订单核心代码
package com.gpdi.order.service;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gpdi.order.dao.MessageMapper;
import com.gpdi.order.dao.OrderMapper;
import com.gpdi.order.domain.Message;
import com.gpdi.order.domain.Order;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Author Lxq
* @Date 2020/3/7 15:56
* @Version 1.0
*/
@Service
@Transactional(rollbackFor = Exception.class)
public class OrderServiceImpl implements OrderService {
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private OrderMapper orderMapper;
@Autowired
private MessageMapper messageMapper;
@PostConstruct
public void setUP() {
// Mq的确认回执
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(@Nullable CorrelationData correlationData, boolean ack, @Nullable String cause) {
if (!ack) {
//mq没有接收到消息
//ToDo 忽略或者重新发送消息
return;
}
// 修改本地消息表
int i = messageMapper.updateById(Long.valueOf(correlationData.getId()));
if (i > 0){
System.out.println("消息发送成功");
}
}
});
}
@Override
public void createOrder(String order) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Order content = new Order();
content.setUserId(1);
content.setOrderContent(order);
content.setCreateTime(sdf.format(new Date()));
orderMapper.insert(content);
System.out.println(content.getId());
//存放到本地消息表
Message message = new Message();
message.setId(content.getId());
message.setMsgContent(content.getOrderContent());
message.setMsgStatus(0);
message.setCreateTime(sdf.format(new Date()));
messageMapper.insert(message);
sendMessage(message);
}
@Override
public void sendMessage(Message message) {
rabbitTemplate.convertAndSend("order-dispatch", "order-dispatch-key", JSONObject.toJSONString(message),
new CorrelationData(String.valueOf(message.getId())));
}
}
派送订单核心代码
package com.gpdi.dispatch.service;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @Author Lxq
* @Date 2020/3/7 17:43
* @Version 1.0
* 消费者
*/
@Component
public class OrderDispatchConsumer {
@RabbitListener(queues = "dispatch-queue")
public void messageConsumer(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
try {
// 收到MQ消息
System.out.println(message);
// 插入到派送单中
//todo
// 给MQ回执
channel.basicAck(tag,false);
} catch (Exception e) {
// 重新发一次
channel.basicNack(tag, false, true);
}
}
}
直接下载源码:
链接:https://pan.baidu.com/s/1tR94adXviEFLEmQZ6G4iDw
提取码:lnbq
复制这段内容后打开百度网盘手机App,操作更方便哦