天天看點

SpringCloud注冊中心-Eureka

1.Eureka注冊中心

1.1.Eureka簡介

首先我們來解決第一問題,服務的管理。

問題分析

在剛才的案例中,user-service對外提供服務,需要對外暴露自己的位址。而consumer(調用者)需要記錄服務提供者的位址。将來位址出現變更,還需要及時更新。這在服務較少的時候并不覺得有什麼,但是在現在日益複雜的網際網路環境,一個項目肯定會拆分出十幾,甚至數十個微服務。此時如果還人為管理位址,不僅開發困難,将來測試、釋出上線都會非常麻煩,這與DevOps的思想是背道而馳的。

網約車

這就好比是網約車出現以前,人們出門叫車隻能叫計程車。一些私家車想做出租卻沒有資格,被稱為黑車。而很多人想要約車,但是無奈計程車太少,不友善。私家車很多卻不敢攔,而且滿大街的車,誰知道哪個才是願意載人的。一個想要,一個願意給,就是缺少引子,缺乏管理啊。

此時滴滴這樣的網約車平台出現了,所有想載客的私家車全部到滴滴注冊,記錄你的車型(服務類型),身份資訊(聯系方式)。這樣提供服務的私家車,在滴滴那裡都能找到,一目了然。

此時要叫車的人,隻需要打開APP,輸入你的目的地,選擇車型(服務類型),滴滴自動安排一個符合需求的車到你面前,為你服務,完美!

Eureka是Netflix的一個子子產品,也是核心子產品之一。Eureka是一個基于REST服務,用于定位服務,以實作雲端中間層服務發現和故障轉移。服務注冊與發現對于微服務架構來說是非常重要的,有了服務發現與注冊,隻需要使用服務的辨別符,就可以通路到服務,而不需要修改服務調用的配置檔案了。功能類似于dubbo的注冊中心,比如Zookeeper。

Netflix在設計Eureka是遵循的就是AP原則

分布式系統的CAP理論:理論首先把分布式系統中的三個特性進行了如下歸納:

●一緻性(C):在分布式系統中的所有資料備份,在同一時刻是否同樣的值。(等同于所有節點通路同一份最新的資料副本)

●可用性(A):在叢集中一部分節點故障後,叢集整體是否還能響應用戶端的讀寫請求。(對資料更新具備高可用性)

●分區容錯性(P):以實際效果而言,分區相當于對通信的時限要求。系統如果不能在時限内達成資料一緻性,就意味着發生了分區的情況,必須就目前操作在C和A之間做出選擇。

●CAP原則隻能三選二

最多隻能同時較好的滿足兩個。

CAP理論的核心足:一個分布式系統不可能同時很好的滿足一緻性,可用性和分區容錯性這止個需求,是以,根據CAP原理将NOSQL資料庫分成了滿足CA原則、滿足CP原則和滿足AP原則三大類:

CA單點叢集,滿足一緻性,可川性的系統,通常在可擴充性上不太強大。

CP滿足一緻性,分區容忍必的系統,通常性能不是特别高。

AP滿足可用統,通常可能對一緻性要求低一些。

SpringCloud注冊中心-Eureka

Eureka遵守AP原則

zookeeper遵守CP原則

雙11、618隻能用AP

1.2.Eureka基本架構

SpringCloud封裝了Netflix公司開發的Eureka子產品來實作服務注冊和發現(請對比Zookeeper)。Eureka采用了C-S的設計架構。EurekaServer作為服務注冊功能的伺服器,它是服務注冊中心。而系統中的其他微服務,使用Eureka的用戶端連接配接到EurekaServer并維持心路連接配接。這樣系統的維護人員就可以通過EurekaServer來監控系統中各個微服務是否正常運作。SpringCloud的一些其他子產品(比如Zuul)就可以通過EurekaServer來發現系統中的其他微服務,并執行相關的邏輯。

SpringCloud注冊中心-Eureka
  • Eureka:就是服務注冊中心(可以是一個叢集),對外暴露自己的位址
  • 提供者:啟動後向Eureka注冊自己資訊(位址,提供什麼服務)
  • 消費者:向Eureka訂閱服務,Eureka會将對應服務的所有提供者位址清單發送給消費者,并且定期更新
  • 心跳(續約):提供者定期通過http方式向Eureka重新整理自己的狀态

