[toc]html
本文首發於個人我的博客,Spring Cloud 負載均衡初體驗 ,歡迎訪問!java
使用 Spring Cloud Netflix 組件 Eureka 和 Ribbon 構建單註冊中心的負載均衡服務。git
Spring Cloud 是基於 Spring 的微服務技術棧,能夠這麼歸納吧,裏面包含了不少例如服務發現註冊、配置中心、消息總線、負載均衡、斷路器、數據監控等組件,能夠經過 Spring Boot 的形式進行集成和使用。github
目前,項目中有這麼個需求,Spring Boot 作一個 web 服務,而後調用 TensorFlow 模型獲得結果。可是因爲 TensorFlow GPU 版,不支持多線程多引擎,因此只能採用多進程的方式去進行調度,因此須要作一個負載均衡。負載均衡的話,能夠分爲客戶端和服務端負載均衡。我目前還沒能領悟到有什麼不一樣,畢竟總體的架構都是同樣的,以下如圖。其中客戶端均衡負載的表明是 Spring Cloud 的 Ribbon,服務端負載均衡表明是 Nginx。web
因爲項目的壓力並不大,日平均請求約 5000 左右,所以就採用 Spring Cloud 中的組件進行客戶端負載均衡。主要用到的就是 Spring Cloud 和 Eureka。不少博客中會也看到 Ribbon 的身影。其實他們都是 Spring Cloud Netflix 中的組件,用來構建微服務。本文所講的例子,也能夠看做是一個微服務,把原來一個的算法服務拆成了若干個小服務。以前講到的 Spring Cloud 的各類組件也都是爲了使得這些獨立的服務可以更好的管理和協做。算法
回到負載均衡,一個使用 Spring Boot 搭建的客戶端負載均衡服務,其實只須要 Rureka 這一個組件就夠了。spring
Eureka 是 Spring Cloud Netflix 當中的一個重要的組件,用於服務的註冊和發現。Eureka 採用了 C-S 的設計架構。具體以下圖,Eureka Server 做爲一個註冊中心,擔任服務中臺的角色,餘下的其餘服務都是 Eureka 的 Client。全部的服務都須要註冊到 Eureka Server 當中去,由它進行統一管理和發現。Eureka Server 做爲管理中心,天然,除了註冊和發現服務外,還有監控等其餘輔助管理的功能。springboot
具體從負載均衡的角度來說:多線程
- Eureka Server——提供服務註冊和發現
- Service Provider——服務提供方,通常有多個服務參與調度
- Service Consumer——服務消費方,從 Eureka 獲取註冊服務列表,從而可以消費服務,也就是請求的直接入口。
服務搭建
下面主要實戰一下負載均衡服務搭建。架構
正如上面所說,一套完整的負載均衡服務,至少須要三個服務。
1.註冊中心——Eureka Server
直接經過 IDEA 建立一個包含 Eureka Server 的 Spring Boot 項目,直接引入所需的 dependency。主要是 spring-cloud-dependencies
和 spring-cloud-starter-netflix-eureka-server
。
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
在啓動類上添加註解 @EnableEurekaServer
。
@EnableEurekaServer @SpringBootApplication public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }
在 yml 中配置 eureka。
server: port: 8080 eureka:. instance: prefer-ip-address: true client: # 表示是否將本身註冊到 Eureka Server,默認爲 true register-with-eureka: false # 表示是否從 Eureka Server 獲取註冊信息,默認爲 true fetch-registry: false service-url: # 默認註冊的域,其餘服務都往這個 url 上註冊 defaultZone: http://localhost:${server.port}/eureka/
因爲目前配置的是單節點的註冊中心,所以 register-with-eureka
和 fetch-registry
都設爲 false,不須要把本身註冊到服務中心,不須要獲取註冊信息。
啓動工程後,訪問:http://localhost:8080/,就能看到一個圖形化的管理界面,目前沒有註冊任何服務。
2.服務提供方——Service Provider
在 yml 中配置 Eureka。
eureka: instance: prefer-ip-address: true # 以 IP 的形式註冊 # 默認是 hostname 開頭的,修改爲 ip 開頭 instance-id: \${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} client: serviceUrl: defaultZone: http://localhost:8080/eureka/ # 註冊到以前的服務中心 server: port: 8084 spring: application: name: services-provider # 應用名稱
默認,Eureka 是經過 hostname 來註冊到 Eureka Server 上的,因爲後面可能涉及到多節點的配置,hostname 可能不如 ip 方便管理,因此將 prefer-ip-address 設爲 true,經過 ip 註冊,並修改 instance-id 格式爲:${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}。注意 ip-address 爲短線鏈接。
而後寫一個簡單的 web 服務作測試。
@RestController public class ServicesController { @RequestMapping("/") public String home() { return "Hello world"; } }
而後運行工程。
3.服務消費方——Service Consumer
新建項目同 2,而後配置 Eureka:
eureka: instance: prefer-ip-address: true instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} client: serviceUrl: defaultZone: http://localhost:8080/eureka/ server: port: 8085 spring: application: name: services-consumer
和以前同樣,注意區分端口號和 name 就能夠了。
再在啓動類,配置 RestTemplate 用於負載均衡的服務分發。這是服務消費的一種基本方式,下面還會介紹第二種 Feign。
@SpringBootApplication public class ServicesConsumerApplication { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(ServicesConsumerApplication.class, args); } }
在 Controller 中的調用
@RestController public class ConsumerController { // services-provider 爲服務 provider 的 application.name,負載均衡要求多個服務的 name 相同 private final String servicesUrl = "http://services-provider/"; private final RestTemplate restTemplate; public ConsumerController(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @RequestMapping("/") public String home() { return restTemplate.getForObject(servicesUrl,String.class); } }
而後運行工程,能夠發現兩個服務都已經成功註冊到註冊中心。
有的博客中可能提到須要添加,@EnableDiscoveryClient 或 @EnableEurekaClient,而實際上,官方文檔已經明確,只要 spring-cloud-starter-netflix-eureka-client 在 classpath 中,應用會自動註冊到 Eureka Server 中去。
By having spring-cloud-starter-netflix-eureka-clienton the classpath, your application automatically registers with the Eureka Server. Configuration is required to locate the Eureka server. https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.2.0.M1/
直接調用 localhost:8085,就顯示 hello world,說明成功!若是不相信的話,能夠起兩個 provider 服務,而後分別輸出不一樣的字符串來驗證負載均衡是否成功。
2019-08-13 15:08:18.689 INFO 14100 --- [nio-8085-exec-4] c.n.l.DynamicServerListLoadBalancer: DynamicServerListLoadBalancer for client services-provider initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=services-provider,current list of Servers=[192.168.140.1:8084]}
簡化後的日誌如上,能夠看到,註解 @LoadBalanced 的負載均衡功能是經過 DynamicServerListLoadBalancer 來實現,這是一種默認的負載均衡策略。獲取到服務列表後,經過 Round Robin 策略(服務按序輪詢從 1 開始,直到 N)進行服務分發。
至此,本地的負載均衡服務就搭建完成了。
服務消費 Feign 與斷路器 Hystrix
若是是初體驗的話能夠忽略這一節。添加這一節的目的是爲了服務的完整性。在實際開發中,Feign 可能用到的更多,而且多會配合 Hystrix。
Feign 也是一種服務消費的方式,採用的是申明式的形式,使用起來更像是本地調用,它也是 Spring Cloud Netflix 中的一員。
Hystrix,簡而言之就是一種斷路器,負載均衡中若是有服務出現問題不可達後,經過配置 Hystrix,能夠實現服務降級和斷路的做用,這個功能 Eureka 默認是不提供的。所以,以前說了,爲了負載均衡的完整性,須要添上它,不然,Ribbon 依然會將請求分發到有問題的服務上去。
那麼要使用他們,首先須要添加依賴
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
在啓動類上加上 @EnableFeignClients
。
調用的時候須要實現接口。
@FeignClient(value = "services-provider") public interface IRestRemote { @GetMapping("/") String home(); }
最後在 Controller 中注入接口,直接調用就能夠了。
@RestController public class ConsumerController { private final IRestRemote remote; public ConsumerController(IRestRemote remote) { this.remote = remote; } @RequestMapping("/") public String home() { return remote.home(); }
相對於 RestTemplate,Fegin 使用起來更簡單,不須要去構造參數請求了,而且 Feign 底層集成了 Ribbon,也不須要顯示添加 @LoadBalanced
註解了。同時 Feign 也能夠直接使用下面要講的 Hystrix。
Hystrix 的功能不少,遠不止斷路器一種,須要詳細瞭解可看別的博客。這裏就講一下 Feign 如何集成 Hystrix 工做的。
下面先描述一下具體場景。假如如今有兩個負載均衡服務,其中有一個掛了或出現異常了,若是沒有斷路器,進行服務分發的時候,仍然會分配到。若是開啓 Hystrix,首先會進行服務降級(FallBack),也就是出現問題,執行默認的方法,並在知足必定的條件下,熔斷該服務。
在原來 @FeignClient 的基礎上添加 fallback
參數, 並實現降級服務。
@FeignClient(value = "services-provider",fallback = RestRemoteHystrix.class) public interface IRestRemote { @GetMapping("/") String home(); }
@Component public class RestRemoteHystrix implements IRestRemote { @override public String home() { return "default"; } }
最後,在配置文件中開啓 Feign 的 Hystrix 開關。
feign: hystrix: enabled: true
下面就能夠測試了,沿着以前的例子,分別開啓兩個服務,並輸出不一樣的文本,當關閉一個服務後,再請求會發現,分配到關閉的那個服務時,會顯示 「default」,請求屢次後發現,該服務不可用,說明斷路器配置成功!欲知更多,能夠閱讀參考文獻 5-6。
特別注意
1.多節點的配置
若是不是單機,則須要修改部分字段,具體以下注釋:
eureka: instance: prefer-ip-address: true # 以 IP 的形式註冊 # 主機的 ip 地址(一樣考慮集羣環境,一個節點可能會配置多個網段 ip,這裏可指定具體的) ip-address: # 主機的 http 外網通訊端口,該端口和 server.post 不一樣,好比外網須要訪問某個集羣節點,直接是沒法訪問 server.post 的,而是須要映射到另外一端口。 # 這個字段就是配置映射到外網的端口號 non-secure-port: # 默認是 hostname 開頭的,修改爲 ip 開頭 instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} client: serviceUrl: # 這裏不是 localhost 了,而是註冊中心的 ip:port defaultZone: http://ip:8080/eureka/ server: port: 8084 spring: application: name: services-provider # 應用名稱
2.高可用註冊中心
本文上述搭建的是單個註冊中心的例子,基本已經知足我目前的項目需求。可是在閱讀別的博客中,發現真正的微服務架構,爲了實現高可用,通常會有多個註冊中心,而且相互註冊造成。這裏先簡單作個記錄。
3.更復雜,自定義的負載均衡規則。
目前,其實只是引入了 spring cloud 和 Eureka 的依賴就實現了簡單的負載均衡。但仔細看 DynamicServerListLoadBalancer 類的位置,是在 Ribbon 下的。雖然沒有顯式去添加 Ribbon 的依賴包,可是實際上已經被包含進去了。那麼 Ribbon 是什麼?
Ribbon is a client-side load balancer that gives you a lot of control over the behavior of HTTP and TCP clients. Feign already uses Ribbon, so, if you use @FeignClient, this section also applies.
Ribbon 是 Spring Cloud 中的一個組件,主要是作客戶端負載均衡的。不關注底層細節,可能上文搭建的服務它無關,實際上仍是用到了。那麼,若是須要自定義複雜均衡的規則,則須要經過配置 Ribbon 來實現。
Summary
本文主要是」拿來主義「的思想,直接用了 Spring Cloud 中的幾個組件來組建一個負載均衡服務。爲了可以更好地進行服務治理,還需循序漸進先學習基本的原理概念,例如CAP理論等,在逐步學習 Cloud 中的組件,有必定的全局觀。
註冊中心 Eureka 自己知足的就是 AP,在生產環境中,爲了保證服務的高可用,勢必要有至少兩個的註冊中心。
Reference
- springcloud(二):註冊中心 Eureka
- springcloud(三):服務提供與調用
- Spring Cloud Netflix
- SpringCloud - 負載均衡器 Ribbon
- Spring Cloud(四):服務容錯保護 Hystrix【Finchley 版】
- Setup a Circuit Breaker with Hystrix, Feign Client and Spring Boot
Source Code
原文出處:https://www.cnblogs.com/Sinte-Beuve/p/11569789.html