Ribbon 是 Netflix 發布的開源項目,主要功能是為 REST 客戶端實現負載均衡。它主要包括六個組件:
- ServerList,負載均衡使用的服務器列表。這個列表會緩存在負載均衡器中,並定期更新。當 Ribbon 與 Eureka 結合使用時,ServerList 的實現類就是 DiscoveryEnabledNIWSServerList,它會保存 Eureka Server 中注冊的服務實例表。
- ServerListFilter,服務器列表過濾器。這是一個接口,主要用於對 Service Consumer 獲取到的服務器列表進行預過濾,過濾的結果也是 ServerList。Ribbon 提供了多種過濾器的實現。
- IPing,探測服務實例是否存活的策略。
- IRule,負載均衡策略,其實現類表述的策略包括:輪詢、隨機、根據響應時間加權等,我們也可以自己定義負載均衡策略,比如我們就利用自己實現的策略,實現了服務的版本控制和直連配置。實現好之后,將實現類重新注入到 Ribbon 中即可。
- ILoadBalancer,負載均衡器。這也是一個接口,Ribbon 為其提供了多個實現,比如 ZoneAwareLoadBalancer。而上層代碼通過調用其 API 進行服務調用的負載均衡選擇。一般 ILoadBalancer 的實現類中會引用一個 IRule。
- RestClient,服務調用器。顧名思義,這就是負載均衡后,Ribbon 向 Service Provider 發起 REST 請求的工具。
Ribbon 工作時會做四件事情:
- 優先選擇在同一個 Zone 且負載較少的 Eureka Server;
- 定期從 Eureka 更新並過濾服務實例列表;
- 根據用戶指定的策略,在從 Server 取到的服務注冊列表中選擇一個實例的地址;
- 通過 RestClient 進行服務調用。
服務提供者
創建一個Spring Starter Project,命名service-producer,添加依賴
1 <dependencies>
2 <dependency>
3 <groupId>org.springframework.boot</groupId>
4 <artifactId>spring-boot-starter-web</artifactId>
5 </dependency>
6 <dependency>
7 <groupId>org.springframework.cloud</groupId>
8 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
9 </dependency>
10 </dependencies>
配置屬性(application.yml)
server:
port: 8080 #啟動其他實例時需要修改端口號
spring:
application:
name: service-producer
eureka:
client:
serviceUrl:
defaultZone: http://admin:123456@localhost:8761/eureka/
控制層創建一個controller,對外提供一個接口(這里比較簡單就只返回服務的端口號)
1 package com.carry.springcloud.controller; 2
3 import org.springframework.beans.factory.annotation.Value; 4 import org.springframework.web.bind.annotation.GetMapping; 5 import org.springframework.web.bind.annotation.RestController; 6
7 @RestController 8 public class ProducerController { 9
10 @Value("${server.port}") 11 String serverPort; 12
13 @GetMapping("/getPortInfo") 14 public String produce() { 15 return "調用服務的端口號為:" + serverPort; 16 } 17 }
啟動類加上@EnableEurekaClient注解即可
1 package com.carry.springcloud; 2
3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 6
7 @EnableEurekaClient 8 @SpringBootApplication 9 public class ServiceProducerApplication { 10
11 public static void main(String[] args) { 12 SpringApplication.run(ServiceProducerApplication.class, args); 13 } 14 }
測試
打開瀏覽器訪問localhost:8080/getPortInfo,出現以下結果說明是OK的
服務消費者(Ribbon)
創建一個Spring Starter Project,命名service-consumer-ribbon,添加依賴
1 <dependencies>
2 <dependency>
3 <groupId>org.springframework.boot</groupId>
4 <artifactId>spring-boot-starter-web</artifactId>
5 </dependency>
6 <dependency>
7 <groupId>org.springframework.cloud</groupId>
8 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
9 </dependency>
10 <dependency>
11 <groupId>org.springframework.cloud</groupId>
12 <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
13 </dependency>
14 </dependencies>
配置屬性(application.yml)
server:
port: 8082
spring:
application:
name: service-consumer-ribbon
eureka:
client:
serviceUrl:
defaultZone: http://admin:123456@localhost:8761/eureka/
在啟動類中@Bean 將 restTemplate注入到ioc容器, 並使用@LoadBalanced 注解聲明開啟 負載均衡
1 package com.carry.springcloud; 2
3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.client.loadbalancer.LoadBalanced; 6 import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 7 import org.springframework.context.annotation.Bean; 8 import org.springframework.web.client.RestTemplate; 9
10 @EnableEurekaClient 11 @SpringBootApplication 12 public class ServiceConsumerRibbonApplication { 13
14 public static void main(String[] args) { 15 SpringApplication.run(ServiceConsumerRibbonApplication.class, args); 16 } 17
18 @Bean 19 @LoadBalanced 20 RestTemplate restTemplate() { 21 return new RestTemplate(); 22 } 23 }
編寫一個controller,注入RestTemplate用其調用服務提供者接口
1 package com.carry.springcloud.controller; 2
3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.web.bind.annotation.GetMapping; 5 import org.springframework.web.bind.annotation.RestController; 6 import org.springframework.web.client.RestTemplate; 7
8 @RestController 9 public class RibbonController { 10
11 @Autowired 12 RestTemplate restTemplate; 13
14 @GetMapping("/getPoducerInfo") 15 public String getPoducerInfo() { 16 String result = this.restTemplate.getForObject("http://service-producer/getPortInfo", String.class); 17 return result; 18 } 19 }
注:上面代碼中restTemplate.getForObject第一個參數url規則為:協議http+服務名(即application.yml配置spring.application.name的值)+接口值
測試
1、啟動Eureka服務
2、啟動兩個服務提供者service-producer實例,端口分別為8080和8081
3、啟動服務消費者service-consumer-ribbon
4、瀏覽器中訪問 localhost:8761,注冊成功
5、訪問服務消費者localhost:8082/getPoducerInfo
再次訪問
結果顯示是輪詢調用兩個服務提供者實例,這是因為默認的負載均衡算法是輪詢,也可自行修改負載均衡算法,例如:隨機算法,權重,只需要在application.yml里配置即可。