注意和Dubbo對比:

SpringCloud注冊中心-Eureka

1.2.1 EurekaServer

EurekaServer作為一個獨立的部署單元,以RESTAPI的形式為服務執行個體提供了注冊、管理和查詢等操作。同時,EurekaServer也為我們提供了可視化的監控頁面,可以直覺地看到各個EurekaServer目前的運作狀态和所有已注冊服務的情況。

EurekaServer節點啟動後,會首先嘗試從鄰近節點擷取所有執行個體系統資料庫資訊,完成初始化。EurekaServer通過getEurekaServiceUrls()方法擷取所有的節點,并且會通過心跳續約的方式定期更新。預設配置下,如果EurekaServer在一定時間内沒有接收到某個服務執行個體的心跳,EurekaServer将會登出該執行個體(預設為90秒,通過eureka.instance.lease-expiration-duration-in-seconds配置)。當EurekaServer節點在短時間内丢失過多的心跳時(比如發生了網絡分區故障),那麼這個節點就會進入自我保護模式。

自我保護模式:

預設配置下,如果EurekaServer每分鐘收到心跳續約的數量低于一個門檻值(instance的數量(60/每個instance的心跳間隔秒數)自我保護系數),并且持續15分鐘,就會觸發自我保護。在自我保護模式中,EurekaServer會保護服務系統資料庫中的資訊,不再登出任何服務執行個體。當它收到的心跳數重新恢複到門檻值以上時,該EurekaServer節點就會自動退出自我保護模式。它的設計理念就是甯可保留錯誤的服務注冊資訊,也不盲目登出任何可能健康的服務執行個體。該模式可以通過eureka.server.enable-self-preservation=false來禁用,同時eureka.instance.lease-renewal-interval-in-seconds可以用來更改心跳間隔,eureka.server.renewal-percent-threshold可以用來修改自我保護系數(預設0.85)。

1.2.2 EurekaClient

●服務注冊:

啟動時,會調用服務注冊方法,向EurekaServer注冊自己的資訊。EurekaServer會維護一個已注冊服務的清單。當執行個體狀态發生變化時(如自身檢測認為Down的時候),也會向EurekaServer更新自己的服務狀态,同時用replicateToPeers()向其它EurekaServer節點做狀态同步。

●續約與剔除:

服務執行個體啟動後,會周期性地向EurekaServer發送心跳以續約自己的資訊,避免自己的注冊資訊被剔除。續約的方式與服務注冊基本一緻,首先更新自身狀态,再同步到其它Peer。如果EurekaServer在一段時間内沒有接收到某個微服務節點的心跳,EurekaServer将會登出該微服務節點(自我保護模式除外)。

●服務消費:ServiceConsumer本質上也是一個EurekaClient。它啟動後,會從EurekaServer上擷取所有執行個體的注冊資訊,包括IP位址、端口等,并緩存到本地。這些資訊預設每30秒更新一次。前文提到過,如果與EurekaServer通信中斷,ServiceConsumer仍然可以通過本地緩存與ServiceProvider通信。

●三處緩存

EurekaServer對注冊清單進行緩存,預設時間為30s。

EurekaClient對擷取到的注冊資訊進行緩存,預設時間為30s。

Ribbon會從上面提到的EurekaClient擷取服務清單,将負載均衡後的結果緩存30s。

2.Eureka項目的建構

我們做三個角色

EurekaServer:提供服務注冊和發現;

ServiceProvider:服務提供方,将自身服務注冊到Eureka,進而使服務消費方能夠找到;

ServiceConsumer:服務消費方,從Eureka擷取注冊服務清單,進而能夠消費服務。

2.1 建立一個工程EurekaServer-7001

●依然使用spring提供的快速搭建工具:

SpringCloud注冊中心-Eureka

●選擇依賴:

SpringCloud注冊中心-Eureka

