前言
我的上家公司是做餐饮系统的,每天中午和晚上用餐高峰期,系统的并发量不容小觑。为了保险起见,公司规定各部门都要在吃饭的时间轮流值班,防止出现线上问题时能够及时处理。
我当时在后厨显示系统团队,该系统属于订单的下游业务。用户点完菜下单后,订单系统会通过发 kafka 消息给我们系统,系统读取消息后,做业务逻辑处理,持久化订单和菜品数据,然后展示到划菜客户端。这样厨师就知道哪个订单要做哪些菜,有些菜做好了,就可以通过该系统出菜。系统自动通知服务员上菜,如果服务员上完菜,修改菜品上菜状态,用户就知道哪些菜已经上了,哪些还没有上。这个系统可以大大提高后厨到用户的效率。
事实证明,这一切的关键是消息中间件: kafka ,如果它有问题,将会直接影响到后厨显示系统的功能。
接下来,我跟大家一起聊聊使用 kafka 两年时间踩过哪些坑?
顺序问题
1. 为什么要保证消息的顺序?
刚开始我们系统的商户很少,为了快速实现功能,我们没想太多。既然是走消息中间件 kafka通信,订单系统发消息时将订单详细数据放在消息体,我们后厨显示系统只要订阅 topic ,就能获取相关消息数据,然后处理自己的业务即可。
不过这套方案有个关键因素: 要保证消息的顺序 。
为什么呢?
订单有很多状态,比如:下单、支付、完成、撤销等,不可能 下单 的消息都没读取到,就先读取 支付 或 撤销 的消息吧,如果真的这样,数据不是会产生错乱?
好吧,看来保证消息顺序是有必要的。
2.如何保证消息顺序?
我们都知道 kafka 的 topic 是无序的,但是一个 topic 包含多个 partition ,每个 partition 内部是有序的。
如此一来,思路就变得清晰了:只要保证生产者写消息时,按照一定的规则写到同一个 partition ,不同的消费者读不同的 partition 的消息,就能保证生产和消费者消息的顺序。
我们刚开始就是这么做的,同一个 商户编号 的消息写到同一个 partition , topic 中创建了 4 个 partition ,然后部署了 4 个消费者节点,构成 消费者组 ,一个 partition 对应一个消费者节点。从理论上说,这套方案是能够保证消息顺序的。
一切规划得看似“天衣无缝”,我们就这样”顺利“上线了。
3.出现意外
该功能上线了一段时间,刚开始还是比较正常的。
但是,好景不长,很快就收到用户投诉,说在划菜客户端有些订单和菜品一直看不到,无法划菜。
我定位到了原因,公司在那段时间网络经常不稳定,业务接口时不时报超时,业务请求时不时会连不上数据库。
这种情况对 顺序消息 的打击,可以说是 毁灭性 的。
为什么这么说?
假设订单系统发了:”下单“、”支付“、”完成“ 三条消息。
而”下单“消息由于网络原因我们系统处理失败了,而后面的两条消息的数据是无法入库的,因为只有”下单“消息的数据才是完整的数据,其他类型的消息只会更新状态。
加上,我们当时没有做 失败重试机制 ,使得这个问题被放大了。问题变成:一旦”下单“消息的数据入库失败,用户