★微服務系列
1 服務注冊中心
前面我們對業內幾種比較常見的注冊中心做了介紹:Eureka、Zookeeper、Consul、Etcd。
並且在各個指標上做了對比:注冊方式(watch\polling)、健康檢查、雪崩保護、安全與權限,以及在Spring Cloud、Dubbo、Kubernets上的支持程度。方便我們在不同的場景下做正確的技術選型。
指標 | 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,所以當工程啟動之后,訪問 http://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 }
這時候跑一下試試看,繼續訪問之前的地址:http://localhost:1000/ ,可以看到Eureka注冊中心頁面已經包含一個我們定義的服務了,就是上面新建的 1001 端口的服務。
如上圖所示,方括號中的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版本 :https://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端口站點,http://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與端口的依賴轉化為服務名的依賴,服務名可以根據具微服務業務來做標識,因此,屏蔽、解耦服務之間的依賴細節是服務發現與注冊解決的第一個問題。
對微服務進行動態管理
在微服務架構中,服務眾多,服務之間的相互依賴也錯綜復雜,無論是服務主動停止,意外掛掉,還是因為流量增加對服務實現進行擴容,這些服務數據或狀態上的動態變化,都需要盡快的通知到被調用方,被調用方才采取相應的措施。因此,對於服務注冊與發現要實時管理者服務的數據與狀態,包括服務的注冊上線、服務主動下線,異常服務的剔除。