一起來學Spring Cloud(F版) | 第二篇:Ribbon軟負載
Spring Cloud
為開發者提供了在分散式系統中的一些常用的元件(例如配置管理,服務發現,斷路器,智慧路由,微代理,控制匯流排,一次性令牌,全域性鎖定,決策競選,分散式會話叢集狀態)。使用Spring Cloud開發人員可以快速地完成實現這些模式的服務和應用程式。它們在任何分散式環境中都能很好地工作
Ribbon
Ribbon
是 Netflix
開源的基於 HTTP
和 TCP
的客戶端負載均衡器框架,目前也已被 Spring Cloud
團隊整合在 spring-cloud-netflix
子專案下,主要用於客戶端軟負載功能,內部已實現了 隨機
、 輪訓
、 權重
、 減壓(選取壓力最小的)
等常見的負載演算法,同時也提供了 ILoadBalance
與 IRule
兩個介面方便我們自己編寫適合自己的負載演算法
- 負載均衡
- 容錯
- 多協議(HTTP,TCP,UDP)支援非同步和反應模型
- 快取和批處理
互動圖
Try
要嘗試 Spring Cloud Ribbon
首要的就是準備一個服務註冊中心,還不太清楚的可以在回頭看看上一章 認識Eureka
,這裡就不做過多贅述了,準備 eureka-server(回顧上一章)
、 product-server
、 order-server
三個專案,後面的兩個可以理解為上一章的 eureka-client
Eureka Server
詳情參考上一章,或從文末的 GITHUB 連結獲取對應篇幅的完整程式碼
Product Server
一個普通的 Eureka Client
即可,詳情參考上一章,或從文末的 GITHUB 連結獲取對應篇幅的完整程式碼
Order Server
一個普通的 Eureka Client
依賴
細心的小夥伴會發現,這和一個普通的 Eureka Client
也沒啥區別啊,沒任何額外依賴的,那是因為在 spring-cloud-starter-netflix-eureka-client
中已經幫我們依賴過 spring-cloud-starter-netflix-ribbon
了。假如使用 consul
、 zookeeper
、 etcd
等容器為服務發現為者時,就必須依賴 spring-cloud-starter-netflix-ribbon
包
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies>
配置檔案
在 src/main/resources
目錄下建立一個 bootstrap.yml
的檔案,寫上 eureka 相關配置資訊
server: port: 7072 spring: application: name: order-server eureka: instance: prefer-ip-address: true instance-id: ${spring.cloud.client.ip-address}:${spring.application.instance_id:${server.port}} client: service-url: defaultZone: http://localhost:7071/eureka/
主函式
各位小夥伴對 Spring Boot
中的 RestTemplate
應該都不陌生,它是由 Spring Boot
提供而不是 Spring Cloud
,無負載功能,為了方便開發者, Spring Cloud
團隊提供了一個 @LoadBalanced
註解(預設採用輪訓演算法)
package com.battcn; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; /** * @author Levin */ @EnableDiscoveryClient @SpringBootApplication public class OrderApplication { @Configuration class MyConfiguration { @LoadBalanced @Bean RestTemplate restTemplate() { return new RestTemplate(); } } public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }
控制器
客戶端( order-server:7702
)從 Eureka Server
同步了 product-server:7703
和 product-server:7704
這個時候它是如何知曉登錄檔中的資訊呢?上一章中遺留了一個知識點就是 DiscoveryClient
,通過它就可以獲得登錄檔中客戶端的資訊了,下列程式碼塊演示了 DiscoveryClient
的簡單用法,更多 API 可以自行嘗試
package com.battcn.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; /** * @author Levin * @since 2018/9/28 0028 */ @RestController @RequestMapping("/orders") public class OrderController { private final RestTemplate restTemplate; private final DiscoveryClient discoveryClient; @Autowired public OrderController(RestTemplate restTemplate, DiscoveryClient discoveryClient) { this.restTemplate = restTemplate; this.discoveryClient = discoveryClient; } @GetMapping public String query() { final List<String> services = discoveryClient.getServices(); for (String service : services) { List<ServiceInstance> list = discoveryClient.getInstances(service); for (ServiceInstance instance : list) { System.out.println(instance.getUri() + "/" + service + " - " + instance.getServiceId()); } } return restTemplate.getForObject("http://PRODUCT-SERVER/products", String.class); } }
Why
有的小夥伴對上面的內容會存在一些疑問,為什麼沒有寫 IP:PORT 了,而是寫了一串字元,它是怎麼做到的?
用通俗的概念來說,它就是編碼與解碼操作,還記得 Eureka Server UI 中 Application 嗎? 編碼:根據 spring.application.name 設定 serviceId Server ID:PRODUCT-SERVER Client A :http://localhost:7073/products Client B :http://localhost:7074/products 解碼:通過 serviceId 找到對應的客戶端, 然後根據客戶端配置的負載演算法從對應集合中 找出符合當前演算法條件的結果,最後拼接出相應的 http 地址即可 解碼前:http://PRODUCT-SERVER/products 那麼將 http://PRODUCT-SERVER 替換成 instance.getUri() 內容是不是就出來了 http://localhost:7073/products 和 http://localhost:7074/products
自定義 IRule
假如我們不想使用輪訓了,換換口味改成隨機演算法,又或者想自己寫一套適合自己的負載演算法,可以用下面這種方式
package com.battcn.config; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author Levin * @since 2018/9/28 0028 */ @Configuration public class RibbonRuleConfiguration { @Bean public IRule ribbonRule() { return new RandomRule(); } } @RibbonClient(name = "ribbonRule",configuration = RibbonRuleConfiguration.class) public class OrderController { }
總結
目前很多大佬都寫過關於 Spring Cloud
的教程了,如有雷同,請多多包涵,本教程基於最新的 spring-cloud:Finchley.SR1
編寫…