微服務5:服務註冊與發現(實踐篇)

★微服務系列

微服務1:微服務及其演進史

微服務2:微服務全景架構 

微服務3:微服務拆分策略

微服務4:服務註冊與發現

微服務5:服務註冊與發現(實踐篇)

1 服務註冊中心

前面我們對業內幾種比較常見的註冊中心做了介紹:Eureka、Zookeeper、Consul、Etcd。

並且在各個指標上做了對比:註冊方式(watch\polling)、健康檢查、雪崩保護、安全與許可權,以及在Spring Cloud、Dubbo、Kubernets上的支援程度。方便我們在不同的場景下做正確的技術選型。

4種註冊中心技術對比
指標 Eureka Zookeeper Consul Etcd
一致性協議 AP CP(Paxos演算法) CP(Raft演算法) CP(Raft演算法)
健康檢查 TTL(Time To Live) TCP Keep Alive TTL\HTTP\TCP\Script Lease TTL KeepAlive
watch/long polling 不支援 watch long polling watch
雪崩保護 支援 不支援 不支援 不支援
安全與許可權 不支援 ACL ACL RBAC
是否支援多數據中心 是 否 是 否
是否有管理介面 是 否(可用第三方ZkTools) 是 否
Spring Cloud 集成 支援 支援 支援 支援
Dubbo 集成 不支援 支援 支援 不支援
K8S 集成 不支援 不支援 支援 支援

我們可以看出,四種技術類型對Spring Cloud的支援度都很高。Spring Cloud是微服務架構的一站式解決方案,我們平時構建微服務的過程中需要做的的如 配置管理、服務發現、負載均衡、斷路器、智慧路由、控制匯流排、全局鎖、決策競選、分散式會話和集群狀態管理等操作。Spring Cloud 為我們提供了一套簡易的編程模型,使我們能在 Spring Boot 的基礎上輕鬆地實現微服務項目的構建。

Spring Cloud包含了多個不同開源產品,來保證一站式的微服務解決方案,如:Spring Cloud Config、Spring Cloud Netflix、Spring Cloud Security、Spring Cloud Commons、Spring Cloud Zookeeper、Spring Cloud CLI等項目。

2 Spring Cloud 框架下實現

Spring Cloud為服務治理做了一層抽象,這樣能夠支援多種不同的服務治理框架,比如:Netflix Eureka、Consul。我們這邊就以這兩個為例子,看看服務治理是如何實現。

在Spring Cloud服務治理抽象層的作用下,可以無縫地切換服務治理實現,且不影響任何其他的服務註冊、發現、調用邏輯。

所以,下面我們通過介紹這兩種服務治理的實現來體會Spring Cloud這一層抽象所帶來的好處。

2.1 Spring Cloud Eureka

Spring Cloud Eureka是Spring Cloud Netflix項目下的服務治理模組。而Spring Cloud Netflix項目是Spring Cloud的子項目之一,主要內容是對Netflix公司一系列開源產品的包裝,它為Spring Boot應用提供了自配置的Netflix OSS整合。

通過一些簡單的註解,開發者就可以快速的在應用中配置一下常用模組並構建龐大的分散式系統。它主要提供的模組包括:服務發現(Eureka),斷路器(Hystrix),智慧路由(Zuul),客戶端負載均衡(Ribbon)等。

下面,就來具體看看如何使用Spring Cloud Eureka實現服務治理。

2.1.1 創建註冊中心

創建一個Spring Cloud項目,我們命名為micro-service-center,並在pom.xml中引入需要的依賴內容:

1 <packaging>pom</packaging> 

表明這個項目中可以沒有Java程式碼,也不執行任何程式碼,只是為了聚合工程或者傳遞依賴,所以可以把src文件夾刪了。這是一個父級項目,因為我們還要在下面建立Eureka的註冊中心、客戶端等多個子項目 。

