我正在参加「启航计划」
咱们在运用mq
的时分,就会很自然思考一个问题:怎样确保数据不丢失?
现在austin
接入层是把音讯发到mq
,下发逻辑层从mq
消费数据,随后调用对应途径接口来下发音讯。

音讯推送途径推送下发【邮件】【短信】【微信服务号】【微信小程序】【企业微信】【钉钉】等音讯类型。
- gitee.com/zhongfuchen…
- github.com/ZhongFuChen…
音讯丢掉一般咱们考虑的是消费端,于是重点看的是下发逻辑层。
(由于对于mq
运用方来说:出产端只需装备mq
相关的参数,在调用下发时有回调重试机制。那就足够了,出产端能做的东西的确不多)
目前为止,下发逻辑层(消费端)运用的是主动提交offset战略。只需消费端存在体系重启或许进程被kill
掉,那就会有丢音讯的情况。
spring.kafka.consumer.enable-auto-commit=true
当时下发逻辑层(消费端)有或许放大了这个丢掉音讯的问题,由于现在是消费到mq
数据后,会把音讯给到线程池去处理。线程池会指定一个堵塞行列,那行列数量越大,或许由重启所丢掉的音讯就越多。

这儿我的战略是:当运用重启的时分,体系里的线程池是优雅封闭的(尽或许等候一段时刻,等堵塞行列里没有音讯了,再封闭线程池)。
但回到问题的本质上,只需消费端是主动提交offset战略,就一定会有丢音讯的问题。所以要做到消费端的音讯不丢,咱们就要设置为手动提交offset,这个是必要条件。
有没有必要确保不丢
在讨论详细的技能完成方案之前,咱们来看看在事务上有没有必要确保音讯不丢。我刚接触到音讯推送途径的时分,当时那个交接的哥们告诉我和我学长:音讯少发比多发要好。
1、重要的音讯用户很或许会手动重试触发。
austin
是一个发送各类途径音讯的途径,从我的经历来说,这儿面最重要的是短信途径。经过austin
下发很或许是登陆验证码,银行卡提现验证码,这类音讯从全局上看是最重要的。
而其他途径,例如push通知栏的通知音讯,微信途径的营销音讯,这种音讯即运用户没收到,也不会对用户带来很大的运用体会问题。这种音讯或许对绝大数用户都是无感知的(少发几条,用户或许更乐意)。
咱们先假设用户的某一次银行卡提现的验证码恰好由于咱们重启体系而丢掉。这时分,绝大数用户或许置疑自己的信号问题,会继续操作,重新发送一次。
(由于客服常常找我排查这种问题,每次都能看到有好几条下发记录。当然了,能到技能的,99%的问题都不是由体系重启丢失音讯导致的,更多或许是用户的客户端本身的确就存在问题)
2、音讯是有时效性的。比方验证码这种短信一般就5min的时效性,由于体系的问题,你超过这个时刻给用户发送,对用户的体会是非常差的。
3、音讯推送途径是有全链路追踪的,是能够知道下发的音讯有没有到达到用户手上,至少都能够知道在咱们的体系内部执行过程中有没有丢。假如这条音讯真的那么重要,那能够独自为丢掉的音讯独自做重发处理,这些功用在音讯推送途径都是支持的。