●完整的Pom檔案:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.4.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>com.bruceliu.eureka.server</groupId>
  <artifactId>eureka-server-7001</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>eureka-server-7001</name>
  <description>Demo project for Spring Boot</description>

  <properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>${spring-cloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>      

●編寫啟動類:

@SpringBootApplication
@EnableEurekaServer // 聲明這個應用是一個EurekaServer
public class EurekaServer7001Application {

  public static void main(String[] args) {
    SpringApplication.run(EurekaServer7001Application.class, args);
  }

}      

●編寫配置:

server:
  port: 7001 # 端口
spring:
  application:
    name: eureka-server-7001 # 應用名稱,會在Eureka中顯示
eureka:
  client:
    register-with-eureka: false # 是否注冊自己的資訊到EurekaServer,預設是true
    fetch-registry: false #false表示自己端就是注冊中心,我的職責就是維護服務執行個體,并不需要去檢索服務
    service-url: # EurekaServer的位址,現在是自己的位址,如果是叢集,需要加上其它Server的位址。
      defaultZone: http://127.0.0.1:${server.port}/eureka      

●啟動服務,并通路:​​http://127.0.0.1:7001​​​

SpringCloud注冊中心-Eureka
SpringCloud注冊中心-Eureka
2.2.将user-service注冊到Eureka

注冊服務,就是在服務上添加Eureka的用戶端依賴,用戶端代碼會自動把服務注冊到EurekaServer中。

我們在springcloud-demo中添加Eureka用戶端依賴:

●先添加SpringCloud依賴:

<!-- SpringCloud的依賴 -->
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>${spring-cloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>      

●然後是Eureka用戶端:

<!-- Eureka用戶端 -->
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  </dependency>      
在啟動類上開啟Eureka用戶端功能

通過添加​

​@EnableDiscoveryClient​

​來開啟Eureka用戶端功能

@SpringBootApplication
@EnableDiscoveryClient // 開啟EurekaClient功能
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
        System.out.println("提供者啟動:7001");
    }
}      

●編寫配置

# 應用名稱
spring.application.name=springcloud-demo-service
# EurekaServer位址
eureka.client.service-url.defaultZone=http://127.0.0.1:7001/eureka
# 當調用getHostname擷取執行個體的hostname時,傳回ip而不是host名稱
eureka.instance.prefer-ip-address=true
# 指定自己的ip資訊,不指定的話會自己尋找
eureka.instance.ip-address=127.0.0.1      

注意:

  • 這裡我們添加了spring.application.name屬性來指定應用名稱,将來會作為應用的id使用。
  • 不用指定register-with-eureka和fetch-registry,因為預設是true

●重新開機項目,通路Eureka監控頁面檢視

SpringCloud注冊中心-Eureka

我們發現service服務已經注冊成功了!!!

2.2 消費者從Eureka擷取服務

接下來我們修改springcloud-demo-consumer,嘗試從EurekaServer擷取服務。

方法與消費者類似,隻需要在項目中添加EurekaClient依賴,就可以通過服務名稱來擷取資訊了!

添加依賴:

●先添加SpringCloud依賴:

<!-- SpringCloud的依賴 -->
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>${spring-cloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>      

●然後是Eureka用戶端:

<!-- Eureka用戶端 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>      

●在啟動類開啟Eureka用戶端:

@SpringBootApplication
@EnableDiscoveryClient // 開啟EurekaClient功能
public class SpringcloudDemoConsumerApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringcloudDemoConsumerApplication.class, args);
  }
}      

●修改配置:

spring.application.name=springcloud-demo-consumer
# EurekaServer位址
eureka.client.service-url.defaultZone=http://127.0.0.1:7001/eureka
# 當調用getHostname擷取執行個體的hostname時,傳回ip而不是host名稱
eureka.instance.prefer-ip-address=true
# 指定自己的ip資訊,不指定的話會自己尋找
eureka.instance.ip-address=127.0.0.1      

●修改代碼,用DiscoveryClient類的方法,根據服務名稱,擷取服務執行個體:

/**
 * @author bruceliu
 * @create 2019-05-02 15:52
 * @description
 */
@RestController
@RequestMapping("consumer")
public class ConsumerController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient client;// Eureka用戶端,可以擷取到服務執行個體資訊

    @RequestMapping("/test")
    public List<User> consumerTest(){
        List<String>list=client.getServices();
        System.out.println("*服務清單*"+list);
        List<ServiceInstance> srvList=client.getInstances("springcloud-demo-service");
        for(ServiceInstance element:srvList){
            System.out.println(element.getServiceId()+"\t"+element.getHost()+"\t"+element.getPort()+"\t" +element.getUri());
        }
        // 因為隻有一個UserService,是以我們直接get(0)擷取
        ServiceInstance instance = srvList.get(0);

        // 擷取ip和端口資訊
        String baseUrl = "http://"+instance.getHost() + ":" + instance.getPort()+"/all";
        System.out.println("通路位址:"+baseUrl);

        return this.restTemplate.getForObject(baseUrl,List.class);
    }
}      