在micro-service-center下,新建一個命名為 eureka-service 的Module,依舊是Spring Cloud 項目,建完之後,pom.xml做如下改動:

 1 <!--    在子工程中添加父工程名稱-->
 2 <parent>
 3     <groupId>com.microservice</groupId>
 4     <artifactId>center</artifactId>
 5     <version>1.0.0</version>
 6 </parent>
 7 
 8 
 9 <dependencies>
10 <!--   加入 eureka 服務 -->
11         <dependency>
12             <groupId>org.springframework.cloud</groupId>
13             <artifactId>spring-cloud-netflix-eureka-server</artifactId>
14         </dependency>
15 </dependencies> 

改完之後,回到父項目micro-service-center,修改pom中的資訊:

 1 <groupId>com.microservice</groupId>
 2 <artifactId>center</artifactId>
 3 <packaging>pom</packaging>
 4 <version>1.0.0</version>
 5 <name>center</name>
 6 <description>Demo project for Spring Boot</description>
 7 
 8 <!--    在父工程添加子工程名稱-->
 9 <modules>
10    <module>eureka-service</module>
11    <module>eureka-client</module>
12 </modules> 

對兩個項目進行clean + install,應該是成功的。

eureka-service我們是作為註冊中心來用的,所以在它的主類Application中加入@EnableEurekaServer註解,就能開啟註冊中心功能。

1 @SpringBootApplication
2 @EnableEurekaServer
3 public class ServiceApplication {
4     public static void main(String[] args) {
5         SpringApplication.run(ServiceApplication.class, args);
6         System.out.println("Start Eureka Service");
7     }
8 }

但是默認情況下,該註冊中心也會把自己當做客戶端,那就變成自己註冊自己了,這個是可以剔除的,我們看一下它的YAML中的詳細配置,注釋比較清楚:

 1 server:
 2   port: 1000
 3 spring:
 4   application:
 5     name: eureka-server
 6 eureka:
 7   instance:
 8     hostname: localhost
 9   client:
10     register-with-eureka: false  # 不作為客戶端進行註冊
11     fetch-registry: false  # 不獲取註冊列表
12     service-url:  # 註冊地址,客戶端需要註冊到該地址中
13       defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ 

文中的注釋還是比較清楚的。 這邊可以看到,埠號是1000,所以當工程啟動之後,訪問 //localhost:1000/ 是可以看到Eureka註冊中心頁面的。其中還沒有發現任何服務。

  

2.1.2 創建客戶端

目前服務中心還是空的,所以我們創建一個能夠提供服務的客戶端,並將其註冊到註冊中心去。

同樣的,我們創建一個Spring Cloud的子項目,命名為eureka-client,pom.xml中的配置如下:

 1 <!--    在子工程中添加父工程名稱-->
 2 <parent>
 3     <groupId>com.microservice</groupId>
 4     <artifactId>center</artifactId>
 5     <version>1.0.0</version>
 6 </parent>
 7 
 8 
 9 <dependencies>
10 
11 <!--    加入 eureka 服務 -->
12 <dependency>
13     <groupId>org.springframework.cloud</groupId>
14     <artifactId>spring-cloud-netflix-eureka-server</artifactId>
15 </dependency>
16 
17 <dependency>
18     <groupId>org.projectlombok</groupId>
19     <artifactId>lombok</artifactId>
20 </dependency>
21 
22 </dependencies> 

在應用主類Application文件中通過加上@EnableDiscoveryClient註解,該註解保證當前服務被Eureka當成provider發現。

1 @SpringBootApplication
2 @EnableDiscoveryClient
3 public class ClientApplication {
4     public static void main(String[] args) {
5         SpringApplication.run(ClientApplication.class, args);
6         System.out.println("start client!");
7     }
8 } 

在YAML文件上加上如下配置:

