SpringCloud系列之一---搭建高可用的Eureka注冊中心

前言

本篇文章主要介紹的是SpringCloud相關(guān)知識、微服務架構(gòu)以及搭建服務注冊與發(fā)現(xiàn)的服務模塊(Eureka)以及Eureka集群。

GitHub源碼鏈接位于文章底部。

什么是SpringCloud

Spring Cloud 是一系列框架的有序集合。 它利用 Spring Boot 的開發(fā)便利性巧妙地簡化了分布式系統(tǒng)基礎(chǔ)設(shè)施的開發(fā), 如服務發(fā)現(xiàn)注冊、配置中心、消息總線、負載均衡、熔斷器、數(shù)據(jù)監(jiān)控等,都可以用 Spring Boot 的開發(fā)風格做到一鍵啟動和部署。Spring 并沒有復制造輪子,它只是將目前各家公司開發(fā)的比較成熟、經(jīng)得起實際考驗的服務框架組合起來,通過 SpringBoot 風格進行再封裝屏蔽掉了復雜的配置和實現(xiàn)原理,最終開發(fā)者留出了一套簡單易懂、易部署和易維護的分布式系統(tǒng)開發(fā)工具包。

SpringCloud 與 SpringBoot 的關(guān)系

Spring Boot是Spring的一套快速配置腳手架,可以基于Spring Boot快速開發(fā)單個微服務,Spring Cloud是一個基于Spring Boot實現(xiàn)的云應用開發(fā)工具;Spring Boot專注于快速、方便集成的單個微服務個體,Spring Cloud關(guān)注全局的服務治理框架;Spring Boot 使用了默認大于配置的理念,很多集成方案已經(jīng)幫你選擇好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot來實現(xiàn),可以不基于Spring Boot嗎?不可以。
Spring Boot可以離開Spring Cloud獨立使用開發(fā)項目, 但是Spring Cloud離不開Spring Boot,屬于依賴的關(guān)系。

SpringCloud 主要組件

用途 組件
服務發(fā)現(xiàn) Netflix Eureka
服務調(diào)用 Netflix Feign
熔斷器 Netflix Hystrix
服務網(wǎng)關(guān) Netflix Zuul
分布式配置 Spring Cloud Config
消息總線 Spring Cloud Bus

SpringCloud 與 Dubbo 對比

dubbo由于是二進制的傳輸,占用帶寬會更少。
springCloud是http協(xié)議傳輸,帶寬會比較多,同時使用http協(xié)議一般會使用JSON報文,消耗會更大。
dubbo的開發(fā)難度較大,原因是dubbo的jar包依賴問題很多大型工程無法解決。
Dubbo只是實現(xiàn)了服務治理,而Spring Cloud下面有很多個子項目分別覆蓋了微服務架構(gòu)下的方方面面,服務治理只是其中的一個方面,一定程度來說,Dubbo 只是 Spring CloudNetflix 中的一個子集。

Dubbo SpringCloud
服務注冊中心 Zookeeper Spring Cloud Netflix Eureka
服務調(diào)用方式 RPC REST API
服務網(wǎng)關(guān) Spring Cloud Netflix Zuul /Spring Cloud GateWay
熔斷器 不完善 Spring Cloud Netflix Hystrix
分布式配置 Spring Cloud Config
服務跟蹤 Spring Cloud Sleuth
消息總線 Spring Cloud Bus
數(shù)據(jù)流 Spring Cloud Stream
批量任務 Spring Cloud Task
...... ...... ......

SpringCloud 的版本

SpringCloud 由于是一系列框架組合,為了避免與包含的自框架版本產(chǎn)生混淆,采用倫敦地鐵站的名稱作為版本名,形式為版本名+里程碑號。M9為第 9 個里程碑版本。以下是SpringBoot與Spring Cloud版本的對照表。

Spring Boot Spring Cloud
1.2.x Angel 版本
1.3.x Brixton 版本
1.4.x Camden 版本
1.5.x Dalston 版本、 Edgware 版本
2.0.x Finchley 版本

服務發(fā)現(xiàn)組件 Eureka

Eureka是Netflix 開發(fā)的服務發(fā)現(xiàn)框架,SpringCloud將它集成在自己的子項目spring-cloud-netflix中,實現(xiàn)SpringCloud的服務發(fā)現(xiàn)功能。Eureka包含兩個組件:Eureka Server和Eureka Client。

Eureka Server提供服務注冊服務,各個節(jié)點啟動后,會在Eureka Server中進行注冊,這樣EurekaServer中的服務注冊表中將會存儲所有可用服務節(jié)點的信息,服務節(jié)點的信息可以在界面中直觀的看到。

