服務治理SpringCloud Eureka
什麼是服務治理
在傳統rpc遠端調用中,服務與服務依賴關系,管理比較複雜,是以需要使用服務治理,管理服務與服務之間依賴關系,可以實作服務調用、負載均衡、容錯等,實作服務發現與注冊。
服務注冊與發現
在服務注冊與發現中,有一個注冊中心,當伺服器啟動的時候,會把目前自己伺服器的資訊 比如 服務位址通訊位址等以别名方式注冊到注冊中心上。
另一方(消費者|服務提供者),以該别名的方式去注冊中心上擷取到實際的服務通訊位址,然後在實作本地rpc調用遠端。
搭建注冊中心
1、pom檔案引入
<!-- 管理依賴 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.M7</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!--SpringCloud eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<!-- 注意: 這裡必須要添加, 否者各種依賴有問題 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
2、appliaction.yml配置檔案
###eureka 服務端口号
server:
port: 8100
###服務注冊名稱
eureka:
instance:
###注冊中心ip位址
hostname: 127.0.0.1
###用戶端調用位址
client:
serviceUrl:
###注冊位址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
###因為該應用為注冊中心,不會注冊自己(叢集是需要為true)
register-with-eureka: false
###因為自己為注冊中心 ,不會去在該應用中的檢測服務
fetch-registry: false
3、啟動項
@SpringBootApplication
//開啟EurekaServer服務
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
4、通路Eureka注冊中心
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL90ERPJTU610dRRVT3V1MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLycTO1UTNyETMzEDNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
注冊服務提供者
1、pom檔案引入
<!-- 管理依賴 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.M7</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- SpringBoot整合Web元件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- SpringBoot整合eureka用戶端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<!-- 注意: 這裡必須要添加, 否者各種依賴有問題 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
2、配置檔案
###會員項目的端口号
server:
port: 8000
###服務名稱(服務注冊到eureka名稱)
spring:
application:
name: app-springCloud-member
###服務注冊到eureka位址
eureka:
client:
service-url:
###目前服務注冊到Eureka服務位址
defaultZone: http://localhost:8100/eureka
###需要将會員服務注冊到Eureka服務中
register-with-eureka: true
###需要檢索服務
fetch-registry: true
3、啟動項
@SpringBootApplication
//将目前服務注冊到Eureka上
@EnableEurekaClient
public class AppMember {
public static void main(String[] args) {
SpringApplication.run(AppMember.class,args);
}
}
4、後端相關代碼
@RestController
public class MemberApiController {
@Value("${server.port}")
private String serverPort;
@RequestMapping("/getMember")
public String getMember(){
return "歡迎來到會員服務:端口号"+serverPort;
}
}
5、通路Eureka服務
注冊服務消費者
1、pom檔案引入
<!-- 管理依賴 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.M7</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- SpringBoot整合Web元件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- SpringBoot整合eureka用戶端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<!-- 注意: 這裡必須要添加, 否者各種依賴有問題 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
2、配置檔案
###服務啟動端口号
server:
port: 8001
###服務名稱(服務注冊到eureka名稱)
spring:
application:
name: app-springCloud-order
###服務注冊到eureka位址
eureka:
client:
service-url:
###目前服務注冊到Eureka服務位址
defaultZone: http://localhost:8100/eureka
###需要将會員服務注冊到Eureka服務中
register-with-eureka: true
###需要檢索服務
fetch-registry: true
3、啟動項
@SpringBootApplication
//将目前服務注冊到Eureka上
@EnableEurekaClient
public class AppOrder {
public static void main(String[] args) {
SpringApplication.run(AppOrder.class,args);
}
//Spring預設沒有對RestTemplate進行管理,
// 是以直接使用@Autowired進入不到Springboot容器中
// 需要手動将RestTemplate注入,通過@Bean注解的方式
@Bean
// 如果使用rest方式以别名方式進行調用依賴ribbon負載均衡器 @LoadBalanced
// @LoadBalanced就能讓這個RestTemplate在請求時擁有用戶端負載均衡的能力
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
4、後端相關代碼
@RestController
public class OrderApiController {
// RestTemplate 是有SpringBoot Web元件提供 預設整合ribbon負載均衡器
// rest方式底層是采用httpclient技術
@Autowired
private RestTemplate restTemplate;
// 訂單服務調用會員服務
@RequestMapping("/getOrder")
public String getOrder() {
// 有兩種方式,一種是采用服務别名方式調用,
// 另一種是直接調用 使用别名去注冊中心上擷取對應的服務調用位址
String url = "http://app-springCloud-member/getMember";
String result = restTemplate.getForObject(url, String.class);
System.out.println("訂單服務調用會員服務result:" + result);
return result;
}
}
5、消費者通路服務者
服務者叢集
隻需要修改服務者的配置檔案中的端口号即可,将端口号由原來的8000改為8100,再啟動新的服務(注意不是重新開機服務),這樣Eureka中心就有兩個服務者了。
此時消費者通路服務者就會出現負載均衡效果了。
高可用注冊中心
在微服務中,注冊中心非常核心,可以實作服務治理,如果一旦注冊出現故障的時候,可能會導緻整個微服務無法通路,在這時候就需要對注冊中心實作高可用叢集模式。
Eureka高可用原理
Eureka高可用實際上将自己作為服務向其他服務注冊中心注冊自己,這樣就可以形成一組互相注冊的服務注冊中心,進而實作服務清單的互相同步,達到高可用效果。
1、建立多個注冊服務,過程同上。
2、唯一需要注意的是修改配置檔案,已達到互相注冊的效果。
###eureka 服務端口号
server:
port: 8100
###定義服務注冊名稱(叢集的服務名稱一定要相同)
spring:
application:
name: app-springCloud-eureka
eureka:
instance:
###注冊中心ip位址
hostname: 127.0.0.1
###用戶端調用位址
client:
serviceUrl:
###注冊位址
defaultZone: http://${eureka.instance.hostname}:8200/eureka/
###因為是叢集,是以需要改為true
register-with-eureka: true
###因為是叢集,是以需要改為true
fetch-registry: true
###eureka 服務端口号
server:
port: 8200
###定義服務注冊名稱(叢集的服務名稱一定要相同)
spring:
application:
name: app-springCloud-eureka
eureka:
instance:
###注冊中心ip位址
hostname: 127.0.0.1
###用戶端調用位址
client:
serviceUrl:
###注冊位址
defaultZone: http://${eureka.instance.hostname}:8100/eureka/
###因為是叢集,是以需要改為true
register-with-eureka: true
###因為是叢集,是以需要改為true
fetch-registry: true
注意,如果想要達成叢集的效果,服務名稱一定要是相同的。
3、通路各自的注冊中心
4、修改服務者配置檔案
需要将服務注冊到Eureka叢集中
###會員項目的端口号
server:
port: 8010
###服務名稱(服務注冊到eureka名稱)
spring:
application:
name: app-springCloud-member
###服務注冊到eureka位址
eureka:
client:
service-url:
###目前服務注冊到Eureka服務位址
defaultZone: http://localhost:8100/eureka,http://localhost:8200/eureka
###需要将會員服務注冊到Eureka服務中
register-with-eureka: true
###需要檢索服務
fetch-registry: true
5、修改消費者配置檔案
###服務啟動端口号
server:
port: 8001
###服務名稱(服務注冊到eureka名稱)
spring:
application:
name: app-springCloud-order
###服務注冊到eureka位址
eureka:
client:
service-url:
###目前服務注冊到Eureka服務位址
defaultZone: http://localhost:8100/eureka,http://localhost:8200/eureka
###需要将會員服務注冊到Eureka服務中
register-with-eureka: true
###需要檢索服務
fetch-registry: true
6、通路各自的注冊中心
因為在注冊的過程中,隻會保證一台注冊中心有對應的服務資訊資料(類似于zookeeper),當8200注冊中心當機後,自動啟動轉移同步資料到8100上去的。
Eureka自我保護機制
預設情況下,EurekaClient會定時向EurekaServer端發送心跳,如果EurekaServer在一定時間内沒有收到EurekaClient發送的心跳,便會把該執行個體從注冊服務清單中剔除(預設是90秒),但是在短時間内丢失大量的執行個體心跳,這時候EurekaServer會開啟自我保護機制,Eureka不會踢出該服務。
關閉自我保護機制
修改Eureka服務端配置檔案
server:
# 測試時關閉自我保護機制,保證不可用服務及時踢出
enable-self-preservation: false
eviction-interval-timer-in-ms: 2000
修改Eureka用戶端配置檔案
# 心跳檢測檢測與續約時間
# 測試時将值設定設定小些,保證服務關閉後注冊中心能及時踢出服務
instance:
###Eureka用戶端向服務端發送心跳的時間間隔,機關為秒(用戶端告訴服務端自己會按照該規則)
lease-renewal-interval-in-seconds: 1
####Eureka服務端在收到最後一次心跳之後等待的時間上限,機關為秒,超過則剔除(用戶端告訴服務端按照此規則等待自己)
lease-expiration-duration-in-seconds: 2
但是,萬萬不幸的是,Eureka閉源了,我們可以想辦法用其他的注冊中心替代Eureka:Zookeeper、Consul。
使用Consul來替代Eureka
簡介
Consul 是一套開源的分布式服務發現和配置管理系統,由 HashiCorp 公司用 Go 語言開發。
它具有很多優點。包括: 基于 raft 協定,比較簡潔; 支援健康檢查, 同時支援 HTTP 和 DNS 協定 支援跨資料中心的 WAN 叢集,提供圖形界面 跨平台,支援 Linux、Mac、Windows。
環境搭建
1、下載下傳
Consul下載下傳位址https://www.consul.io/downloads.html
下載下傳window版,解壓得到一個可執行檔案。
2、設定環境變量
在path後面添加consul所在目錄。
3、啟動consul命
讓我們直接在cmd裡可直接使用consul使命。
consul agent -dev -ui -node=cy
-dev開發伺服器模式啟動,-node結點名為cy,-ui可以用界面通路,預設能通路。
4、通路Consul
http://localhost:8500
Consul用戶端
1、pom檔案引入
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<!-- 管理依賴 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.M7</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- SpringBoot整合Web元件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--SpringCloud consul-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
</dependencies>
<!-- 注意: 這裡必須要添加, 否者各種依賴有問題 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
2、配置檔案
###服務端口号
server:
port: 8502
spring:
### 服務名稱
application:
name: consul-member
####consul注冊中心位址
cloud:
consul:
### consul位址
host: localhost
### ### consul端口号
port: 8500
discovery:
##服務位址直接為ip位址
hostname: 192.168.18.220
###預設情況下 服務注冊到注冊中心 位址随機生成英文 pc-yushengjun:
### 換不同注冊中心的時候,接口調用方式都不變 無非變化 配置檔案和maven依賴資訊
3、啟動項
@EnableDiscoveryClient 與@EnableEurekaClient差別
- @EnableDiscoveryClient注解是基于spring-cloud-commons依賴,并且在classpath中實作; 适合于consul、zookeeper注冊中心
- @EnableEurekaClient注解是基于spring-cloud-netflix依賴,隻能為eureka作用
@SpringBootApplication
@EnableDiscoveryClient
// @EnableEurekaClient 是Eureka使用的
// @EnableDiscoveryClient 作用是 如果服務使用connsul、zookeeper 使用
// @EnableDiscoveryClient 向注冊中心上注冊服務
public class ConsulAPP {
public static void main(String[] args) {
SpringApplication.run(ConsulAPP.class, args);
}
}
4、DiscoveryClient用法
discoveryClient接口,可以擷取注冊中心上的執行個體資訊。
@RequestMapping("/discoveryClientMember")
public List<ServiceInstance> discoveryClientMember() {
List<ServiceInstance> instances = discoveryClient.getInstances("consul-member");
for (ServiceInstance serviceInstance : instances) {
System.out.println("url:" + serviceInstance.getUri());
}
return instances;
}
5、訂單服務調用會員服務
// springcloud 中使用feign或者rest技術實作調用服務接口
// 訂單服務調用會員服務
@RequestMapping("/orderToMember")
public String orderToMember() {
// 有兩種方式,一種是采用服務别名方式調用,
// 另一種是直接調用 使用别名去注冊中心上擷取對應的服務調用位址
//第一種方式:String memberUrl = "http://zk-member/getMember";
//第二種方式:使用别名
String serviceUrl = getServiceUrl("consul-member") + "/getMember";
String result = restTemplate.getForObject(serviceUrl, String.class);
System.out.println("訂單服務調用會員服務result:" + result);
return result;
}
public String getServiceUrl(String name) {
List<ServiceInstance> list = discoveryClient.getInstances(name);
if (list != null && !list.isEmpty()) {
return list.get(0).getUri().toString();
}
return null;
}
使用Zookeeper來替代Eureka
簡介
Zookeeper是一個分布式協調工具,可以實作服務注冊與發現、注冊中心、消息中間件、分布式配置中心等。
ZK用戶端
1、啟動zk伺服器
2、pom檔案引入
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<!-- 管理依賴 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.M7</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- SpringBoot整合Web元件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- SpringBoot整合eureka用戶端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
</dependencies>
<!-- 注意: 這裡必須要添加, 否者各種依賴有問題 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
3、配置檔案
會員配置檔案
###服務端口号
server:
port: 8000
###服務名稱
spring:
application:
name: zk-member
cloud:
zookeeper:
###注冊到zookeeper位址
connect-string: 127.0.0.1:2181
訂單配置檔案
###服務端口号
server:
port: 8060
###服務名稱
spring:
application:
name: zk-order
cloud:
zookeeper:
###注冊到zookeeper位址
connect-string: 127.0.0.1:2181
啟動zk-member服務和zk-order服務,可以發現在Zk伺服器端上有對應的節點資訊。
Zookeeper與Eureka差別
首先,我們需要了解CPA理論。
CAP:一個分布式系統不可能同時滿足C(一緻性)、A(可用性)和P(分區容錯性)。由于分區容錯性在是分布式系統中必須要保證的,是以我們隻能在A和C之間進行權衡。在此Zookeeper保證的是CP, 而Eureka則是AP。
Consistency(一緻性), 資料一緻更新,所有資料變動都是同步的
Availability(可用性), 好的響應性能
Partition tolerance(分區容忍性) 可靠性
1、“C”是指一緻性,即當一個Process(過程)修改了某個資料後,其他Process讀取這是資料是,得到的是更新後的資料,但并不是所有系統都 可以做到這一點。例如,在一些并非嚴格要求一緻性的系統中,後來的Process得到的資料可能還是修改之前的資料,或者需要等待一定時間後才能得到修改 之後的資料,這被成為“弱一緻性”,最經典的應用就是DNS系統。當使用者修改了DNS配置後,往往不會馬上在全網更新,必定會有一個延遲,這個延遲被稱為 “不一緻視窗”,它的長度取決于系統的負載、備援的個數等因素。但對于某些系統而言,一旦寫入,後面讀取的一定是修改後的資料,如銀行賬戶資訊,這被稱為 “強一緻性”。
2、“A”是指可用性。即系統總是能夠為使用者提供連續的服務能力。當使用者送出請求是,系統能給出響應(成功或者失敗),而且是立即給出響應,而不是等待其他事情完成才響應。如果需要等待某件事情完成才響應,那麼“可用性”就不存在了。
3、“P”是指容錯性。任何一個分布式計算系統都是由多個節點組成的。在正常情況下,節點與節點之間的通信是正常的。但是在某些情況下,節點之間的通信會 斷開,這種斷開成為“Partition”。在分布式計算的實作中,Partition是很常見的,因為節點不可能永遠不出故障,尤其是對于跨實體地區的 海量存儲系統而言,而容錯性則可以保證如果隻是系統中的部分節點不可用,那麼相關的操作仍舊能夠正常完成。
Zookeeper是保證CP
當向注冊中心查詢服務清單時,我們可以容忍注冊中心傳回的是幾分鐘以前的注冊資訊,但不能接受服務直接down掉不可用。也就是說,服務注冊功能對可用性的要求要高于一緻性。但是zk會出現這樣一種情況,當master節點因為網絡故障與其他節點失去聯系時,剩餘節點會重新進行leader選舉。問題在于,選舉leader的時間太長,30 ~ 120s, 且選舉期間整個zk叢集都是不可用的,這就導緻在選舉期間注冊服務癱瘓。在雲部署的環境下,因網絡問題使得zk叢集失去master節點是較大機率會發生的事,雖然服務能夠最終恢複,但是漫長的選舉時間導緻的注冊長期不可用是不能容忍的。
Eureka是保證AP
Eureka看明白了這一點,是以在設計時就優先保證可用性。Eureka各個節點都是平等的,幾個節點挂掉不會影響正常節點的工作,剩餘的節點依然可以提供注冊和查詢服務。而Eureka的用戶端在向某個Eureka注冊或時如果發現連接配接失敗,則會自動切換至其它節點,隻要有一台Eureka還在,就能保證注冊服務可用(保證可用性),隻不過查到的資訊可能不是最新的(不保證強一緻性)。除此之外,Eureka還有一種自我保護機制,如果在15分鐘内超過85%的節點都沒有正常的心跳,那麼Eureka就認為用戶端與注冊中心出現了網絡故障,此時會出現以下幾種情況:
1. Eureka不再從注冊清單中移除因為長時間沒收到心跳而應該過期的服務
2. Eureka仍然能夠接受新服務的注冊和查詢請求,但是不會被同步到其它節點上(即保證目前節點依然可用)
3. 當網絡穩定時,目前執行個體新的注冊資訊會被同步到其它節點中
是以, Eureka可以很好的應對因網絡故障導緻部分節點失去聯系的情況,而不會像zookeeper那樣使整個注冊服務癱瘓。