第一篇:为什么需要消息队列?

第一篇:为什么需要消息队列?
为什么需要消息队列上一篇我们聊完了 Redis 系列的最后一篇《Redis 为什么不能当数据库》。Redis 解决的是一个问题如何让系统访问数据更快。但是当系统继续发展流量继续增加只靠 Redis 还不够。因为很多时候系统慢并不是因为数据库慢也不是因为缓存慢而是因为一次请求需要同步完成太多事情。这也是消息队列Message QueueMQ出现的原因。一个最开始的订单系统刚开始开发一个电商系统下单逻辑可能非常简单。PostMapping(/order)publicLongcreateOrder(OrderRequestrequest){OrderorderorderService.create(request);returnorder.getId();}用户提交订单保存订单。返回结果没有任何问题。但是随着业务发展订单创建之后需要做的事情越来越多。比如扣减库存发送短信发放优惠券增加积分更新会员等级通知物流系统写入数据分析平台于是代码慢慢变成PostMapping(/order)publicLongcreateOrder(OrderRequestrequest){OrderorderorderService.create(request);stockService.reduce(order);couponService.send(order);pointService.add(order);smsService.send(order);logisticsService.notify(order);returnorder.getId();}从业务角度看这段代码没有错。订单创建之后这些事情确实都需要做。但是从系统设计角度它开始出现问题。第一个问题接口越来越慢假设每个服务耗时创建订单 20ms扣库存 30ms优惠券 50ms积分 20ms短信 500ms物流通知 200ms最终接口耗时20 30 50 20 500 200 820ms用户点击一次下单需要等待接近 1 秒。但是仔细想一下用户真的需要等待短信发送完成吗需要等待积分增加完成吗需要等待物流系统收到通知吗其实不需要。用户真正关心的是我的订单有没有创建成功。其他事情可以稍后完成。第二个问题服务之间越来越耦合更麻烦的是订单服务现在知道太多东西。它知道订单服务↓库存服务↓短信服务↓优惠券服务↓积分服务↓物流服务如果以后新增一个需求“下单后发送邮件”怎么办继续修改emailService.send(order);如果邮件系统异常创建订单成功↓发送邮件失败整个接口怎么办返回失败但是订单已经创建了这就出现了一个很经典的问题非核心业务失败影响核心业务。那能不能让订单服务只负责订单重新思考一下订单服务真正需要做什么其实只有创建订单告诉其他系统“订单创建成功了”至于谁需要这个消息怎么处理什么时候处理订单服务不应该关心。于是架构变成用户请求↓订单服务↓发送消息↓消息队列↓其他服务消费代码变成PostMapping(/order)publicLongcreateOrder(OrderRequestrequest){OrderorderorderService.create(request);rocketMQTemplate.send(order-created,order.getId());returnorder.getId();}订单服务只负责发送“订单创建成功” MQ 到底做了什么很多人理解 MQMQ 就是帮我存一条消息这个理解不完整。真正重要的是MQ 在系统之间增加了一层缓冲。以前订单服务↓短信服务订单服务必须等待短信服务完成。现在订单服务↓Broker↓短信服务订单服务只需要把消息交给 Broker后面的事情异步完成。这就是消息队列最核心的价值。那为什么不用 HTTP 调用既然服务之间可以 HTTP 调用为什么还需要 MQ比如smsService.send(order);换成POST /sms/send 不也可以吗区别在于HTTP 是主动调用。调用方必须知道谁提供服务地址是什么接口是什么对方是否成功。MQ 是消息通知。生产者只关心消息有没有发送出去。消费者只关心有没有自己感兴趣的消息。两者的关系完全不同。MQ 底层为什么需要 Broker这里其实已经涉及 MQ 的核心设计。很多初学者会想既然订单服务要通知短信服务。为什么不直接订单服务↓短信服务而要增加订单服务↓Broker↓短信服务中间这个 Broker 就是 MQ 的核心。它解决三个问题消息暂存如果短信服务挂了订单服务↓Broker↓短信服务异常消息不会消失等短信服务恢复后继续消费。消费速度不同订单创建每秒 10000 次。短信发送每秒只能处理 1000 次。如果直接调用订单服务也会被拖慢。有了 MQ10000 条消息↓Broker↓消费者慢慢处理生产和消费速度被隔离。多个消费者订阅同一个订单消息订单创建事件↓Broker/ |库存 积分 短信不同系统消费自己关心的数据订单服务不需要知道它们存在。RocketMQ 中消息到底怎么流转以 RocketMQ 为例。一次消息发送并不是Producer↓Consumer而是Producer↓NameServer↓Broker↓CommitLog↓ConsumerProducer 发送消息Broker 保存消息。Consumer 从 Broker 拉取消息。后面的文章我们会继续拆为什么消息需要 BrokerBroker 为什么选择 CommitLog为什么 RocketMQ 写消息这么快消息为什么不会丢消息为什么会重复消费这些才是 MQ 真正有意思的地方。总结消息队列出现不是因为开发者喜欢增加中间件。而是因为系统发展到一定阶段后简单的同步调用已经无法满足需求。当一个接口需要同时通知几十个系统时同步调用会让系统越来越慢服务依赖会越来越复杂。任何一个下游异常都可能影响核心流程。MQ 做的事情本质上就是把一次强依赖的同步调用变成一次可靠的消息通知。它让系统从你必须马上帮我完成变成我告诉你发生了什么你什么时候处理由你决定这就是消息队列存在的意义。上一篇《Redis 为什么不能当数据库》下一篇《消息为什么能够解耦系统》