在分布式系统中,业务被拆分到不同的子系统,往往伴随有大量系统间联动的场景:某个系统的状态或者数据发生变化,其他系统得知后需要做出相应的处理。例如在zookeeper集群中,监控到主节点下线后,其他节点就会重新协商选主。目前,比较常用的两种用于感知系统变化的方式是轮询模式和观察者模式。
轮询本质是一种请求-响应模式,系统A按一定时间间隔向系统B发送请求,B收到请求后会告知A是否有状态、数据等变动。轮询的优点是设计简单、不用长时间占用网络通信,但如果系统发生变化的频率很低,轮询就会显得浪费性能。
观察者模式,即订阅-发布模式,假设系统A是订阅者,系统B是发布者,那么系统B发生变化时,会产生一个“事件”,并把事件发送给A,然后A会根据事件的类型做出相应动作。
通常来说,观察者模式会更好。我们举个例子来简单分析下两种模式的优劣:例如,有一台手机,操作系统希望在每次短信app收到新消息时,发出“叮咚”的提示音。
- 轮询:系统每隔一段时间向app发送一个请求,询问是否有收到新消息,如果收到的响应是有新消息,就播放提示音。
- 观察者:app收到新消息时,自动向系统发送一个“通知”,系统收到通知后播放声音。
这种场景下,轮询会更消耗cpu(因为要不断发送和处理请求),更主要的问题是不够实时,假设轮询间隔是10秒,那么最坏情况下可能消息到达后10秒才发出提示音。
当然,也不是说所有场景都更适合用观察者。例如,某个订阅者关注的事件发生频率非常高,或者说这个事件通知的消息体很大,那么订阅者可能就会忙于消化事件通知,处于一个高负载运转的状态。这个时候,如果对实时性要求不高,也可以采用轮询模式,适当地增大轮询间隔,把一个时间段内发生的多次变化“合并”成一个进行消化。
通过上面的一些分析,我自己简单比较了两种模式的优缺点:
- 如果需要实时响应系统变化,更适合用观察者模式。用轮询的话,问题在于如果变化发生的间隔小于轮询间隔,可能会导致遗漏(但某些情景下这可能是一个优点)。
- 如果一个系统事件需要通知多个系统进行处理,也是更适合用观察者。因为多个系统同时轮询,会导致被轮询者的负荷过大。但如果变化频率较高,使用观察者模式也会有负荷问题,因为事件发生时,发布者也是要通知所有订阅者,不过用redis等中间件解耦可以缓解症状。