一、簡介
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。
-