自我保護機制

Eureka Client是一個java客戶端,用于簡化與Eureka Server的交互,客戶端同時也就別一個內(nèi)置的、使用輪詢(round-robin)負載算法的負載均衡器。在應用啟動后,將會向EurekaServer發(fā)送心跳,默認周期為30秒,如果Eureka Server在多個心跳周期內(nèi)沒有接收到某個節(jié)點的心跳,Eureka Server將會從服務注冊表中把這個服務節(jié)點移除(默認90秒)。但是在短時間內(nèi)丟失大量的實例心跳,這時候EurekaServer會開啟自我保護機制,Eureka不會踢出該服務,這就是Eureka的自我保護機制。
產(chǎn)生原因:在開發(fā)測試時,需要頻繁地重啟微服務實例,但是我們很少會把eureka server一起重啟(因為在開發(fā)過程中不會修改eureka注冊中心),當一分鐘內(nèi)收到的心跳數(shù)大量減少時,會觸發(fā)該保護機制。可以在eureka管理界面看到Renews threshold和Renews(last min),當后者(最后一分鐘收到的心跳數(shù))小于前者(心跳閾值)的時候,觸發(fā)保護機制,會出現(xiàn)紅色的警告:

EMERGENCY!EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT.RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEGING EXPIRED JUST TO BE SAFE.

從警告中可以看到,eureka認為雖然收不到實例的心跳,但它認為實例還是健康的,eureka會保護這些實例,不會把它們從注冊表中刪掉。

該保護機制的目的是避免網(wǎng)絡連接故障,在發(fā)生網(wǎng)絡故障時,微服務和注冊中心之間無法正常通信,但服務本身是健康的,不應該注銷該服務,如果eureka因網(wǎng)絡故障而把微服務誤刪了,那即使網(wǎng)絡恢復了,該微服務也不會重新注冊到eureka server了,因為只有在微服務啟動的時候才會發(fā)起注冊請求,后面只會發(fā)送心跳和服務列表請求,這樣的話,該實例雖然是運行著,但永遠不會被其它服務所感知。所以,eureka server在短時間內(nèi)丟失過多的客戶端心跳時,會進入自我保護模式,該模式下,eureka會保護注冊表中的信息,不在注銷任何微服務,當網(wǎng)絡故障恢復后,eureka會自動退出保護模式。自我保護模式可以讓集群更加健壯。

但是我們在開發(fā)測試階段,需要頻繁地重啟發(fā)布,如果觸發(fā)了保護機制,則舊的服務實例沒有被刪除,這時請求有可能跑到舊的實例中,而該實例已經(jīng)關(guān)閉了,這就導致請求錯誤,影響開發(fā)測試。所以,在開發(fā)測試階段,我們可以把自我保護模式關(guān)閉,只需在eureka server配置文件中加上如下配置即可:

#eureka基本配置信息
eureka:
  client:
    service-url:
      #Eureka 客戶端與 Eureka 服務端進行交互的地址
      defaultZone: http://127.0.0.1:${server.port}/eureka
    #是否將自己注冊到Eureka服務中,本身就是注冊中心所以無需注冊
    register-with-eureka: false
    #是否從Eureka中檢索注冊信息,本身就是注冊中心所以無需檢索
    fetch-registry: false
  server:
    # 測試時關(guān)閉自我保護機制,保證不可用服務及時踢出
    enable-self-preservation: false
    ##剔除失效服務間隔
    eviction-interval-timer-in-ms: 2000

在eureka client配置文件中加上:

#eureka基本配置信息
eureka:
  client:
    service-url:
      #Eureka 客戶端與 Eureka 服務端進行交互的地址
      defaultZone: http://127.0.0.1:8100/eureka/
  # 心跳檢測檢測與續(xù)約時間
  # 測試時將值設(shè)置設(shè)置小些,保證服務關(guān)閉后注冊中心能及時踢出服務
  instance:
    # Eureka客戶端向服務端發(fā)送心跳的時間間隔,單位為秒(客戶端告訴服務端自己會按照該規(guī)則)
    lease-renewal-interval-in-seconds: 1
    # Eureka服務端在收到最后一次心跳之后等待的時間上限,單位為秒,超過則剔除(客戶端告訴服務端按照此規(guī)則等待自己)
    lease-expiration-duration-in-seconds: 2

但在生產(chǎn)環(huán)境,不會頻繁重啟,所以,一定要把自我保護機制打開,否則網(wǎng)絡一旦中斷,就無法恢復。
當然關(guān)于自我保護還有很多個性化配置,這里不詳細說明。

