大萧条时期的野蛮生长——消息队列优化

147 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划·4月更文挑战」的第10天,点击查看活动详情

消息队列已经成了现代互联网架构中必不可少的基础设施了,如果说整个互联网服务是一个城市,那数据就是城市中的建筑,消息队列则是城市中的交通线路。在一些复杂度较低,微服务较少的业务中,人们往往会选择直接的http接口调用来通信。但随着业务复杂度的上升,数据量的持续增长,同步的http调用已经显然无法满足业务的需求了。同时,各个业务的迭代相互影响,也亟需一个可以解耦的工具解除相互之间的影响。

从无到有的优化

首先你的服务可能处理能力只有100tps,即每秒只能处理100次数据新增请求。如果有一天流量突然增加到110,此时的服务可能就会报错,甚至彻底瘫痪。此时,在服务前增加一个队列,先把请求堆积起来,再根据自身的服务能力慢慢处理,至少可以保证服务不会宕机,只是用户侧看起来响应的稍慢一些。这个队列可以是内存中的队列,也可以是外部的队列,实践过程中会有更详细的方案,但是这种一种可以抵消外部请求峰值的做法,行内黑话叫“削峰”。本质上和汽车的差速器一样,是一种高速到低速的转换。

一致性优化

大多数情况下,公司内都会提供公共的消息队列服务,公有云服务也同样会提供类似的服务。我们把消息写到外部,再从外部来消费,很多时候出了问题首先就会“怪罪”到这样的消息队列丢消息了。“我的服务默认没有问题,肯定是外部的系统有问题”。这是一种普遍思想。

单体应用的时代,想要做到消息不丢,真的是玩出花来都很难保证。但现代的消息队列都是集群部署,设计者也提供了十分完善的一致性方案。以kafka为例,提供了acks机制,当生产者对消息一致性敏感时,可以设置acks=all,那消息发出后,必须得到所有的主从节点回应收到才算消息发送成功,同时消息会保存在所有的副本节点中,当主节点挂了之后,也会触发自动选举的机制,重新设置一个新的主节点提供服务。通过这样的冗余,可以满足绝大部分系统的一致性要求。比起你的服务,kafka不知道要强大多少倍,所以下次不要轻易觉得基础组件有问题了。

生产端能保证可以后,下一步就是保证消费端不重复消费。这个相对简单,写消息时对每一条消息附带一个唯一id,然后保存在redis中,每次消费时判断该id是否存在即可,能挡住大部分的重复消费了。如果消费并发太高,那再从数据库层设置乐观锁,也能防住剩下的大部分请求了。