前言 服務註冊與發現是微服務中最為基礎的環節,而 Eureka 就是一個可以幫助你實現服務註冊與發現的選擇之一。如果你對 Eureka 和服務發現瞭解甚少,那麼該篇博客將會幫助到你。文中通過具體操作帶你瞭解如下內容: 1. 什麼是服務註冊與發現 2. 什麼是 Eureka 3. SpringClou ...
前言
服務註冊與發現是微服務中最為基礎的環節,而 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。具體依賴代碼如下:
html <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客戶端的配置,具體配置信息如下:
html 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