帶你入門SpringCloud服務發現 | Eurka搭建和使用
- 2019 年 10 月 3 日
- 筆記
前言
服務註冊與發現是微服務中最為基礎的環節,而 Eureka 就是一個可以幫助你實現服務註冊與發現的選擇之一。如果你對 Eureka 和服務發現了解甚少,那麼該篇部落格將會幫助到你。文中通過具體操作帶你了解如下內容:
- 什麼是服務註冊與發現
- 什麼是 Eureka
- SpringCloud Eureka 單台環境搭建
- SpringCloud Eureka 高可用環境搭建
- SpringCloud Eureka RestTemplate Ribbion 的使用
- SpringCloud Feign SpringCloud Eureka 的使用
閱讀本文需要你熟悉SpringBoot項目的基本使用即可,還有一點需要注意的是在操作過程中盡量和我本地環境一致,因為環境不一致可能會帶來一些問題。我本地環境如下:
SpringBoot Version: 2.1.0.RELEASE
SpringCloud Version: Greenwich.RELEASE
Apache Maven Version: 3.6.0
Java Version: 1.8.0_144
IDEA:Spring Tools Suite (STS)
服務註冊與發現介紹
上面講到服務發現是微服務中最為基礎的環節,什麼是服務發現呢 ?我們可以從單體架構說起,單體架構各個服務都在一起,是不需要被發現的。但是在微服務的架構中會出現很多的服務。服務與服務之間怎麼獲取調用地址URL 呢 ?例如 一個商城網站有商品服務和訂單服務。查詢訂單服務時需要從商品服務中獲取商品的資訊。
如果有1個訂單服務和1 個商品服務,訂單服務服務在配置文件中將商品服務的 URL 配置在配置文件中即可。如果出現 2個商品服務,你可能會想到將2個商品服務URL 配置到訂單服務中。如果出現上千個服務呢?你可能發現這種方式不是一個好的方案。同時還有一個問題就是:商品服務宕機後。如何將宕機的服務從配置文件中剔除。
我們可以將商品服務的 URL 地址資訊都註冊到統一的服務上,在調用時拉取所有商品服務的 URL 資訊。訂單服務可以自定義負載均衡策略來選取一個 URL 地址進行調用,如果註冊中心發現某個商品服務已經宕機,可以將其從註冊中心剔除。而今天的主角 Eureka 就是通過這種機制來完成服務發現的。
接下來就正式開始今天的主角 Eureka 的詳細介紹!
Eureka 是什麼 ?
在介紹操作前,首先來了解一下 Eureka 是什麼?Eureka 是 Netflix 公司開源的一個基於 REST 服務的服務發現框架,主要用於AWS雲,用於定位服務,以實現中間層伺服器的負載平衡和故障轉移。SpringCloud 對 Netflix 眾多的開源框架都封裝到其子項目spring-cloud-netflix 中,而 Eureka 就是其中的一個。SpringCloud 可以通過 Eureka 來完成微服務的服務註冊與發現,當然你也可以使用其他的服務發現框架在 SpringCloud 中。
具體使用和介紹可以訪問 Eureka 的 GitHub wiki 進行查看
https://github.com/Netflix/eureka/wiki/
https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance
閑話少說,開始我們的 Eureka 環境搭建!
SpringCloud Eureka 單台環境搭建
Eureka Server 端搭建
按下圖所示打開SpringBoot Starter構建的窗口 File--New--Spring Starter Project

勾選 Eureka Server

如果不勾選 Eureka Server 也可以在SpringBoot項目中引入 spring-cloud-starter-netflix-eureka-server 依賴即可。程式碼如下:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> 在 SpringBoot Application上聲明 @EnableEurekaServer