1 server:
2   port: 1001
3 spring:
4   application:
5     name: eureka-client
6 eureka:
7   client:
8     service-url:  # 這邊就保證了註冊到 eureka-service 這個註冊中心去
9       defaultZone: http://localhost:1000/eureka/ 

spring.application.name屬性,指定了微服務的名稱,在調用的時候可以通過該名稱進行服務訪問。eureka.client.serviceUrl.defaultZone屬性對應服務註冊中心的配置內容,指定服務註冊中心的位置。

大家看到,這邊埠設置為1001,那是因為要在本機上測試 服務提供方 和 服務註冊中心,所以server的port屬性需設置不同的埠。

最後,我們再寫一個介面,通過DiscoveryClient對象,在客戶端中獲取註冊中心的所有服務資訊。

 1 @Controller
 2 @RequestMapping("/eurekacenter")
 3 public class EuServiceController {
 4 
 5     @Autowired
 6     DiscoveryClient discoveryClient;
 7 
 8     /**
 9      * 獲取註冊服務資訊
10      */
11     @RequestMapping(value = "/service", method = {RequestMethod.GET})
12     @ResponseBody
13     public String getServiceInfo() {
14        return  "service:"+discoveryClient.getServices()+" , memo:"+discoveryClient.description();
15     }
16 } 

這時候跑一下試試看,繼續訪問之前的地址://localhost:1000/ ,可以看到Eureka註冊中心頁面已經包含一個我們定義的服務了,就是上面新建的 1001 埠的服務。

  

同樣,我們可以調用上面的那個獲取註冊服務資訊的介面,從服務發現的角度看看有多少個服務被註冊到註冊中心去。 //localhost:1001/eurekacenter/service

 

如上圖所示,方括弧中的eureka-client通過Spring Cloud定義的 getServiceInfo 介面在eureka的實現中獲取到的所有服務清單,他是一個String的List,如果註冊了多個提供者,就會全部顯示。

2.2 Spring Cloud Consul

Consul 用於實現分散式系統的服務發現與配置。與其它分散式服務註冊與發現的方案,Consul 的方案更具「一站式」特徵,內置了服務註冊與發現框 架、分布一致性協議實現、健康檢查、Key/Value 存儲、多數據中心方案,不再需要依賴其它工具(比如 ZooKeeper 之類的)。

而Spring Cloud Consul ,是將其作為一個整體,在微服務架構中為我們的基礎設施提供服務發現和服務配置的工具。

2.2.1 Consul 的優勢

1、使用 Raft 演算法來保證一致性, 比複雜的 Paxos 演算法更直接。

2、支援多數據中心,內外網的服務採用不同的埠進行監聽。 多數據中心集群可以避免單數據中心的單點故障,而其部署則需要考慮網路延遲, 分片等情況等。 zookeeper 和 etcd 均不提供多數據中心功能的支援,上面表格中有體現。

3、支援健康檢查。 

4、支援 http 和 dns 協議介面。 zookeeper 的集成較為複雜, etcd 只支援 http 協議。

5、官方提供 web 管理介面, etcd 無此功能。

2.2.2 Consul的特性

1、服務發現

2、健康檢查

3、Key/Value存儲

4、多數據中心

2.2.3 安裝Consul註冊中心

1、官方下載64版本 ://www.consul.io/downloads.html

2、解壓後複製到目錄 /usr/local/bin 下

3、啟動終端,先看下啥版本的

1 wengzhihua@B000000147796DS ~ % consul --version
2 Consul v1.10.4
3 Revision 7bbad6fe
4 Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents) 

4、執行安裝命令,可以看到他的 Client Addr 的埠為8500。所以訪問 8500埠站點,//127.0.0.1:8500/ui/dc1/services

 1 wengzhihua@B000000147796DS ~ % consul agent -dev
 2 ==> Starting Consul agent...
 3            Version: '1.10.4'
 4            Node ID: '6db154b4-62ff-e67d-e745-1a7270fa1ce8'
 5          Node name: 'B000000147796DS'
 6         Datacenter: 'dc1' (Segment: '<all>')
 7             Server: true (Bootstrap: false)
 8        Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: 8502, DNS: 8600)
 9       Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