●注冊中心:

SpringCloud注冊中心-Eureka

●通路測試:

SpringCloud注冊中心-Eureka

3.Eureka詳解

接下來我們詳細講解Eureka的原理及配置。

3.1.基礎架構

Eureka架構中的三個核心角色:

  • 服務注冊中心

    Eureka的服務端應用,提供服務注冊和發現功能,就是剛剛我們建立的eureka-server-7001

  • 服務提供者

    提供服務的應用,可以是SpringBoot應用,也可以是其它任意技術實作,隻要對外提供的是Rest風格服務即可。本例中就是我們實作的springcloud-demo

  • 服務消費者

    消費應用從注冊中心擷取服務清單,進而得知每個服務方的資訊,知道去哪裡調用服務方。本例中就是我們實作的springcloud-demo-consumer

3.2 actuator與注冊微服務資訊完善

●主機名稱:服務名稱修改預設:

SpringCloud注冊中心-Eureka

●在springcloud-demo屬性檔案中加入

eureka.instance.instance-id=springcloud-demo-service-80      

●修改後,檢視頁面

SpringCloud注冊中心-Eureka

●通路資訊有IP資訊提示在springcloud-demo屬性檔案中加入

# 當調用getHostname擷取執行個體的hostname時,傳回ip而不是host名稱
eureka.instance.prefer-ip-address=true      
SpringCloud注冊中心-Eureka

●微服務info内容詳情預設:

SpringCloud注冊中心-Eureka
  • POM中加入
<!--actuator監控資訊完善-->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-actuator</artifactId>
   </dependency>      
  • 屬性檔案中加入
info.app.name=springcloud-demo-service
info.company.name=www.itxiongmao.com
info.build.artifactId=$project.artifactId$
info.build.version=$project.version$      
  • 測試:
    SpringCloud注冊中心-Eureka
3.3.高可用的Eureka Server

Eureka Server即服務的注冊中心,在剛才的案例中,我們隻有一個EurekaServer,事實上EurekaServer也可以是一個叢集,形成高可用的Eureka中心。

服務同步

多個Eureka Server之間也會互相注冊為服務,當服務提供者注冊到Eureka Server叢集中的某個節點時,該節點會把服務的資訊同步給叢集中的每個節點,進而實作資料同步。是以,無論用戶端通路到Eureka Server叢集中的任意一個節點,都可以擷取到完整的服務清單資訊。

動手搭建高可用的EurekaServer

我們假設要搭建兩條EurekaServer的叢集,端口分别為:7001、7002、7003

1)我們修改原來的EurekaServer配置-7001:

server:
  port: 7001 # 端口
spring:
  application:
    name: eureka-server-7001 # 應用名稱,會在Eureka中顯示
eureka:
  client:
    register-with-eureka: false # 是否注冊自己的資訊到EurekaServer,預設是true
    fetch-registry: false #false表示自己端就是注冊中心,我的職責就是維護服務執行個體,并不需要去檢索服務
    service-url: # EurekaServer的位址,現在是自己的位址,如果是叢集,需要加上其它Server的位址。
      defaultZone: http://127.0.0.1:7002/eureka,http://127.0.0.1:7003/eureka      

所謂的高可用注冊中心,其實就是把EurekaServer自己也作為一個服務進行注冊,這樣多個EurekaServer之間就能互相發現對方,進而形成叢集。是以我們做了以下修改:

  • 把service-url的值改成了另外一台EurekaServer的位址,而不是自己

2)我們修改原來的EurekaServer配置-7002:

server:
  port: 7002 # 端口
spring:
  application:
    name: eureka-server-7002 # 應用名稱,會在Eureka中顯示