这个问题我曾经的搭档也跟我讨论过,便是把上面的内容给我近邻的老哥听的,他说:你就尽扯淡吧,到面试的时分人家可不认你,丢了便是丢了,其他都是托言。
我说:没事,要是不认的话,就把咱们处理订单那一套给他讲讲嘛,反正处理的思路都是一样的。
不过啊,广告订单逻辑处理又相对没那么杂乱,广告订单终究是以入数据库作为标准的,又能够接受一定的延迟,只需能确保处理完就行了。
要想client端消费数据不能丢,肯定是不能运用autoCommit的,所以有必要是手动提交的。
候选者:咱们这边是这样完成的:
候选者:一、从Kafka拉取音讯(一次批量拉取500条,这儿主要看装备)时
候选者:二、为每条拉取的音讯分配一个msgId(递加)
候选者:三、将msgId存入内存行列(sortSet)中
候选者:四、运用Map存储msgId与msg(有offset相关的信息)的映射联系,经过msgId用来获取相关元信息
候选者:五、当事务处理完音讯后,ack时,获取当时处理的音讯msgId,然后从sortSet删去该msgId(此时代表现已处理过了)
候选者:六、接着与sortSet行列(本地内存行列)的首部第一个Id比较(其实便是最小的msgId),假如当时msgId<=sort Set第一个ID,则提交当时offset
候选者:七、体系即使挂了,在下次重启时就会从sortSet队首的音讯开端拉取,完成至少处理一次语义
候选者:八、会有少数的音讯重复,但只需下游做好幂等就OK了。
面试官:嗯,你也提到了幂等,你们这事务怎样完成幂等性的呢?
候选者:嗯,还是以处理订单音讯为例好了。
候选者:幂等Key咱们由订单编号+订单状况所组成(一笔订单的状况只会处理一次)
候选者:在处理之前,咱们首先会去查Redis是否存在该Key,假如存在,则说明咱们现已处理过了,直接丢掉
候选者:假如Redis没处理过,则继续往下处理,终究的逻辑是将处理过的数据刺进到事务DB上,再到终究把幂等Key刺进到Redis上
候选者:显然,单纯经过Redis是无法确保幂等的(:
候选者:所以,Redis其实只是一个「前置」处理,终究的幂等性是依赖数据库的仅有Key来确保的(仅有Key实际上也是订单编号+状况)
候选者:总的来说,便是经过Redis做前置处理,DB仅有索引做终究确保来完成幂等性的
确保austin数据不丢需要做什么?
确保数据不丢简略来说,便是咱们要在消费端手动ack offset
,不能再用主动提交战略了。这样当咱们体系重启时,kafka
会主动从未ack
的offset
中拉取。
假如要完成音讯推送途径不丢音讯的话,有几个问题是需要考虑的:
1、音讯少发比多发要好,那么要完成音讯不丢,就有必要要在体系内完成幂等。由于现在的音讯不丢,一般都是基于【至少一次]消费语义去做的。
2、那完成幂等的逻辑是在调用途径下发接口前,还是途径下发接口后?
假如做在下发接口前,那是不是会有或许第一次下发记录写入了,但实际调用下发接口却失利了,后边的重试都被幂等处理掉了。
假如做在下发接口后,那是不是会有或许调用调用下发接口成功了,但写入幂等处理的音讯失利了,后边的重试就会导致音讯多发
3、音讯是有时效性的,那假如重试的处理时刻过长,那是不是要考虑把这条音讯给丢掉掉,不再重试了。
4、重试的音讯不应该影响到正常音讯的下发,他得作为一种补偿的机制,而非主流程。
稍微细想下技能完成,应该不太好搞,还有很多细节的地方得关注到。比方事务上的:应该是不需要一切的途径的一切类型音讯都得完成音讯不丢吧?现在的规划是寻求高性能的,能在短时刻内下发批量的音讯。而假如做到一切音讯不丢,肯定会影响到下发的速率。
什么时分动手?
1、对于这个功用吧,有用肯定是有用,但这功用又没那么急。
2、我估摸对现有代码改动还是蛮大的,现在我还没想好该怎样完成比较好,也一直没下手。
3、最近作业的事挺多的,没那么有空。
结论:先看看想要这个功用的人多不多,不多就鸽一会。
都看到这了,假如按上面的理由,我不完成这个功用,你认不认可?
音讯推送途径推送下发【邮件】【短信】【微信服务号】【微信小程序】【企业微信】【钉钉】等音讯类型。
- gitee.com/zhongfuchen…
- github.com/ZhongFuChen…