微服务广播模式实践
微服务广播模式,指的是在微服务多实例部署的场景下,将消息广播到多个微服务实例的一种模式。
广播模式,一般用来维护微服务的内存数据,根据数据类型的不同,有助于解决两类问题。通常广播模式会使用支持发布订阅的消息中间件实现(如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级别。
bin/pulsar-admin namespaces | grep expiration
get-subscription-expiration-time Get subscription expiration time for
Usage: get-subscription-expiration-time [options] tenant/namespace
set-subscription-expiration-time Set subscription expiration time for
Usage: set-subscription-expiration-time [options] tenant/namespace
Subscription expiration time in minutes
remove-subscription-expiration-time Remove subscription expiration
Usage: remove-subscription-expiration-time [options] tenant/namespace
这里注意要合理地配置消费组的老化时间,在pulsar的当前版本(2.11版本)下,catch up读,也就是说消费组平时积压量不大。如果将消费组的老化时间配置大于等于消息的老化时间,会出现消费组老化不了的现象。
当然,由于消费组和消息老化都是定时任务,预估时间时,要考虑一定的buffer。
这里让我们稍稍dive一下原理,消费组的老化是通过判断Cursor游标的LastActive time来判断能否老化的。如果该消费组的游标位置到达了消息老化区域,被老化掉了,消费组的游标位置就会强制更新到一个可用的位置,这个时候会更新游标的LastActive time到当前时间,周而复始,导致消费组无法老化。举个
假设消费组的老化时间为4h,消息的老化时间为3h,就可能会发生这样的事情
总结
广播模式在微服务架构中起到了重要的角色,尤其是在需要在微服务实例之间同步数据的场景中,它具有显著的优势。它能够帮助维护内存数据的缓存一致性。希望本篇文章能提供您全面的广播模式的知识。