在 application.properties 添加配置資訊
server.port=8761 spring.application.name=EUREKASERVER eureka.client.register-with-eureka=false eureka.client.fetch-registry=false eureka.client.service-url.defaultZone= http://localhost:8761/eureka/ eureka.server.enable-self-preservation=false server.port: 配置服務端的埠號。
spring.application.name: 配置服務端應用名稱.。
eureka.client.register-with-eureka: 是否將自己註冊到服務端,Eureka 服務端需要配置成false 默認是 true
eureka.client.fetch-registry:是否從其他Eureka服務端獲取註冊資訊,默認為true。如過是單台伺服器可以設置為false。
eureka.server.enable-self-preservation:開啟自我保護模式,默認為 true 表示為開啟。開發中為了方便可以將其設置為false。
eureka.client.service-url.defaultZone:設置 Eureka 服務端的地址,如果是單體服務就配置該 Eureka 單台服務的 ip 埠號 /eureka。需要注意的是 /eureka 一定要加上。
自我保護模式介紹:
默認情況下,如果Eureka Server在一定時間內(默認90秒)沒有接收到某個微服務實例的心跳,Eureka Server將會移除該實例。但是當網路分區故障發生時,微服務與Eureka Server之間無法正常通訊,而微服務本身是正常運行的,此時不應該移除這個微服務,所以引入了自我保護機制。
自我保護機制的工作機制是如果在15分鐘內超過85%的客戶端節點都沒有正常的心跳,那麼Eureka就認為客戶端與註冊中心出現了網路故障,Eureka Server自動進入自我保護機制,此時會出現以下幾種情況:
1、Eureka Server不再從註冊列表中移除因為長時間沒收到心跳而應該過期的服務。 2、Eureka Server仍然能夠接受新服務的註冊和查詢請求,但是不會被同步到其它節點上,保證當前節點依然可用。 3、當網路穩定時,當前Eureka Server新的註冊資訊會被同步到其它節點中。
自我保護介紹內容摘抄自 —— Java技術棧的部落格: Spring Cloud Eureka 自我保護機制 https://segmentfault.com/a/1190000015349644
如果 eureka.client.register-with-eureka=true 或者不配置該資訊會,Eureka 服務端本身也註冊到服務端如下圖所示:
Eureka Client 端搭建
首先在 SpringBoot Starter 構建的窗口 勾選 Eureka Discovery Client 和 Spring Web Starter 如下圖所示:

需要注意的是如果沒有勾選 Spring Web Starter 客戶端註冊是註冊不到服務端的。
如果不勾選,也可以直接在SpringBoot項目中引入下面2個依賴來完成。
<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> 然後在 SpringBoot Application 上聲明 @EnableDiscoveryClient 如下圖所示:

最後在 application.properties 配置如下資訊:
server.port=8760 spring.application.name=EUREKACLIENT eureka.client.service-url.defaultZone= http://localhost:8762/eureka/ 客戶端的 eureka.client.service-url.defaultZone 配置和Eureka Server(Eureka註冊中心)配置一樣。
訪問 Eureka Server(Eureka註冊中心)端如下圖所示中Instances currently registered With Eureka中查看註冊的客戶端。

如果你按照上述方式搭建並未成功,可以請參考我在GitHub 項目 spring-cloud-get-started 倉庫中模組名為: spring-cloud-eureka-single-service(Eureka Server)和 spring-cloud-eureka-single-service-client( Eureka Client)進行對比查看是否配置有誤。
spring-cloud-get-started 項目地址:https://github.com/zhuoqianmingyue/spring-cloud-get-started
搭建高可用的 Eureka 服務端
在生產環境中為保證 Eureka 服務的高可用,我們需要搭建多台 Eureka Server(Eureka註冊中心)。Eureka Server(Eureka註冊中心)搭建集群還是比較簡單的,只需要讓各個服務端互相註冊即可。官網搭建集群框架圖如下:

我來舉個例子,假如你有2台 Eureka Server 服務,名稱分別為 Eureka1、Eureka2。
Eureka1的配置: 需要將 eureka.client.service-url.defaultZone 配置成 Eureka2 的服務地址。配置程式碼如下:
eureka.client.service-url.defaultZone=http://eureka2:8762/eureka/
Eureka2 的配置 需要將 eureka.client.service-url.defaultZone 配置成 Eureka1 的服務地址。配置程式碼如下:
eureka.client.service-url.defaultZone=http://eureka1:8761/eureka/
假如你有3台 Eureka 服務名稱分別為 Eureka1、Eureka2、Eureka3。
Eureka1的配置: 需要將 eureka.client.service-url.defaultZone 配置上 Eureka2 和 Eureka3的服務地址並用逗號分隔。
eureka.client.service-url.defaultZone=http://eureka2:8762/eureka/,http://eureka3:8763/eureka/
Eureka2的配置: 需要將 eureka.client.service-url.defaultZone 配置上 Eureka1 和 Eureka3的服務地址並用逗號分隔。
eureka.client.service-url.defaultZone=http://eureka1:8761/eureka/,http://eureka3:8763/eureka/
Eureka3的配置: 需要將 eureka.client.service-url.defaultZone 配置上 Eureka1 和 Eureka2的服務地址並用逗號分隔。
eureka.client.service-url.defaultZone=http://eureka1:8761/eureka/,http://eureka2:8762/eureka/
需要注意的是如果配置了 eureka.client.fetch-registry 需要將其改為 true,如果沒有配置可以忽略。eureka.client.fetch-registry 表示可以從其他 Eureka Server 獲取註冊資訊。
還有一點需要注意的是,eureka.client.serviceUrl.defaultZone 配置不能使用 localhost。
如果你使用的是Windows 系統,並且只有一台機器的情況下,需要在 HOSTS 中配置三個域名資訊。
我本地程式碼服務端和客戶端搭建具體配置:
客戶端和服務端引入依賴和添加@EnableXXX的註解比較簡單,我這裡就不在詳細說明了。可以參考 SpringCloud Eureka 單台環境搭建 中的介紹。
主要需要注意的是 application.properties 中的配置,以下是我本地服務端和客戶端配置詳細資訊:
Eureka服務1的配置
server.port=8761 spring.application.name=MULTISERVICEEUREKASERVER1 eureka.client.register-with-eureka=false eureka.client.fetch-registry=true eureka.client.service-url.defaultZone=http://MULTISERVICEEUREKASERVER2:8762/eureka/,http://MULTISERVICEEUREKASERVER3:8763/eureka/ eureka.server.enable-self-preservation=falses Eureka服務2的配置
server.port=8762 spring.application.name=MULTISERVICEEUREKASERVER2 eureka.client.register-with-eureka=false eureka.client.fetch-registry=true eureka.client.service-url.defaultZone=http://MULTISERVICEEUREKASERVER1:8761/eureka/,http://MULTISERVICEEUREKASERVER3:8763/eureka/ eureka.server.enable-self-preservation=false Eureka服務3的配置
server.port=8763 spring.application.name=MULTISERVICEEUREKASERVER3 eureka.client.register-with-eureka=false eureka.client.fetch-registry=true eureka.client.service-url.defaultZone=http://MULTISERVICEEUREKASERVER1:8761/eureka/,http://MULTISERVICEEUREKASERVER2:8762/eureka/ eureka.server.enable-self-preservation=false 客戶端的配置
server.port=8760 spring.application.name=MULTISERVICEEUREKACLIENT eureka.client.service-url.defaultZone=http://MULTISERVICEEUREKASERVER1:8761/eureka/,http://MULTISERVICEEUREKASERVER2:8762/eureka/,http://MULTISERVICEEUREKASERVER3:8763/eureka/ Eureka服務1 控制台介面資訊:

Eureka服務2 控制台介面資訊:

Eureka服務3 控制台介面資訊:

如果你按照上述方式搭建並未成功,可以參考我在 GitHub 項目 spring-cloud-get-started 倉庫中模組名為:
spring-cloud-eureka-multi-service-server1(MULTISERVICEEUREKASERVER1)
spring-cloud-eureka-multi-service-server2(MULTISERVICEEUREKASERVER2)
spring-cloud-eureka-multi-service-server3(MULTISERVICEEUREKASERVER3)
spring-cloud-eureka-multi-service-client(MULTISERVICEEUREKACLIENT)
進行對比查看是否配置有誤。
spring-cloud-get-started 項目地址:https://github.com/zhuoqianmingyue/spring-cloud-get-started
SpringCloud Eureka RestTemplate Ribbion 使用
上面我們介紹如何搭建單機版和高可用版的 Eureka 環境,接下來我們來介紹如果通過 RestTemplate Ribbion 從 Eureka Server 調用我們的 Eureka 客戶端服務的 API 介面。
調用的流程是:消費端從 Eureka Server 獲取我們要調用具體服務生產者(Eureka 客戶端)的所有 URL 地址資訊,然後通過 Ribbion 的負載策略選取一個服務地址。最後通過RestTemplate 進行調用具體服務 API 介面。
我通過一台 Eureka Server( Eureka註冊中心服務)端 和 2個商品服務(Eureka客戶端),外加一個服務消費者訂單服務(Eureka客戶端)。通過訂單服務從 Eureka 註冊中心服務獲取2個商品服務(Eureka客戶端)地址URL並通過 Ribbion 負載策略選取其中一個商品服務URL 進行服務的調用。
** Eureka Server 註冊中心服務的搭建:**
添加 spring-cloud-starter-netflix-eureka-server 依賴,然後再 SpringBoot Application 上聲明 @EnableEurekaServer,最後在 application.properties 中添加相關配置資訊。
spring-cloud-starter-netflix-eureka-server 依賴配置如下:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> application.properties 具體配置資訊如下:
server.port=8761 spring.application.name=EUREKASERVER eureka.client.register-with-eureka=false eureka.client.fetch-registry=false eureka.client.service-url.defaultZone=http://localhost:8761/eureka/ eureka.server.enable-self-preservation=false ** 2個 商品服務 (Eureka 客戶端)搭建:**
添加 spring-cloud-starter-netflix-eureka-client 依賴,然後再 SpringBoot Application 上聲明 @EnableDiscoveryClient ,最後在 application.properties 中添加相關配置資訊。
分別在商品服務1 和商品服務2 中定義一樣的 RestFul 介面,並通過返回不同的商品名稱以示區分。
spring-cloud-starter-netflix-eureka-client 依賴配置如下:
<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> 商品服務1 配置資訊:
server.port=8760 spring.application.name=PRODUCTSERVER eureka.client.service-url.defaultZone=http://localhost:8761/eureka/ 商品服務1 API 介面程式碼如下:
@RestController public class ProductController { @GetMapping("/product/{id}") public Product productInfo(@PathVariable(name="id")Long productId) { Product product = new Product(); product.setId(productId); product.setName("product 1:" "蘋果"); product.setPrice(12d); return product; } } 商品服務 2 配置資訊:
server.port=8770 spring.application.name=PRODUCTSERVER eureka.client.service-url.defaultZone=http://localhost:8761/eureka/ 商品服務 2 API 介面程式碼如下:
@RestController public class ProductController { @GetMapping("/product/{id}") public Product productInfo(@PathVariable(name="id")Long productId) { Product product = new Product(); product.setId(productId); product.setName("product 2:" "蘋果"); product.setPrice(12d); return product; } } 商品類的程式碼如下
public class Product { private Long id; private String name; private Double price; //省略 getter 和 setter } 訂單服務(Eureka客戶端)搭建:
添加 spring-cloud-starter-netflix-eureka-client 依賴,然後再 SpringBoot Application 上聲明 @EnableDiscoveryClient ,最後在 application.properties 中添加相關配置資訊。
server.port=8762 spring.application.name=ORDERSERVICE eureka.client.service-url.defaultZone=http://localhost:8761/eureka/ 商品服務的調用需要使用到 RestTemplate,所以需要配置 RestTemplate 到Spring的上下文中。具體程式碼如下:
@Configuration public class RestTemplateConfig { @Bean public RestTemplate buildRestTemplate() { return new RestTemplate(); } } 通過 LoadBalancerClient 的 choose 方法來獲取 ServiceInstance,ServiceInstance 就是2個 商品服務(PRODUCTSERVER)其中一個服務資訊,LoadBalancerClient 是 SpringCloud 獲取服務的抽象。具體實現是通過 spring-cloud-netflix-ribbon 項目中 RibbonLoadBalancerClient 來實現的。通過 ServiceInstance 獲取商品服務(PRODUCTSERVER)的服務地址 URL。然後通過 RestTemplate 調用 API 介面。具體程式碼如下:
@RestController public class ProductController { @Autowired private LoadBalancerClient loadBalancerClient; @Autowired private RestTemplate restTemplate; @RequestMapping("/product/{id}") public String productInfo(@PathVariable(name="id")Long productId) { ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCTSERVER"); URI uri = serviceInstance.getUri(); String url = uri.toString() "/product/" productId; ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class); String message = forEntity.getBody(); return message; } } 我們也可以通過使用 @LoadBalanced 註解來完成上述操作,使用@LoadBalanced註解的方式相對上面使用 LoadBalancerClient 程式碼簡潔了不少。具體操作如下:
在 RestTemplateConfig 構建方法上添加 @LoadBalanced 註解。具體程式碼如下:
@Configuration public class RestTemplateConfig { @Bean @LoadBalanced public RestTemplate buildRestTemplate() { return new RestTemplate(); } } 在ProductController 中無需在注入 LoadBalancerClient,直接使用 RestTemplate 調用即可。具體程式碼如下:
@RestController public class ProductController { @Autowired private RestTemplate restTemplate; @RequestMapping("/product/{id}") public String productInfo(@PathVariable(name="id")Long productId) { String url = "http://PRODUCTSERVER/product/" productId; ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class); String message = forEntity.getBody(); return message; } } 測試:
整個測試過程需要啟動 一台 註冊中心服務、2台商品服務、1台訂單服務,個人建議註冊中心服務和商品服務通過java -jar的方式啟動,而訂單服務通過 IDEA 來啟動。當然你可以可以都在IDEA 中進行啟動所有的服務。
啟動 Eureka 註冊中心服務:
進入 Eureka 註冊中心服務目錄執行打包命令 mvn clean package -Dmaven.test.skip=true。會生成一個可執行的 jar 包如下圖所示:

然後進入 target目錄 執行 java -jar jar包的名稱,如下圖所示:

如下圖所示:Eureka 註冊服務啟動成功!

啟動商品服務:
進入商品服務項目的目錄,打包並且通過 java -jar 啟動商品服務1項目。具體操作如下圖所示:


商品 2服務操作和上述一致,這裡就不再進行演示
最後訪問服務註冊服務,查看商品1和商品2服務是否註冊到註冊中心服務中,如下圖所示表示註冊成功!

啟動訂單服務:
通過下圖的方式啟動訂單服務:


訪問 http://localhost:8762/product/1 從訂單服務中獲取商品服務的資訊,如下圖所示它會以輪詢的方式調用商品服務1 和商品服務2。


如果你按照上述方式搭建並未成功,可以參考我在GitHub 項目 spring-cloud-get-started 倉庫中模組名為:
spring-cloud-eureka-ribbion-service ( Eureka Server 服務註冊中心 EUREKASERVER )
spring-cloud-eureka-ribbion-product (商品服務1)
spring-cloud-eureka-ribbion-product2(商品服務2)
spring-cloud-eureka-ribbion-service-order-consume(訂單服務)
spring-cloud-eureka-ribbion-service-order-consume-annotation(訂單服務註解版)
進行對比查看是否配置有誤。
spring-cloud-get-started 項目地址:https://github.com/zhuoqianmingyue/spring-cloud-get-started
SpringCloud Feign SpringCloud Eureka 使用
調用 Eureka 客戶端服務還可以通過 SpringCloud Feign 來進行操作。Feign的底層還是使用Ribbion。
接下來我們開始 SpringCloud Feign 是用操作流程介紹,看到這裡你有沒有發SpringCloud 使用的套路。這個套路攏共分三步:
1.引入組件 starter依賴。
2.Application 上添加@EnableXXX註解。
3.appcation.properties上進行添加相關配置資訊。
SpringCloud Feign也繞不開這個套路,第一步及先在訂單服務添加 Feign starter。具體依賴程式碼如下:
<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> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> 第二步是在 SpringBoot Application上聲明 @EnableFeignClients 如下圖所示:

第三步是在application.properties中配置 Eureka客戶端的配置,具體配置資訊如下:
server.port=8762 spring.application.name=ORDERSERVICE eureka.client.service-url.defaultZone=http://localhost:8761/eureka/ 第四步是在 RestTemplateConfig 構建方法上聲明 @LoadBalanced
@Configuration public class RestTemplateConfig { @Bean @LoadBalanced public RestTemplate buildRestTemplate() { return new RestTemplate(); } } 第五步是創建調用介面,並在介面上聲明 @FeignClient(name="PRODUCTSERVER") 其中name的值是調用服務的名稱。在聲明調用的方法 @RequestMapping()中配置 URL 和商品服務 API 介面一樣即可。
@FeignClient(name="PRODUCTSERVER") public interface ProductClient { @GetMapping("/product/{id}") public Product productInfo(@PathVariable(name="id")Long productId); } 第六步開始編寫調用的測試類,在ProductController 中引入 PersonClient 然後調用ProductClient 調用方法即可。
@RestController public class ProductController { @Autowired private ProductClient personClient; @RequestMapping("/product/{id}") public Product productInfo(@PathVariable(name="id")Long productId) { Product product = personClient.productInfo(productId); return product; } } 測試:
Feign 使用 和 Ribbion 使用是一樣的服務註冊中心和商品服務,啟動方式請參考上面Feign 使用操作介紹。
訪問 http://localhost:8762/product/1 調用訂單服務,和使用Ribbion方式效果一致。具體訪問結果如下:


SpringCloud Feign SpringCloud Eureka 使用 和 SpringCloud Eureka RestTemplate Ribbion 使用 共用 Eureka 服務端和服務生產者的項目,如果搭建過程中遇到問題可以參考我在GitHub 項目 spring-cloud-get-started 倉庫中模組名為: spring-cloud-eureka-feign-service-order-consume 進行對比即可。
spring-cloud-get-started 項目地址:https://github.com/zhuoqianmingyue/spring-cloud-get-started
小結
SpringCloud Eureka 搭建和使用只要你掌握了搭建三步法:1.引入組件 starter依賴、2.Application 上添加@EnableXXX註解、3.appcation.properties上進行添加相關配置資訊。關於具體的配置可以結合 Spring 官網文檔進行查閱。
參考文獻
https://spring.io/guides/gs/service-registration-and-discovery/
https://cloud.spring.io/spring-cloud-netflix/reference/html/
https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-ribbon.html
http://seymours.cn/articles/2018/09/19/1537338458105.html