eureka:
  client:
    register-with-eureka: false # 是否注冊自己的資訊到EurekaServer,預設是true
    fetch-registry: false #false表示自己端就是注冊中心,我的職責就是維護服務執行個體,并不需要去檢索服務
    service-url: # EurekaServer的位址,現在是自己的位址,如果是叢集,需要加上其它Server的位址。
      defaultZone: http://127.0.0.1:7001/eureka,http://127.0.0.1:7003/eureka      

3)我們修改原來的EurekaServer配置-7003:

server:
  port: 7003 # 端口
spring:
  application:
    name: eureka-server-7003 # 應用名稱,會在Eureka中顯示
eureka:
  client:
    register-with-eureka: false # 是否注冊自己的資訊到EurekaServer,預設是true
    fetch-registry: false #false表示自己端就是注冊中心,我的職責就是維護服務執行個體,并不需要去檢索服務
    service-url: # EurekaServer的位址,現在是自己的位址,如果是叢集,需要加上其它Server的位址。
      defaultZone: http://127.0.0.1:7001/eureka,http://127.0.0.1:7002/eureka      

4)在自己的host中加入端口映射

127.0.0.1       eureka7001.com
127.0.0.1       eureka7002.com
127.0.0.1       eureka7003.com      

5)啟動測試

注意:idea中一個應用不能啟動兩次,我們需要重新配置一個啟動器:

SpringCloud注冊中心-Eureka
SpringCloud注冊中心-Eureka

然後啟動即可。

SpringCloud注冊中心-Eureka
SpringCloud注冊中心-Eureka
SpringCloud注冊中心-Eureka

6)springcloud-demo叢集版,用戶端注冊服務到叢集

因為EurekaServer不止一個,是以注冊服務的時候,service-url參數需要變化:

# EurekaServer位址
eureka.client.service-url.defaultZone= http:// eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka      

7)測試服務是否注冊

SpringCloud注冊中心-Eureka
3.4.服務提供者

服務提供者要向EurekaServer注冊服務,并且完成服務續約等工作。

服務注冊

服務提供者在啟動時,會檢測配置屬性中的:​

​eureka.client.register-with-erueka=true​

​參數是否正确,事實上預設就是true。如果值确實為true,則會向EurekaServer發起一個Rest請求,并攜帶自己的中繼資料資訊,Eureka Server會把這些資訊儲存到一個雙層Map結構中。第一層Map的Key就是服務名稱,第二層Map的key是服務的執行個體id。

服務續約

在注冊服務完成以後,服務提供者會維持一個心跳(定時向EurekaServer發起Rest請求),告訴EurekaServer:“我還活着”。這個我們稱為服務的續約(renew);

有兩個重要參數可以修改服務續約的行為:

eureka:
  instance:
    lease-expiration-duration-in-seconds: 90
    lease-renewal-interval-in-seconds: 30      
  • lease-renewal-interval-in-seconds:服務續約(renew)的間隔,預設為30秒
  • lease-expiration-duration-in-seconds:服務失效時間,預設值90秒

也就是說,預設情況下每個30秒服務會向注冊中心發送一次心跳,證明自己還活着。如果超過90秒沒有發送心跳,EurekaServer就會認為該服務當機,會從服務清單中移除,這兩個值在生産環境不要修改,預設即可。

但是在開發時,這個值有點太長了,經常我們關掉一個服務,會發現Eureka依然認為服務在活着。是以我們在開發階段可以适當調小。

eureka:
  instance:
    lease-expiration-duration-in-seconds: 10 # 10秒即過期
    lease-renewal-interval-in-seconds: 5 # 5秒一次心跳      
執行個體id

先來看一下服務狀态資訊:

在Eureka監控頁面,檢視服務注冊資訊:

執行個體id

先來看一下服務狀态資訊:

在Eureka監控頁面,檢視服務注冊資訊:

在status一列中,顯示以下資訊:

  • UP(1):代表現在是啟動了1個示例,沒有叢集
  • DESKTOP-2MVEC12:user-service:8081:是示例的名稱(instance-id),
  • 預設格式是:​

    ​${hostname} + ${spring.application.name} + ${server.port}​

  • instance-id是區分同一服務的不同執行個體的唯一标準,是以不能重複。

我們可以通過instance-id屬性來修改它的構成:

eureka:
  instance:
    instance-id: ${spring.application.name}:${server.port}      

重新開機服務再試試看:

SpringCloud注冊中心-Eureka

