我终于搞懂了微服务,太不容易了...( 四 )


这一节,我们着重讲的并不是注册中心自身可用性保证,而更多的是与节点状态相关的部分 。
①节点信息的保障
我们说过,当注册中心完全宕机后,微服务框架仍然需要有正常工作的能力 。这得益于框架内处理节点状态的一些机制 。
本机内存:首先服务消费者会将节点状态保持在本机内存中 。
一方面由于节点状态不会变更得那么频繁,放在内存中可以减少网络开销 。另一方面,当注册中心宕机后,服务消费者仍能从本机内存中找到服务节点列表从而发起调用 。
本地快照:我们说,注册中心宕机后,服务消费者仍能从本机内存中找到服务节点列表 。那么如果服务消费者重启了呢?
这时候我们就需要一份本地快照了,即我们保存一份节点状态到本地文件,每次重启之后会恢复到本机内存中 。
②服务节点的摘除
现在无论注册中心工作与否,我们都能顺利拿到服务节点了 。但是不是所有的服务节点都是正确可用的呢?
在实际应用中,这是需要打问号的 。如果我们不校验服务节点的正确性,很有可能就调用到了一个不正常的节点上 。所以我们需要进行必要的节点管理 。
对于节点管理来说,我们有两种手段,主要是去摘除不正确的服务节点 。
注册中心摘除机制:一是通过注册中心来进行摘除节点 。服务提供者会与注册中心保持心跳,而一旦超出一定时间收不到心跳包,注册中心就认为该节点出现了问题,会把节点从服务列表中摘除,并通知到服务消费者,这样服务消费者就不会调用到有问题的节点上 。
服务消费者摘除机制:二是在服务消费者这边拆除节点 。因为服务消费者自身是最知道节点是否可用的角色,所以在服务消费者这边做判断更合理,如果服务消费者调用出现网络异常,就将该节点从内存缓存列表中摘除 。
当然调用失败多少次之后才进行摘除,以及摘除恢复的时间等等细节,其实都和客户端熔断类似,可以结合起来做 。
一般来说,对于大流量应用,服务消费者摘除的敏感度会高于注册中心摘除,两者之间也不用刻意做同步判断,因为过一段时间后注册中心摘除会自动覆盖服务消费者摘除 。
③服务节点是可以随便摘除/变更的么
上一节我们讲可以摘除问题节点,从而避免流量调用到该节点上 。但节点是可以随便摘除的么?同时,这也包含"节点是可以随便更新的么?"疑问 。
频繁变动:当网络抖动的时候,注册中心的节点就会不断变动 。这导致的后果就是变更消息会不断通知到服务消费者,服务消费者不断刷新本地缓存 。
如果一个服务提供者有 100 个节点,同时有 100 个服务消费者,那么频繁变动的效果可能就是 100*100,引起带宽打满 。
这时候,我们可以在注册中心这边做一些控制,例如经过一段时间间隔后才能进行变更消息通知,或者打开开关后直接屏蔽不进行通知,或者通过一个概率计算来判断需要向哪些服务消费者通知 。
增量更新:同样是由于频繁变动可能引起的网络风暴问题,一个可行的方案是进行增量更新,注册中心只会推送那些变化的节点信息而不是全部,从而在频繁变动的时候避免网络风暴 。
可用节点过少:当网络抖动,并进行节点摘除过后,很可能出现可用节点过少的情况 。
这时候过大的流量分配给过少的节点,导致剩下的节点难堪重负,罢工不干,引起恶化 。
而实际上,可能节点大多数是可用的,只不过由于网络问题与注册中心未能及时保持心跳而已 。
这时候,就需要在服务消费者这边设置一个开关比例阈值,当注册中心通知节点摘除,但缓存列表中剩下的节点数低于一定比例后(与之前一段时间相比),不再进行摘除,从而保证有足够的节点提供正常服务 。
这个值其实可以设置的高一些,例如百分之 70,因为正常情况下不会有频繁的网络抖动 。当然,如果开发者确实需要下线多数节点,可以关闭该开关 。
服务消费者如何保障稳定性
一个请求失败了,最直接影响到的是服务消费者,那么在服务消费者这边,有什么可以做的呢?
①超时
如果调用一个接口,但迟迟没有返回响应的时候,我们往往需要设置一个超时时间,以防自己被远程调用拖死 。
超时时间的设置也是有讲究的,设置的太长起的作用就小,自己被拖垮的风险就大,设置的太短又有可能误判一些正常请求,大幅提升错误率 。
在实际使用中,我们可以取该应用一段时间内的 P999 的值,或者取 p95 的值*2,具体情况需要自行定夺 。


推荐阅读