上次我們了解了eureka(參見Greenwich.SR2版本的Spring Cloud Eureka實例),里面的服務消費方(服務實例a-beautiful-client)我們其實已經用到了ribbon。在pom里我們引入了
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>
在主類中我們通過RestTemplate的@LoadBalanced注解啟動了ribbon
@LoadBalanced @Bean public RestTemplate restTemplate() { return new RestTemplate(); }
因此,ribbon實例我們上次其實已經實現了,只要我們啟動兩個服務提供方實例(a-bootiful-client,如何啟動多個實例參見IDEA同一項目啟動多個實例),不停的刷新瀏覽器中消費方的請求鏈接(http://localhost:8763/sayHello?name=world),你會發現端口號是順序變化的:
這是因為ribbon默認的負載均衡策略就是輪詢。
Spring Cloud Ribbon是一個客戶端負載均衡工具,與傳統的服務端負載均衡(如Nginx)不同。首先服務端負載均衡需要一個獨立的服務器來路由客戶端請求到服務端去,另外客戶端負載均衡需要客戶端自己去維護服務實例的列表。或者可以這么理解,客戶端里集成了一個服務端的負載均衡就成了客戶端負載均衡了。那么客戶端如何查詢並維護(心跳)這份服務列表呢?它只能去找注冊中心Eureka求助了。Spring Cloud微服務之間的調用不是rpc而是http,我們的客戶端調服務端(或者說服務的消費方調提供方)是通過REST模板請求來進行的,因此我們的客戶端負載均衡也離不開REST。
那么ribbon的客戶端負載均衡是如何通過REST實現的呢?首先從服務列表中根據負載均衡策略選出一個指定的服務實例,再根據服務實例進行http調用。我們看下ribbon給我提供的負載均衡策略都有哪些:
策略名 | 策略描述 |
BestAvailableRule | 選擇一個最小的並發請求的服務實例 |
AvailabilityFilteringRule | 過濾掉那些因為一直連接失敗的被標記為熔斷的服務實例,並過濾掉那些高並發超過一定閾值的服務實例 |
WeightedResponseTimeRule | 根據響應時間分配一個權重區間,響應時間越長,權重區間越窄,被選中的可能性越低 |
RetryRule | 對選定的負載均衡策略機上重試機制 |
RoundRobinRule | 方式輪詢選擇服務實例 |
RandomRule | 隨機選擇一個服務實例 |
ZoneAvoidanceRule | 復合判斷服務實例所在區域的性能和可用性進行服務實例選擇 |
如果你想定制自己的負載均衡策略也是可以的,只需在application里新增一個配置即可:
#配置調用服務實例a-bootiful-client的負載均衡策略
a-bootiful-client.ribbon.NFLoadBalanceRuleClassName=com.netflix.loadbalancer.RandomRule
我們這里配置了隨機,兩個實例隨機的比例無法看出,我們再加兩個實例8773、8774:
這時我們再不停的刷瀏覽器的,可以看到端口號不再是順序出現的,而是隨機的。
接下來我們看下,ribbon如果脫離注冊中心和REST如何實現負載均衡。
1、application新增如下配置:
#負載均衡時關閉到eureka的連接 ribbon.eureka.enabled=false #配置服務實例a-bootiful-client的服務列表 a-bootiful-client.ribbon.listOfServers=localhost:8762,localhost:8772 #配置服務實例a-bootiful-client的負載均衡策略 a-bootiful-client.ribbon.NFLoadBalanceRuleClassName=com.netflix.loadbalancer.RandomRule
2、去掉主類的REST的@LoadBalance注解
// @LoadBalanced @Bean public RestTemplate restTemplate() { return new RestTemplate(); }
3、獲取負載均衡處理后選中的服務實例,並解析服務實例ip和port進行調用
@Autowired private LoadBalancerClient loadBalancerClient; @Override public String call(String name) { // 獲取負載均衡策略選擇后的實例 ServiceInstance instance = loadBalancerClient.choose("a-bootiful-client"); // 不再根據服務實例名調用,而是通過ip和端口調用 ResponseEntity resultResponseEntity = restTemplate.postForEntity("http://" + instance.getHost() + ":" + instance.getPort() + "/hello?name=" + name, null, String.class); if (resultResponseEntity != null && resultResponseEntity.getBody() != null) { return name + " says: " + resultResponseEntity.getBody().toString(); } return null; }
從上面可以看出,缺少了注冊中心eureka后,我們只能自己配置服務列表ribbon.listOfServers(想一想,如果只配置一個服務實例,不是就實現直連了嗎?),而且也沒法通過服務實例調用REST了,我們得解析出負載均衡選出的服務實例的url才能調用成功。如果服務列表中某個實例掛掉了,客戶端是無法感知的,這時就有可能會出現調用失敗的情況了。所以一般我們不推薦這種配置。如果服務提供方掛掉,消費方如何實現服務保護呢?詳見Greenwich.SR2版本的Spring Cloud Hystrix實例。