在status一列中,顯示以下資訊:

  • UP(1):代表現在是啟動了1個示例,沒有叢集
  • localhost:springcloud-demo-service:80:是示例的名稱(instance-id),
  • 預設格式是:​

    ​${hostname} + ${spring.application.name} + ${server.port}​

  • instance-id是區分同一服務的不同執行個體的唯一标準,是以不能重複。

我們可以通過instance-id屬性來修改它的構成:

eureka:
  instance:
    instance-id: ${spring.application.name}:${server.port}      

重新開機服務再試試看:

SpringCloud注冊中心-Eureka
3.5.服務消費者
擷取服務清單

當服務消費者啟動是,會檢測​

​eureka.client.fetch-registry=true​

​​參數的值,如果為true,則會從Eureka Server服務的清單隻讀備份,然後緩存在本地。并且​

​每隔30秒​

​會重新擷取并更新資料。我們可以通過下面的參數來修改:

eureka:
  client:
    registry-fetch-interval-seconds: 5      

生産環境中,我們不需要修改這個值。

但是為了開發環境下,能夠快速得到服務的最新狀态,我們可以将其設定小一點。

3.5.失效剔除和自我保護
失效剔除

有些時候,我們的服務提供方并不一定會正常下線,可能因為記憶體溢出、網絡故障等原因導緻服務無法正常工作。Eureka Server需要将這樣的服務剔除出服務清單。是以它會開啟一個定時任務,每隔60秒對所有失效的服務(超過90秒未響應)進行剔除。

可以通過​

​eureka.server.eviction-interval-timer-in-ms​

​參數對其進行修改,機關是毫秒,生成環境不要修改。

這個會對我們開發帶來極大的不變,你對服務重新開機,隔了60秒Eureka才反應過來。開發階段可以适當調整,比如10S

自我保護

我們關停一個服務,就會在Eureka面闆看到一條警告:

SpringCloud注冊中心-Eureka

這是觸發了Eureka的自我保護機制。當一個服務未按時進行心跳續約時,Eureka會統計最近15分鐘心跳失敗的服務執行個體的比例是否超過了85%。在生産環境下,因為網絡延遲等原因,心跳失敗執行個體的比例很有可能超标,但是此時就把服務剔除清單并不妥當,因為服務可能沒有當機。Eureka就會把目前執行個體的注冊資訊保護起來,不予剔除。生産環境下這很有效,保證了大多數服務依然可用。

但是這給我們的開發帶來了麻煩, 是以開發階段我們都會關閉自我保護模式:

eureka:
  server:
    enable-self-preservation: false # 關閉自我保護模式(預設為打開)
    eviction-interval-timer-in-ms: 1000 # 掃描失效服務的間隔時間(預設為60*1000ms)      
3.6 作為服務注冊中心,Eureka比zookeeper好在哪

CAP理論指出,一個分布式系統不可能同時滿足C(一緻性)、A(可用性)和P(分區容錯性)。由于分區容錯性P在是分布式系統中必須要保證的,是以我們隻能在A和C之間進行權衡。

是以Zookeeper保證的是CP,Eureka則是AP。

3.6.1 Zookeeper保證CP

當向注冊中心查詢服務清單時,我們可以容忍注冊中心傳回的是幾分鐘以前的注冊資訊,但不能接受服務直接down掉不可用。也就是說,服務注冊功能對可用性的要求要高于一緻性。但是zk會出現這樣一種情況,當master節點因為網絡故障與其他節點失去聯系時,剩餘節點會重新進行leader選舉。問題在于,選舉leader的時間太長,30~120s,且選舉期間整個zk叢集都是不可用的,這就導緻在選舉期間注冊服務癱瘓。在雲部署的環境下,因網絡問題使得zk叢集失去master節點是較大機率會發生的事,雖然服務能夠最終恢複,但是漫長的選舉時間導緻的注冊長期不可用是不能容忍的。

3.6.2 Eureka保證AP

  • Eureka不再從注冊清單中移除因為長時間沒收到心跳而應該過期的服務
  • Eureka仍然能夠接受新服務的注冊和查詢請求,但是不會被同步到其它節點上(即保證目前節點依然可用)
  • 當網絡穩定時,目前執行個體新的注冊資訊會被同步到其它節點中

繼續閱讀