1. 程式人生 > >SpringCloud | Feign如何整合Ribbon進行負載均衡的?

SpringCloud | Feign如何整合Ribbon進行負載均衡的?

Ribbon是SpringCloud框架進行負載均衡的腳手架,貫穿springCloud專案中所有的http服務呼叫。Ribbon針對RestTemplate負載均衡已經提供了完整實現,網上很多的ribbon demo也是分析restTemplate如何負載均衡的。

而我們都知道,feignClient已經預設使用了ribbon,feign是如何利用ribbon的負載均衡的呢?帶著疑惑看了一遍程式碼。首先先看一下spring-cloud-netflix-core包:

    細心觀察,上面的包結構中有兩個ribbon包目錄,為什麼會有兩個模組呢?下面的ribbon包是針對ribbon的完整實現,有興趣的可以瀏覽原始碼,也就是上圖第二個的ribbon包,這裡不做展開,主要介紹feign如何整合ribbon。

而feign包下面的ribbon包,就是feign整合ribbon的邏輯,這裡具體來看一下。

   這是feign執行邏輯的入口,feign的代理類都會由此方法進來(三種代理類包括 原生的feign代理類,整合了ribbon的feign代理類,整合了sleuth)。跟進executeAndDecode 方法到LoadBalancerFeignClient類中的execute方法:

注意上圖中標紅的關鍵程式碼。可以看到,每個請求都會獲取對應的IClientConfig物件。

繼續進行getClientConfig方法會發現一個重要物件:SpringClientFactory

SpringClientFactory是一個bean容器,來獲取每個feign對應的properties和loadBalancer。

仔細檢視獲取LB(LoadBalance)例項和獲取properties的方法,發現LB例項最終從父類NamedContextFactory獲取Bean。

Bean來源AnnotationConfigApplicationContext context上下文:

至此,一個關鍵的配置類被發現了:RibbonClientConfiguration,整個邏輯也就清晰了:RibbonClientConfiguration類是ribbon包的配置類,在feign請求的時候動態配置的。

在每個服務第一次請求的時候,會到抽象工廠類NamedContextFactory中獲取當前服務對被呼叫服務配置,

通過getContext方法獲取,該方法會先從map中獲取:

private Map contexts = new ConcurrentHashMap<>();

獲取不到會執行createContext方法動態載入配置。該方法中用到了spring的註解容器:AnnotationConfigApplicationContext

該容器主要配合@Configuration和註解使用。上述程式碼中向該容器中注入RibbonClientConfiguration配置類,呼叫context.refresh()重新整理容器,也就拿到了每個服務的配置。

每個服務對應一個配置,我們在專案中可以這麼配置:

  到這裡也明白了,為什麼feign呼叫,第二次呼叫的時間會比第一次用時略少,因為每個feign第一次根據serviceId

載入對應的ribbon配置。具體的ribbon配置有興趣可以看下ribbon原始碼。

    每個服務第一次呼叫的時候控制檯會列印一下資訊,loadBalancer初始化...

2017-11-21 17:31:59.787  INFO 4892 --- [   hystrix-ak-1] c.n.l.DynamicServerListLoadBalancer      : DynamicServerListLoadBalancer for client ak initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=ak,current list of Servers=[192.168.57.237:8888],Load balancer stats=Zone stats: {},Server stats: [[Server:192.168.57.237:8888;	Zone:defaultZone;	Total Requests:0;	Successive connection failure:0;	Total blackout seconds:0;	Last connection made:Thu Jan 01 08:00:00 CST 1970;	First connection made: Thu Jan 01 08:00:00 CST 1970;	Active Connections:0;	total failure count in last (1000) msecs:0;	average resp time:0.0;	90 percentile resp time:0.0;	95 percentile resp time:0.0;	min resp time:0.0;	max resp time:0.0;	stddev resp time:0.0]
]}ServerList:org.springf[email protected]51b887dc

在我們進行feign這是ribbon的一種飢餓載入機制,ribbon也提供了修改配置:

ribbon.eager-load.enabled=true
ribbon.eager-load.clients=hello-service, user-service

理論上,只要hystrix超時時間合理的話,是可以避免服務啟動後第一次呼叫超時的問題,所以ribbion的飢餓載入機制是可以關閉的,等到真正需要呼叫的時候再進行初始化對應的ribbon配置。