Spring Cloud(三):服務提供與調用 Eureka【Finchley 版】
上一篇文章我們介紹了 Eureka 服務注冊中心的搭建,這篇文章介紹一下如何使用 Eureka 服務注冊中心,搭建一個簡單的服務端注冊服務,客戶端去調用服務使用的案例。
案例中有三個角色:服務注冊中心、服務提供者、服務消費者,其中服務注冊中心就是我們上一篇的 Eureka 單節點啟動既可。
流程如下:
服務提供者
我們假設服務提供者有一個 hello()
方法,可以根據傳入的參數,提供輸出 “hello xxx + 當前時間” 的服務。
POM 包配置
創建一個基本的 Spring Boot 應用,命名為eureka-producer
,在 pom.xml 中添加如下配置:
1 |
<dependency> |
配置文件
application.yml 配置如下
1 |
spring: |
通過spring.application.name
屬性,我們可以指定微服務的名稱后續在調用的時候只需要使用該名稱就可以進行服務的訪問。eureka.client.serviceUrl.defaultZone
屬性對應服務注冊中心的配置內容,指定服務注冊中心的位置。為了在本機上測試區分服務提供方和服務注冊中心,使用server.port
屬性設置不同的端口。
啟動類
保持默認生成的即可, Finchley.RC1 這個版本的 Spring Cloud 已經無需添加@EnableDiscoveryClient
注解了。(那么如果我引入了相關的 jar 包又想禁用服務注冊與發現怎么辦?設置eureka.client.enabled=false
)
@EnableDiscoveryClient
is no longer required. You can put aDiscoveryClient
implementation on the classpath to cause the Spring Boot application to register with the service discovery server.
Spring Cloud - @EnableDiscoveryClient
1 |
|
Controller
提供 hello 服務
1 |
|
啟動工程后,就可以在注冊中心 Eureka 的頁面看到 EUERKA-PRODUCER 服務。
我們模擬一個請求試一下 Producer 能否正常工作
http://localhost:8000/hello/?name=windmt
1 |
Hello, windmt Fri Apr 13 18:36:36 CST 2018 |
OK, 直接訪問時沒有問題的,到此服務提供者配置就完成了。
服務消費者
創建服務消費者根據使用 API 的不同,大致分為三種方式。雖然大家在實際使用中用的應該都是 Feign,但是這里還是把這三種都介紹一下吧,如果你只關心 Feign,可以直接跳到最后。
三種方式均使用同一配置文件,不再單獨說明了
1 |
spring: |
使用 LoadBalancerClient
從LoadBalancerClient
接口的命名中,我們就知道這是一個負載均衡客戶端的抽象定義,下面我們就看看如何使用 Spring Cloud 提供的負載均衡器客戶端接口來實現服務的消費。
POM 包配置
我們先來創建一個服務消費者工程,命名為:eureka-consumer
。pom.xml 同 Producer 的,不再贅述。
啟動類
初始化RestTemplate
,用來發起 REST 請求。
1 |
|
Controller
創建一個接口用來消費 eureka-producer 提供的接口:
1 |
|
可以看到這里,我們注入了LoadBalancerClient
和RestTemplate
,並在hello
方法中,先通過loadBalancerClient
的choose
方法來負載均衡的選出一個eureka-producer
的服務實例,這個服務實例的基本信息存儲在ServiceInstance
中,然后通過這些對象中的信息拼接出訪問服務調用者的/hello/
接口的詳細地址,最后再利用RestTemplate
對象實現對服務提供者接口的調用。
另外,為了在調用時能從返回結果上與服務提供者有個區分,在這里我簡單處理了一下,name+="!"
,即服務調用者的 response 中會比服務提供者的多一個感嘆號(!)。
訪問 http://localhost:9000/hello/?name=windmt 以驗證是否調用成功
1 |
Hello, windmt! Fri Apr 13 18:44:55 CST 2018 |
Spring Cloud Ribbon
之前已經介紹過 Ribbon 了,它是一個基於 HTTP 和 TCP 的客戶端負載均衡器。它可以通過在客戶端中配置 ribbonServerList 來設置服務端列表去輪詢訪問以達到均衡負載的作用。
當 Ribbon 與 Eureka 聯合使用時,ribbonServerList 會被 DiscoveryEnabledNIWSServerList 重寫,擴展成從 Eureka 注冊中心中獲取服務實例列表。同時它也會用 NIWSDiscoveryPing 來取代 IPing,它將職責委托給 Eureka 來確定服務端是否已經啟動。
POM 包配置
將之前的 eureka-consumer 工程復制一份,並命名為 eureka-consumer-ribbon。
pom.xml 文件還用之前的就行。至於 spring-cloud-starter-ribbon,因為我使用的 Spring Cloud 版本是 Finchley.RC1,spring-cloud-starter-netflix-eureka-client 里邊已經包含了 spring-cloud-starter-netflix-ribbon 了。
1 |
<dependency> |
啟動類
修改應用主類,為RestTemplate
添加@LoadBalanced
注解
1 |
|
Controller
修改 controller,去掉LoadBalancerClient
,並修改相應的方法,直接用 RestTemplate
發起請求
1 |
|
可能你已經注意到了,這里直接用服務名eureka-producer
取代了之前的具體的host:port
。那么這樣的請求為什么可以調用成功呢?因為 Spring Cloud Ribbon 有一個攔截器,它能夠在這里進行實際調用的時候,自動的去選取服務實例,並將這里的服務名替換成實際要請求的 IP 地址和端口,從而完成服務接口的調用。
訪問 http://localhost:9001/hello/?name=windmt 以驗證是否調用成功
1 |
Hello, windmt! Fri Apr 13 22:24:14 CST 2018 |
也可以通過啟動多個 eureka-producer 服務來觀察其負載均衡的效果。
Spring Cloud Feign
在實際工作中,我們基本上都是使用 Feign 來完成調用的。我們通過一個例子來展現 Feign 如何方便的聲明對 eureka-producer 服務的定義和調用。
POM 包配置
創建一個基本的 Spring Boot 應用,命名為eureka-producer-feign
,在 pom.xml 中添加如下配置:
1 |
<dependency> |
啟動類
在啟動類上加上@EnableFeignClients
1 |
|
Feign 調用實現
創建一個 Feign 的客戶端接口定義。使用@FeignClient
注解來指定這個接口所要調用的服務名稱,接口中定義的各個函數使用 Spring MVC 的注解就可以來綁定服務提供方的 REST 接口,比如下面就是綁定 eureka-producer 服務的/hello/
接口的例子:
1 |
|
此類中的方法和遠程服務中 Contoller 中的方法名和參數需保持一致。
這里有幾個坑,后邊有詳細說明。
Controller
修改 Controller,將 HelloRemote 注入到 controller 層,像普通方法一樣去調用即可
1 |
|
通過 Spring Cloud Feign 來實現服務調用的方式非常簡單,通過@FeignClient
定義的接口來統一的聲明我們需要依賴的微服務接口。而在具體使用的時候就跟調用本地方法一點的進行調用即可。由於 Feign 是基於 Ribbon 實現的,所以它自帶了客戶端負載均衡功能,也可以通過 Ribbon 的 IRule 進行策略擴展。另外,Feign 還整合的 Hystrix 來實現服務的容錯保護,這個在后邊會詳細講。(在 Finchley.RC1 版本中,Feign 的 Hystrix 默認是關閉的。參考 Spring Cloud OpenFeign 和 Disable HystrixCommands For FeignClients By Default)。
在我的 IDEA 里,這里會有錯誤提示,如下
這個其實不用管,運行的時候會被正確注入。如果嫌這個提示煩,可以在HelloRemote
這個接口上邊加@Component
注解。
訪問 http://localhost:9002/hello/windmt 以驗證是否調用成功
1 |
Hello, windmt! Sat Apr 14 01:03:56 CST 2018 |
踩坑記錄
問題一:not have available server
1 |
com.netflix.client.ClientException: Load balancer does not have available server for client: eureka-producer |
這個問題剛開始困擾了我好長時間,最后發現原來是因為我沒加入 eureka-client 這個依賴,只加了 spring-boot-starter-web 和 spring-cloud-starter-openfeign。只有后兩者的話,啟動的時候其實是不會有任何異常被拋出的,
但是如果細心地查看了啟動 log 的話,其中有這么一條可以看出實際上確實是沒有獲取到任何服務的
1 |
c.netflix.loadbalancer.BaseLoadBalancer : Client: eureka-producer instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=eureka-producer,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null |
所以,要想使用 Feign,至少需要以下三個依賴
- spring-boot-starter-web
- spring-cloud-starter-openfeign
- spring-cloud-starter-netflix-eureka-client
問題二:Request method ‘POST’ not supported
1 |
feign.FeignException: status 405 reading HelloRemote#hello(String); content: |
HelloRemote
中的代碼是這樣的,出現上邊的異常
1 |
|
改成這樣,還是同樣的異常
1 |
|
再改,這次 OK 了
1 |
|
這個問題挺奇葩的的,不加@RequestParam
就變成了 POST 請求,不論我是用@GetMapping
還是method = RequestMethod.GET
,也是無語了。
至於怎么想到的加了個@RequestParam(value = "name")
,說來話長。一開始我沒加 eureka-client 依賴,也沒加@RequestParam
注解,一啟動就報異常:
1 |
Caused by: java.lang.IllegalStateException: PathVariable annotation was empty on param 0. |
所以就加了@RequestParam
,算是歪打正着吧。
至於為什么出現這個 GET 變 POST 的情況,個人猜測應該是當參數沒有被@RequestParam
注解修飾時,會自動被當做 request body 來處理。只要有 body,就會被 Feign 認為是 POST 請求,所以整個服務是被當作帶有 request parameter 和 body 的 POST 請求發送出去的。
負載均衡
以上三種方式都能實現負載均衡,都是以輪詢訪問的方式實現的。這個以大家常用的 Feign 的方式做一個測試。
以上面 eureka-producer 為例子修改,將其中的 controller 改動如下:
1 |
|
打包啟動
1 |
// 打包 |
訪問 http://localhost:9002/hello/windmt 進行測試。在不斷的測試下去會發現兩種結果交替出現
1 |
[1]Hello, , windmt Sun Apr 15 19:37:15 CST 2018 |
這說明兩個服務中心自動提供了服務均衡負載的功能。如果我們將服務提供者的數量在提高為 N 個,測試結果一樣,請求會自動輪詢到每個服務端來處理。
相關閱讀
Spring Cloud(一):服務治理技術概覽
Spring Cloud(二):服務注冊與發現 Eureka
Spring Cloud(三):服務提供與調用 Eureka
Spring Cloud(四):服務容錯保護 Hystrix
Spring Cloud(五):Hystrix 監控面板
Spring Cloud(六):Hystrix 監控數據聚合 Turbine
Spring Cloud(七):配置中心(Git 版與動態刷新)
Spring Cloud(八):配置中心(服務化與高可用)
Spring Cloud(九):配置中心(消息總線)
Spring Cloud(十):服務網關 Zuul(路由)
Spring Cloud(十一):服務網關 Zuul(過濾器)
Spring Cloud(十二):分布式鏈路跟蹤(Sleuth 與 Zipkin)
示例代碼:GitHub
參考
Spring Cloud 構建微服務架構:服務消費(基礎)【Dalston 版】
Spring Cloud 構建微服務架構:服務消費(Ribbon)【Dalston 版】
Spring Cloud 構建微服務架構:服務消費(Feign)【Dalston 版】
springcloud(三):服務提供與調用
Spring Cloud OpenFeign
Disable HystrixCommands For FeignClients By Default
Feign 使用 Hystrix 無效原因及解決方法
spring cloud-Feign 使用中遇到的問題總結
- 本文鏈接: https://windmt.com/2018/04/15/spring-cloud-3-service-producer-and-consumer/
- 版權聲明: 本博客所有文章除特別聲明外,均采用 CC BY-NC-SA 4.0 許可協議。轉載請注明出處!