注意考慮網(wǎng)絡不可達情況下:調(diào)用接口冪等、重試、補償?shù)取?/p>

創(chuàng)建工程

1. 目前工程結(jié)構(gòu)
image
2. 首先創(chuàng)建父工程springcloud,以后這個工程下存放子工程eureka,feign,zuul等組件

將該工程的src文件夾刪除,在pom文件中添加依賴

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath ></relativePath>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <!--引用倉庫-->
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <!--管理依賴,子項目中的依賴不用列出版本號-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.M9</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

創(chuàng)建eureka父工程

新建springcloud-eureka項目,在pom文件中添加依賴

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

創(chuàng)建eureka服務端

以springcloud-eureka為父工程,新建springboot-eureka-server項目,這里不需要添加eureka-server依賴,因為父工程中有了。

1. 添加配置

在resources目錄中添加application.yml文件,在文件中添加配置

#服務端口號
server:
  port: 8100

spring:
  application:
    name: eureka-server
    
#eureka基本配置信息
eureka:
  client:
    service-url:
      #Eureka 客戶端與 Eureka 服務端進行交互的地址
      defaultZone: http://127.0.0.1:${server.port}/eureka
    #是否將自己注冊到Eureka服務中,本身就是注冊中心所以無需注冊
    register-with-eureka: false
    #是否從Eureka中檢索注冊信息,本身就是注冊中心所以無需檢索
    fetch-registry: false
  server:
    # 測試時關(guān)閉自我保護機制,保證不可用服務及時踢出
    enable-self-preservation: false
    ##剔除失效服務間隔
    eviction-interval-timer-in-ms: 2000

2. 啟動類

java目錄下創(chuàng)建com.lxg二級目錄,然后創(chuàng)建EurekaServerApp啟動類

@SpringBootApplication
@EnableEurekaServer
public class EurekaServer {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer.class, args);
    }
}

在類上加EnableEurekaServer注解,啟動EurekaServer。

創(chuàng)建eureka客戶端

以springcloud-eureka為父工程,新建springboot-eureka-client項目。在pom文件中添加依賴:

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
1. 添加配置

在resources目錄中添加application.yml文件,在文件中添加配置

#端口號
server:
  port: 9100

spring:
  application:
    name: eureka-client

#eureka基本配置信息
eureka:
  client:
    service-url:
      #Eureka 客戶端與 Eureka 服務端進行交互的地址
      defaultZone: http://127.0.0.1:8100/eureka/
  # 心跳檢測檢測與續(xù)約時間
  # 測試時將值設(shè)置設(shè)置小些,保證服務關(guān)閉后注冊中心能及時踢出服務
  instance:
    # Eureka客戶端向服務端發(fā)送心跳的時間間隔,單位為秒(客戶端告訴服務端自己會按照該規(guī)則)
    lease-renewal-interval-in-seconds: 1
    # Eureka服務端在收到最后一次心跳之后等待的時間上限,單位為秒,超過則剔除(客戶端告訴服務端按照此規(guī)則等待自己)
    lease-expiration-duration-in-seconds: 2
2. 啟動類

java目錄下創(chuàng)建com.lxg二級目錄,然后創(chuàng)建EurekaClientApp啟動類,這里啟動類名稱不能為EurekaClient,否則會起沖突導致啟動失敗。類上使用EnableEurekaClient注解。

@SpringBootApplication
@EnableEurekaClient
public class EurekaClientApp {
    public static void main(String[] args) {
        SpringApplication.run(EurekaClientApp.class, args);
    }
}

測試

先啟動服務端,再啟動客戶端,訪問127.0.0.1:8100 ,訪問監(jiān)控頁面。


image

圖中的紅色字體是在提示已經(jīng)關(guān)閉了eureka的自我保護機制,此時如果關(guān)閉客戶端,頁面中的服務就會被踢出,如果沒有關(guān)閉的話,即使客戶端關(guān)閉了,服務依然會存在。

高可用注冊中心(Eureka集群)

在微服務中,注冊中心非常核心,可以實現(xiàn)服務治理,如果一旦注冊出現(xiàn)故障的時候,可能會導致整個微服務無法訪問,在這時候就需要對注冊中心實現(xiàn)高可用集群模式。

Eureka高可用原理

默認情況下Eureka是讓服務注冊中心,不注冊自己,但是在集群中,需要設(shè)置能注冊自己,因為這兩個屬性默認為true,只需要不寫就行了。

###使該注冊中心注冊自己
    register-with-eureka: true
###需要去注冊中心上檢索服務
    fetch-registry: true

