一、简介
ribbon的区域亲和机制如下(需要说明一点,亲和性发生在过滤阶段,请参考负载均衡器中server列表是如何动态更新的部分):
以上的图示及文字仅为举例说明,下面描述一下图上表示的流程:
首先,先看右下角的provider集群,因为会分别部署在北京,上海,广州三个地区,把这里地区看作zone,故有三个zone。
provider集群会把实例信息(ip+port+name等等)按照配置eureka.client.serviceUrl.defaultZone,遍历配置的eureka实例,注册到Eureka集群。
其次,Eureka集群每个实例都会具有provider的实例信息。
因为eureka实例也会根据eureka.client.serviceUrl.defaultZone,遍历配置的eureka实例,把provider实例信息进行复制(但仅传播一级,不会多级传播)。
最后,Consumer从Eureka集群获取到服务实例的列表,然后根据zone(Consumer使用的zone来自于自己的配置文件:eureka.instance.metadataMap.zone)进行比对,即亲和。
二、实现
请参考ServerListFilter的ZonePreferenceServerListFilter
三、问题&测试
-
如果provider有多个zone,但是consumer亲和性选择的zone的server整个不可用,会不会导致Consumer没有可用的provider?
参考ServerListFilte中的ZoneAffinityServerListFilter:
- 其首先进行亲和性过滤,即过滤后剩下的server都是同一个zone的
- 之后对这些server进行健康检查,如果认为不健康,则返回之前所有的server列表,否则,使用亲和性server。
再参考ZonePreferenceServerListFilter,这里,我们假设consumer已经正确配置了自己需要的zone,比如eureka.instance.metadataMap.zone=北京
注意:如果consumer配置文件没有设置zone,则zone为defaultZone,而provider设置了正确的zone,由于zone不相同,那么将不涉及亲和性问题。那么,在负载均衡阶段,将使用ZoneAwareLoadBalancer来过滤不健康的zone,之后轮询选择。
-
参考ZonePreferenceServerListFilter的如下代码:
public List<Server> getFilteredListOfServers(List<Server> servers) { List<Server> output = super.getFilteredListOfServers(servers); if (this.zone != null && output.size() == servers.size()) { List<Server> local = new ArrayList<Server>(); for (Server server : output) { if (this.zone.equalsIgnoreCase(server.getZone())) { local.add(server); } } if (!local.isEmpty()) { return local; } } return output; }
即consumer配置了zone并且ZoneAffinityServerListFilter过滤后server列表没变化,则ZonePreferenceServerListFilter执行亲和性过滤,强制和consumer配置的zone进行比对。
那么分为以下情况:
- 如果亲和性zone对应的server只要存在,那么就使用亲和性zone的server。
- 如果亲和性zone对应的server全军覆没了(从Eureka上获取不到了),那么使用其他zone的server。
-
那么针对这种情况进行测试,看看是不是上述的过程:
-
首先测试provider正确设置了zone,但是consumer采用defaultZone的情况:
配置图及测试情况如下:
-
再测试provider设置了正确的zone,consumer也设置了对应的zone的情况:
配置图及测试情况如下:
-
再测试provider设置了正确的zone,consumer也设置了对应的zone,但是亲和性的zone中的实例全挂了:
配置图及测试情况如下:
-
结论,如果某个zone整个不可用,亲和过滤会导致consumer调用异常(降级),30秒后,eureka client更新,则选择其他可用zone。
-
如果要想避免这种情况(30秒的不可用),可以将客户端配置如下:
ribbon: NIWSServerListFilterClassName: com.netflix.loadbalancer.ZoneAffinityServerListFilter
即使用ZoneAffinityServerListFilter(注意,springcloud默认的ZonePreferenceServerListFilter会导致30秒不可用问题),由于它会对区域亲和后的zone进行健康检查,故,当区域亲和后的zone不健康时,则不返回区域亲和的结果,所以可以保证在区域亲和的zone不可用时,使用其他的zone,详细请参考ServerListFilter。
-