在之前註冊中心那一章,消費者調用提供者的接口,是需要寫死這樣的url的。但是真實的環境我們一般要求服務提供者高可用,所以呢同一個服務需要多個服務提供者。那麼消費者就不能夠寫死這個url了。所以基於如此我們改一下代碼。
一、修改代碼,添加ribbon實現負載均衡
1、服務提供者
首先呢,用ip+port用於區別調用不同的服務。設置到ProductName
我們用兩個application分別啓動同一個項目, 不過用的是不同端口,模擬兩個服務。記得修改端口。當然如果不會的話,就copy一下再建立一個子項目。
2、服務消費者
這個@LoadBalanced是實現負載均衡的關鍵!所以restTemplate調用的時候就可以按負載均衡來走。
替換成相應的服務的名字,就是在服務yml中配置的應用名。
之後來測試
2、關於ribbon介紹
3、負載均衡策略
com.netflix.loadbalancer.RoundRobinRule :以輪詢的方式進行負載均衡。
com.netflix.loadbalancer.RandomRule :隨機策略
com.netflix.loadbalancer.RetryRule :重試策略。
com.netflix.loadbalancer.WeightedResponseTimeRule :權重策略。會計算每個服務的權重,越高的被調用的可能性越大。
com.netflix.loadbalancer.BestAvailableRule :最佳策略。遍歷所有的服務實例,過濾掉故障實例,並返回請求數最小的實例返回。
com.netflix.loadbalancer.AvailabilityFilteringRule :可用過濾策略。過濾掉故障和請求數超過閾值的服務實例,再從剩下的實力中輪詢調用。
在服務消費者的application.yml配置文件中修改負載均衡策略
##需要調用的微服務名稱
shop-service-product:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
4、重試機制
比如我們現在有一個消費者A,兩個提供者B-1、B-2(都是提供同一個服務,高可用),現在採取負載均衡策略的是輪詢,如果此時訪問的B-1,B-1掛掉了或者是由於網絡不好超過了20ms(默認),那麼就會報錯!所以我們需要開啓重試機制就會起作用向另一個服務B-2發送一次請求。下面是關於重試機制的配置。
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
service-product:
ribbon:
#NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
ConnectTimeout: 250 # Ribbon的連接超時時間
ReadTimeout: 1000 # Ribbon的數據讀取超時時間
OkToRetryOnAllOperations: true # 是否對所有操作都進行重試
MaxAutoRetriesNextServer: 1 # 切換實例的重試次數
MaxAutoRetries: 1 # 對當前實例的重試次數
5、負載均衡的源碼解析
爲什麼這個方法上面加入了一個標籤@LoadBalanced就可以實現負載均衡了呢?難道是這個標籤有啥特別之處。大家可以點進去。沒啥特別之處!這裏只是一個標記,以至於我們在攔截器進行攔截的時候可以找到哪些是需要負載均衡的。
其實主要還是我們之前說的springboot的自動裝載機制。去瞅一眼依賴。
我們進入這個類
實際上,在加載這個類之前要加載LoadBalancerAutoConfiguration這個類。
這個類,主要做了三件事情:
(1)創建了一個 LoadBalancerInterceptor 的Bean,用於實現對客戶端發起請求時進行攔截,以實現客戶端負載均衡。
(2)創建了一個 RestTemplateCustomizer 的Bean,用於給 RestTemplate 增加 LoadBalancerInterceptor 攔截器。
(3)維護了一個被 @LoadBalanced 註解修飾的 RestTemplate 對象列表,並在這裏進行初始化,通過調用 RestTemplateCustomizer 的實例來給需要客戶端負載均衡的 RestTemplate 增加LoadBalancerInterceptor 攔截器。
實際上這個纔是最重要的真大佬!!!一會我們點進去康康 ,先不要着急
這個時候我們調用restTemplate.getForObject("http://service-product:" +"/product/1",Product.class);就會進入我們的攔截器。
接着我們看一下這個攔截器LoadBalancerInterceptor的攔截器方法。
攔截器攔截執行的代碼都在intercept方法裏,看一下excute方法。
這裏面創建了一個LoadBalancer對象,其實就是負載均衡器(含有負載均衡算法)。IloadBalancer其實是一個接口,主要是實現類BaseLoadBalancer。
看一下getServer方法:
點進去chooseServer方法。
實際上rule就是負載均衡採用的算法,默認是輪詢
我們來debug調試一下。我們打兩個斷點
當order-service啓動的時候就到了第一個斷點。添加一個負載均衡攔截器。
現在我們到第二個斷點,輸入http://localhost:9002/order/buy/1。這裏面就攔截到。
再打一個斷點到
這樣就獲取到了
負載均衡要訪問的服務的地址。