微服务广播模式实践:维护内存数据的缓存一致性
本文分享自华为云社区《微服务广播模式实践》,作者:张俭 。
微服务广播模式,指的是在微服务多实例部署的场景下,将消息广播到多个微服务实例的一种模式。
广播模式,一般用来维护微服务的内存数据,根据数据类型的不同,有助于解决两类问题。通常广播模式会使用支持发布订阅的消息中间件实现(如 Redis、Kafka、Pulsar 等),本文也基于消息中间件进行讨论。
利用广播模式维护一致的缓存
这应该是广播模式利用最多的一种场景,假想一个拥有海量用户的电商网站、或是一个亿级设备连接的 IoT 平台。势必会存在一些缓存数据,像是用户的购物车信息,或是设备的密钥缓存。如果没有广播模式,可能会存在这样的问题。
当用户更新了它的购物车之后,微服务实例 1 的数据发生了更新,数据库的数据也成功更新。但是微服务实例 2 中的缓存数据未能更新,那么如果用户的请求均衡到了实例 2,就会发生意想不到的后果。
这种情况下我们可以让微服务 1 在广播通道中发送一个缓存的 invalidate 消息,将微服务实例 2 中该用户的缓存清零,使得微服务实例 2 在下一次处理该用户的请求时,从数据库中读取最新的消息。
使用该模式需要注意的点:
每个微服务实例应该使用不同的消费组,可以通过微服务的 IP、主机名、UUID 等拼装成订阅组名称,这才称得上广播之名
微服务消费消息的时候,应从 Latest 开始消费,避免从 Earliest 开始消费无用的缓存清理消息
由于每一次微服务重启都会产生一个新的消费组,需要注意消费组的老化,可以通过消息中间件自带的不活跃消费组老化能力兜底,建议通过 gracefulExit、监听 kill 信号等机制来主动删除消费组信息
为什么说消费组老化比较重要呢,因为很多监控系统都会根据消费组的积压来做告警,很容易产生误告警。
利用广播模式维护内存中的数据
这种模式相对比较少见,常见于 key 的基数不是很大,能够将数据完整地存储在内存中,比如电商平台的企业卖家个数、物联网平台的用户个数等,并且对数据的一致性要求不是很高(因为广播模式情况下,对于两个微服务实例来说没有一致性保障)。像 Apache Pulsar 设计的 TableView,在我看来,就是做这个事的一个最佳实践。Pulsar 内部大量使用了 topic 存储数据,就是采用这个方式。
使用该模式需要注意的点:
同上,需要使用不同的消费组名称
微服务消费消息的时候,应该从 Earliest 开始消费,保证所有微服务内存中的消息视图一致
同上,需要注意消费组的老化
为什么需要消费组老化作为保底手段
因为在极端场景下,无论是 graceful 的代码,还是监听 kill 信号的代码,都不能保证代码百分百地被执行。需要兜底。
Kafka 消费组老化
Kafka 通过 offsets.retention.minutes 参数控制消费组中 offsets 保留时间,在此时间内如果没有提交 offset,offsets 将会被删除。Kafka 判定消息组中没有在线的消费者(如 empty 状态),且没有 offsets 时,将会删除此消费组。
Pulsar 消费组老化
pulsar 的消费组老化策略更加灵活,可以配置到 namespace 级别。
这里注意要合理地配置消费组的老化时间,在 pulsar 的当前版本(2.11 版本)下,catch up 读,也就是说消费组平时积压量不大。如果将消费组的老化时间配置大于等于消息的老化时间,会出现消费组老化不了的现象。
当然,由于消费组和消息老化都是定时任务,预估时间时,要考虑一定的 buffer。
这里让我们稍稍 dive 一下原理,消费组的老化是通过判断 Cursor 游标的 LastActive time 来判断能否老化的。如果该消费组的游标位置到达了消息老化区域,被老化掉了,消费组的游标位置就会强制更新到一个可用的位置,这个时候会更新游标的 LastActive time 到当前时间,周而复始,导致消费组无法老化。举个🌰
假设消费组的老化时间为 4h,消息的老化时间为 3h,就可能会发生这样的事情。
总结
广播模式在微服务架构中起到了重要的角色,尤其是在需要在微服务实例之间同步数据的场景中,它具有显著的优势。它能够帮助维护内存数据的缓存一致性。希望本篇文章能提供您全面的广播模式的知识。
版权声明: 本文为 InfoQ 作者【华为云开发者联盟】的原创文章。
原文链接:【http://xie.infoq.cn/article/5547f08b69ce6905f93ccf026】。文章转载请联系作者。
评论