Eureka高可用實際上將自己作為服務向其他服務注冊中心注冊自己,這樣就可以形成一組相互注冊的服務注冊中心,從而實現(xiàn)服務清單的互相同步,達到高可用效果。

Eureka集群環(huán)境搭建

新增server1,server2,server3三個節(jié)點。


image

server1配置:

#服務端口號
server:
  port: 8100

spring:
  application:
    name: eureka-server

#eureka基本配置信息
eureka:
  client:
    service-url:
      #Eureka 客戶端與 Eureka 服務端進行交互的地址
      defaultZone: http://127.0.0.1:8200/eureka,http://127.0.0.1:8300/eureka
  server:
    # 測試時關(guān)閉自我保護機制,保證不可用服務及時踢出
    enable-self-preservation: false
    ##剔除失效服務間隔
    eviction-interval-timer-in-ms: 2000

server2配置:

#服務端口號
server:
  port: 8200

spring:
  application:
    name: eureka-server

#eureka基本配置信息
eureka:
  client:
    service-url:
      #Eureka 客戶端與 Eureka 服務端進行交互的地址
      defaultZone: http://127.0.0.1:8100/eureka,http://127.0.0.1:8300/eureka
  server:
    # 測試時關(guān)閉自我保護機制,保證不可用服務及時踢出
    enable-self-preservation: false
    ##剔除失效服務間隔
    eviction-interval-timer-in-ms: 2000

server3配置:

#服務端口號
server:
  port: 8300

spring:
  application:
    name: eureka-server

#eureka基本配置信息
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8100/eureka,http://127.0.0.1:8200/eureka
  server:
    # 測試時關(guān)閉自我保護機制,保證不可用服務及時踢出
    enable-self-preservation: false
    ##剔除失效服務間隔
    eviction-interval-timer-in-ms: 2000

然后修改客戶端的配置,因為以前是單個eureka注冊中心,只需要注冊進一個地址就行了,現(xiàn)在要注冊進所有的注冊中心。

#端口號
server:
  port: 9100

spring:
  application:
    name: eureka-client

#eureka基本配置信息
eureka:
  client:
    service-url:
      #Eureka 客戶端與 Eureka 服務端進行交互的地址
      #單機
      #defaultZone: http://127.0.0.1:8100/eureka/
      #集群
      defaultZone: http://127.0.0.1:8100/eureka/,http://127.0.0.1:8200/eureka/,http://127.0.0.1:8300/eureka/
  # 心跳檢測檢測與續(xù)約時間
  # 測試時將值設(shè)置設(shè)置小些,保證服務關(guān)閉后注冊中心能及時踢出服務
  instance:
    # Eureka客戶端向服務端發(fā)送心跳的時間間隔,單位為秒(客戶端告訴服務端自己會按照該規(guī)則)
    lease-renewal-interval-in-seconds: 1
    # Eureka服務端在收到最后一次心跳之后等待的時間上限,單位為秒,超過則剔除(客戶端告訴服務端按照此規(guī)則等待自己)
    lease-expiration-duration-in-seconds: 2

先啟動所有的服務端節(jié)點,再啟動客戶端。訪問localhost:8100 ,localhost:8200 ,localhost:8300 都能進入eureka的界面,同時看到客戶端服務和其他的注冊中心。這個時候即使有某一個節(jié)點掛了,服務依然是可用的,而且性能肯定比單機版要好。

搭建eureka集群有幾點需要注意的:

1.與先前獨立運行注冊不同,注意defaultZone屬性,它的值為除了自己以外的所有eureka節(jié)點的地址,以英文逗號分割。
2.去掉fetch-registry 與 register-with-eureka配置(其實這樣做就會取對應的默認值,兩個值均為true),需要讓自己能被注冊和檢索。
3.啟動第一個注冊中心時會報Cannot execute request on any known server的錯誤,暫時不管它,實際上eureka注冊中心的ui界面是能打開的,當所有的節(jié)點啟動完畢,就能找到服務,此錯誤就會消失。
4.所有注冊中心的節(jié)點的spring.application.name必須保持一致。
5.當客戶端需要往注冊中心集群注冊服務時defaultZone屬性需要把所有節(jié)點地址都加上,如果像單節(jié)點一樣的話,你連接的那個節(jié)點掛了,集群中其他節(jié)點就無法獲取到該服務,也就不能達到高可用。

本文GitHub源碼:https://github.com/lixianguo5097/springcloud/tree/master/springcloud-eureka

CSDN:https://blog.csdn.net/qq_27682773
簡書:http://www.itdecent.cn/u/e99381e6886e
博客園:https://www.cnblogs.com/lixianguo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容