回复
MQ解决分布式事务 原创
新一代程序猿
发布于 2022-1-22 00:37
浏览
1收藏
MQ解决什么问题?
要了解MQ的必要性,需要先了解一下微服务的产生与出现的问题。只有了解了问题产生的原因才能明白MQ的作用。
举个栗子:支付业务
在微服务的环境下,会将三个步骤拆分成三个服务,例如:支付服务,订单服务,物流服务。三者顾名思义只做好自己的一件事情。
情况会变复杂,同样以支付业务为例,三个步骤需要有服务间的调用,如下图,有两次调用产生,服务间调用一般有两种方式:restful 的http与RPC。这里不讨论两者的区别。
微服务化的好处:化整为零,单一服务制作单一的事情,可以防止单服务无线膨胀。无论从开发,测试,运维方面单个服务都是占优势的。
微服务带来的问题:
- 分布式事务
服务间调用难以保持一致性。例如上图中的两次调用。因为三个步骤操作的不是同一个数据库,导致无法使用jdbc事务管理以达到一致性。而且两次服务调用,因为涉及到复杂的网络环境,很容易出现,服务调用失败。所以,微服务化之后出现的一个很严重的问题就是,分布式事务问题如何解决。
解决分布式事务
MQ作为解决分布式事务的一种,是如何解决分布式事务问题的,如下图,支付服务完成支付步骤后,往MQ发送一条已支付消息,订单服务收到消息后更改订单状态。
MQ实战
- 初始化MQ队列
@Configuration
public class MyRabbitMQConfig {
/* 容器中的Queue、Exchange、Binding 会自动创建(在RabbitMQ)不存在的情况下 */
/**
* 死信队列
*
* @return
*/@Bean
public Queue orderDelayQueue() {
/*
Queue(String name, 队列名字
boolean durable, 是否持久化
boolean exclusive, 是否排他
boolean autoDelete, 是否自动删除
Map<String, Object> arguments) 属性
*/
HashMap<String, Object> arguments = new HashMap<>();
arguments.put("x-dead-letter-exchange", "order-event-exchange");
arguments.put("x-dead-letter-routing-key", "order.release.order");
arguments.put("x-message-ttl", 60000); // 消息过期时间 1分钟
Queue queue = new Queue("order.delay.queue", true, false, false, arguments);
return queue;
}
/**
* storage队列
*
* @return
*/
@Bean
public Queue orderToStorageQueue() {
Queue queue = new Queue("order.release.storage.queue", true, false, false);
return queue;
}
/**
* TopicExchange
*
* @return
*/
@Bean
public Exchange orderEventExchange() {
/*
* String name,
* boolean durable,
* boolean autoDelete,
* Map<String, Object> arguments
* */
return new TopicExchange("order-event-exchange", true, false);
}
@Bean
public Binding orderCreateBinding() {
/*
* String destination, 目的地(队列名或者交换机名字)
* DestinationType destinationType, 目的地类型(Queue、Exhcange)
* String exchange,
* String routingKey,
* Map<String, Object> arguments
* */
return new Binding("order.delay.queue",
Binding.DestinationType.QUEUE,
"order-event-exchange",
"order.create.order",
null);
}
@Bean
public Binding orderReleaseStorageBinding() {
return new Binding("order.release.storage.queue",
Binding.DestinationType.QUEUE,
"order-event-exchange",
"order.release.order",
null);
}
}
- 编写下单接口创建订单并锁定库存,通过发送消息给mq的等待队列
@Override
@Transactional(rollbackFor = Exception.class)
public void create(Orders order) {
log.info("------->交易开始");
//本地方法
order.setStatus(Orderstatus.NOPAYMENT);
super.save(order);
//远程方法 锁定库存
storageApi.decrease(order.getProductId(),order.getCount());
log.info("订单号:"+order.getId());
log.info("------->发送mq");
rabbitTemplate.convertAndSend("order-event-exchange", "order.create.order", order);
}
- StorageReleaseListener监听等待队列(根据实际情况设置等待时间)判断队列是否支付,如果没有支付关闭订单并回滚库存
@Slf4j
@Service
public class StorageReleaseListener {
@Autowired
private orderApi orderApi;
@Autowired
private IStorageService storageService;
@RabbitListener(queues = "order.release.storage.queue")
public void handleStockLockedRelease(Orders to, Channel channel, Message message) throws IOException {
log.info("******order.release.account.queue---》收到回滚库存的信息******");
try {
Orders orders = orderApi.GetByOrders(to.getId().longValue());
if("0".equals(orders.getStatus())){
storageService.rollbackStorage(orders.getProductId(),orders.getCount());
}
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
} catch (Exception e) {
e.printStackTrace();
}
}
}
©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
赞
1
收藏 1
回复
相关推荐