消息总线架构
什么时候使用MQ:跨进程通信传递消息;解耦;如果调用方实时依赖执行结果就不适用;加入MQ系统更复杂,传递路径更长,消息不丢不重难以同时保证
数据驱动的任务依赖:cron人工排执行时间表:这个似不似有点傻; 用MQ,step1完了发个消息说完了,task2订阅收到step1完了就开始,以此类推;
上游不关心执行结果:调用(发帖后调用其他相关业务),这个似乎也没有,现在不都MQ了吗,帖子发成功给MQ一个消息,下游订阅;
上游关注执行结果,但执行时间很长:跨公网调用、离线处理,执行结果很长,可以用回调网关+MQ来解耦; 调用方直接跨公网调用微信接口,微信返回调用成功,此时并不代表返回成功,微信执行完成后,回调统一网关,网关将返回结果通知MQ,请求方收到结果通知
=======================================================================================
MQ实现延迟消息: 比如48小时后自动评价
常见方案:启动一个cron定时任务,每小时跑一次,将完成时间超过48小时的订单取出,置为5星,并把评价状态置为已评价
select oid from t_order where finish_time > 48hours and status=0;
update t_order set stars=5 and status=1 where oid in[…];
如果数据量大,需要分页查询分页更新,所以轮询效率低,已经执行过的还会再被扫,时效性不够好
好的方案:
环形队列,本质是个数组,环上每个slot是一个Set<Task>,同时启动一个timer每隔1s,在环形队列中移动一格,有一个Current Index指针来标志正在检测的slot
Task结构中包含两个重要属性,cycle-num:当current index第几圈扫到这个slot时,执行任务;task-function:需要执行的任务指针
假设环3600个slot,每秒扫描一个slot,需要3610秒后执行的延时消息到达后,发现当前index指向1,经过计算,应该存在11格子并且应该在一圈之后执行,每秒移动到一个新的slot,看set<task>中每个task的cycle-num是不是0,如果不是0,则经过减1,如果是0,则执行并删除该task
此方案好处是无需轮询全部订单,效率高,一个订单任务只执行一次,时效性好
=======================================================================================
消息的必达:落地 + 消息超时、重传、确认
发送方 -> MQ -> 接收方
发送,MQ服务器落地消息,应答成功; MQ服务器发送消息给订阅端,订阅端回复服务器,服务器收到确认,将落地消息删除,完成一次可靠的投递
=======================================================================================
幂等
对于服务端对客户端的确认丢失,导致客户端不断重发,避免服务端收到消息后重复落地,需要对每条消息生成全局唯一并且业务无关的inner-msg-id,由MQ生成并且业务无关,作为去重和幂等的依据
客户端ack消费到服务端未成功,服务端多次推送消息给消费端,解决方法是业务消息体系中有一个Biz-id,作为去重和幂等依据,对同一个业务场景,全局唯一,由业务消息发送方生成,业务相关,对MQ透明,由业务消息消费方负责判重,保证幂等