大佬们,你们公司普通业务里 mq 有做消息可靠性吗?怎么做的
- 0次
- 1975-02-23 00:43:44
- idczone
普通业务 比如 修改内容后发送 mq 同步到 e美国服务器s 。
经历的公司里都不管消息是否发送成功,失败就失败了
那是很明显数据不重要, 丢一些没关系.
一般这种如果要求一定成功会用失败补偿 /延迟校验 /双通道发送之类的方法减少数据丢失的可能.
纳尼? rabbitmq 有持久化啊
写入重试,打好日志。
再写一个分时段同步 db 数据到 es 的工具。
MQ 出问题了直接调接口补数据。
是发送的时候,异步发送
怎么衡量重不重要呢?比如一些数据同步到 es 失败就搜索不到,感觉也挺重要
指标定义可以用是否会产生资金损失 /是否会产生客诉 /是否会对目标用户产生负面影响等方面评估, 然后做这些容灾也是需要花钱的, ROI 就得你们自己评估了. 是丢了这些数据造成的损失大, 还是做这些容灾手段付出的成本大.
有做的,订单类业务 mq 丢失后续业务流程全部挂了 ,说说我用的方案吧
rocketmq 用的比较多,拿 rocketmq 来说吧
1. producer client 设置自动重试 3 次,注意发送重试是立即重试(循环),默认 timeOut 是 3 秒
2. producer.send(Message msg,SendCallback sendCallback) 这种是异步发送,但是有 callback 业务可以实时感知发送结果
2. callback 记录日志 ,并捕捉 callback 异常消息持久化至 db(拼接好完整的重试消息 body)
3. 定时器每隔 5 秒扫描一遍待重试的消息,超过最大次数(3)则发送至告警平台人工介入
还有一种方案是 rmq 的事务消息 , 落库前发送一条事务消息 , rmq 会自动来询问注册的回调 listener 消息是否可以发送 , 事务状态一直是 waitting 的话 rmq 会周期性的来回调 listener ,只是时间周期是固定的
一般涉及到消息发送都基本上是异步流程了,建议在一个本地事务中将需要发送的消息写入到一个“消息发送表”内,另一个线程定时扫描这张表,然后将消息发送出去,发送完成就可以删掉或标记为已处理,发送失败记录失败次数和异常等原因。
2. 后面两步,是不是可以简化成:只记录日志,日志里包含重试消息的 body,然后用钉钉告警出来呢?
可以的 , 无非是自动与手动的区别.
我的场景是电商订单 , 尽可能的希望这种异常场景程序能自愈而不是直接人工介入
同步失败重做啊
上市公司项目,MQ 都只用来发短信、微信。服务调用基本都是同步。这样都能活得下出,消息可不可靠不影响,哈哈
mq 不能保证其可靠性,但是这消息又很重要,那么就应该实现分布式事务。
一步出错就回滚,日志记录错误。然后慢慢修正错误提高性能
消息重试了解下
只要的肯定加确认和重试 都失败进入日志 后期处理
死信队列用起来
我觉得这个问题应该分成三个,一个是发送者发送到消息队列可不可靠,一个是消息队列发送给接收者可不可靠,第三个是接收者接收了之后处理的可不可靠。第二个问题不用担心,可以简单相信消息队列保证了“至少一次”。第一个问题简单解决就是发送者先写一个数据库,然后一个线程发,或者发送者弄成全异步的,通过 Task 来 track 一条消息。第三个问题就直接接收者全处理完了再给 mq 回 ack,或者接收者先写个 db 回个 ack 再慢慢处理。这个问题的重点在于不要让消息 lose track,一直能够 track 到就不怕细小的问题,因为随时可以有人来查看恢复。比如,发送流程都很好,接收者成功收到以后挂了,然后消息丢了就不好办了。mq 是一个异步和解耦组件,异步了就要在发送者和接收者都保存一些信息。同步状态下只发送端保存,接收端挂了就挂了,发送端保存了还知道接收者处理哪条消息的时候挂了,于是重来就行了。多说一句,其实点对点的发送接收做异步的话也不一定非得用 mq 啦,可以学异步 rpc 那样发 promise 。发送端发个 promise 就走,接收端把 promise 放到监听对列。发送端啥时候闲了,或者 promise 太多了,或者其他线程,去发 promise 的实际内容,接收端等哪个 promise 好了就处理好的那个。
发出去了发现最后那个少了一点,要求接收端给回应的话,就是两边互发 promise 。发送端发个 promise,接收端收到以后回个结果的 promise,然后两边异步等 promise,balanala 。
@v2exblog
不好意思,错误地 @了
说到补偿..我想知道..有没有一种可能(其实就是业务 Bug).就是 mq 端认为消息已达,但实际上数据库并没有变更...这种如何发现?(我听闻大公司有类似数据校验组?那么是不是类似这种去做的?
先入库,状态为待处理,生产者发送消息,消费者处理消息,消费者远程调用更新状态为已完成。定时任务查询未完成记录,并再次发送消息。消费者做幂等处理。
同步写消息表,异步读取 消息表 binlog,做消息消费。
我们都是事务消息
生产者 -> 超时重传,最大重试次数
消息队列 -> 多点节,多副本,高可用
消费者 -> ACK,使用唯一 ID,另外是消息幂等性
durable
retry
confirm
mannual ack
db
batch job
26 楼加 1,生产者:重试加 ack,broker:多副本,消费者:db 持久化位移之后提交位移,如果是写 es 的话只保证不丢,不保证不重复,如果消费者的任务是写 mysql 的话,和位移的持久化放到一个事务,可以保证恰好一次
把 mq 当作一个服务,调用失败的话就报错呗,
mq 有死信队列
消费者发送 ack 。
够用了
这样不行的, 调用成功并不是代表真的成功了. 死信队列局限性很大的, 大多数网络异常, 死信队列都无能为力. 最简单的, 你先死循环发数据, 然后本地网络适配器里禁用网络再启用网络, 你会发现禁用瞬间, rabbitmq 并不能立即发现网络失效, 数据能照常发送. 要过一段时间, 通常是十几秒到几十秒, rabbitmq 才发现网络失效抛出异常. 那么这个十几秒到几十秒之间发出去的数据, rabbitmq 客户端认为发出去了, 而服务器并不会收到, 如何进死信队列?
对 rabbitmq 来说, 只有服务端 ack 了的才算成功了, 服务 nack 的算发送失败,
还有一种情况是, 长时间既未收到服务端 ack 信号又未收到服务端 nack 信号, 这种数据有可能发送成功了, 也有可能发送失败了.
如果要求一条不丢, 那么在收到服务器 ack 信号时标记这条数据发出去了. 再新起一个守护线程, 定期将已发送但尚未未标记发送成功的数据重发一次.
一句话总结
重试+重试队列+mq 持久化+ack+confirm+幂等性+打 log+定时补偿
发出去的同时往 db 存一个