10            Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false, Auto-Encrypt-TLS: false 

 

我們可以看到,現在沒有客戶端註冊上來,只有一個自身的實例。

2.2.4 創建服務提供者

由於Spring Cloud Consul項目的實現,我們可以輕鬆的將基於Spring Boot的微服務應用註冊到Consul上,並通過此實現微服務架構中的服務治理。

我們在micro-service-center下新建一個cloud項目consul-client,該項目pom文件添加如下:

 1 <!--    在子工程中添加父工程名稱-->
 2 <parent>
 3     <groupId>com.microservice</groupId>
 4     <artifactId>center</artifactId>
 5     <version>1.0.0</version>
 6 </parent>
 7 
 8 <dependencies>
 9 <!--        Consul服務發現-->
10 <dependency>
11     <groupId>org.springframework.cloud</groupId>
12     <artifactId>spring-cloud-starter-consul-discovery</artifactId>
13 </dependency>
14 <!--        Consul健康檢查-->
15 <dependency>
16     <groupId>org.springframework.boot</groupId>
17     <artifactId>spring-boot-starter-actuator</artifactId>
18 </dependency>
19 </dependencies>

然後修改一下application.yml的配置資訊,將consul配置寫入,注釋應該很清楚了,如下:

1 spring:
2   application:
3     name: consul-producer # 當前服務的名稱
4   cloud:
5     consul: # 以下為Consuk註冊中心的地址,如果安裝的不是這個host和port,這邊可以調整
6       host: localhost
7       port: 8500
8 server:
9   port: 8501 # 當前服務的埠

同樣的,我們要在應用主類Application文件中通過加上@EnableDiscoveryClient註解,該註解保證當前服務被Consul當成provider發現。

大家看到這個做法跟Eureka一樣,因為Spring Cloud對服務治理做的一層抽象,所以可以屏蔽Eureka和Consul服務治理的實現細節,

程式上不需要做改變,只需要引入不同的服務治理依賴,並配置相關的配置屬性 就能輕鬆的將微服務納入Spring Cloud的各個服務治理框架中。

1 @SpringBootApplication
2 @EnableDiscoveryClient
3 public class ConsulClientApplication {
4     public static void main(String[] args) {
5         SpringApplication.run(ClientApplication.class, args);
6     }
7 } 

修改完成之後,我們就可以把這個服務提供者啟動了,然後再去註冊中心查看服務的註冊情況,就可以看到被註冊進來的Provider(consul-producer):

3 總結

除了 Eureka、Consul,還有其他的的註冊中心技術,如Zookeeper、Nocas等。但無論何種註冊中心技術,本質上都是為了解決微服務中的如下問題:

解耦服務之間相互依賴的細節

我們知道服務之間的遠程調用必須要知道對方的IP、埠資訊。我們可以在調用方直接配置被調用方的IP、埠,這種調用方直接依賴IP、埠的方式存在明顯的問題,如被調用的IP、埠變化後,調用方法也要同步修改。 

通過服務發現,將服務之間IP與埠的依賴轉化為服務名的依賴,服務名可以根據具微服務業務來做標識,因此,屏蔽、解耦服務之間的依賴細節是服務發現與註冊解決的第一個問題。 

對微服務進行動態管理

在微服務架構中,服務眾多,服務之間的相互依賴也錯綜複雜,無論是服務主動停止,意外掛掉,還是因為流量增加對服務實現進行擴容,這些服務數據或狀態上的動態變化,都需要儘快的通知到被調用方,被調用方才採取相應的措施。因此,對於服務註冊與發現要實時管理者服務的數據與狀態,包括服務的註冊上線、服務主動下線,異常服務的剔除。