1. 程式人生 > >Spring Cloud Eureka叢集

Spring Cloud Eureka叢集

                        Spring Cloud基礎教程[Eureka叢集]

Netflix Eureka介紹

Spirng Cloud Eureka使用Netflix Eureka來實現服務註冊與發現。它既包含了服務端元件,也包含了客戶端元件,並且服務端與客戶端均採用java編寫,所以Eureka主要適用於通過java實現的分散式系統,或是JVM相容語言構建的系統。Eureka的服務端提供了較為完善的REST API,所以Eureka也支援將非java語言實現的服務納入到Eureka服務治理體系中來,只需要其他語言平臺自己實現Eureka的客戶端程式。目前.Net平臺的Steeltoe、Node.js的eureka-js-client等都已經實現了各自平臺的Ereka客戶端元件。

服務註冊中心

在服務治理框架中,通常都會構建一個註冊中心,每個服務單元向註冊中心登記自己提供的服務,包括服務的主機與埠號、服務版本號、通訊協議等一些附加資訊。註冊中心按照服務名分類組織服務清單,同時還需要以心跳檢測的方式去監測清單中的服務是否可用,若不可用需要從服務清單中剔除,以達到排除故障服務的效果。
 

Eureka服務端

Eureka服務端,即服務註冊中心。它同其他服務註冊中心一樣,支援高可用配置。依託於強一致性提供良好的服務例項可用性,可以應對多種不同的故障場景。

   Eureka服務端支援叢集模式部署,當叢集中有分片發生故障的時候,Eureka會自動轉入自我保護模式。它允許在分片發生故障的時候繼續提供服務的發現和註冊,當故障分配恢復時,叢集中的其他分片會把他們的狀態再次同步回來。叢集中的的不同服務註冊中心通過非同步模式互相複製各自的狀態,這也意味著在給定的時間點每個例項關於所有服務的狀態可能存在不一致的現象。
 

Eureka客戶端

Eureka客戶端,主要處理服務的註冊和發現。客戶端服務通過註冊和引數配置的方式,嵌入在客戶端應用程式的程式碼中。在應用程式啟動時,Eureka客戶端向服務註冊中心註冊自身提供的服務,並週期性的傳送心跳來更新它的服務租約。同時,他也能從服務端查詢當前註冊的服務資訊並把它們快取到本地並週期行的重新整理服務狀態。

我們通過一張圖來理解叢集:


下面就來完成Eureka的高可用實現與搭建

Spring Cloud Eureka【Dalston版】

github地址:[email protected]:13849141963/spring-cloud.git

搭建Eureka高可用叢集流程:[三個節點]

建立一個基礎的Spring Boot工程,命名為springcloud-eureka,並在pom.xml中引入需要的依賴內容:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.13.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <!--Eureka服務註冊中心-->
       <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>


        <!--生產環境下給Eureka註冊中心新增安全的使用者認證-->
        <!--<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>-->

        <!-- 熱部署 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

  <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.RC1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

 

第一個節點application-peer1.properties配置檔案中如下資訊:

spring.application.name=eureka-server
server.port=1111
eureka.instance.hostname=peer1
#指向你的從節點的Eureka
#兩個副本節點
eureka.client.service-url.defaultZone=http://peer2:2222/eureka/,http://peer3:3333/eureka/
#一個副本節點
#eureka.client.service-url.defaultZone=http://peer2:2222/eureka/
# 由於該應用為註冊中心,所以設定為false,代表不向註冊中心註冊自己 ,http://peer3:3333/eureka/
eureka.client.register-with-eureka=true
# 由於註冊中心的職責就是維護服務例項,他並不需要去檢索服務,所以也設定為false
eureka.client.fetch-registry=true
# 關閉自我保護
#eureka.server.enableSelfPreservation=false
#spring.security.basic.enabled=true
#spring.security.user.name=root
#spring.security.user.password=123456

第二個節點application-peer2.properties配置檔案中如下資訊:

spring.application.name=eureka-server
server.port=2222
eureka.instance.hostname=peer2
#指向你的從節點的Eureka
#兩個副本節點
eureka.client.service-url.defaultZone=http://peer1:1111/eureka/,http://peer3:3333/eureka/
#一個副本節點
#eureka.client.service-url.defaultZone=http://peer1:1111/eureka/
# 由於該應用為註冊中心,所以設定為false,代表不向註冊中心註冊自己 ,http://peer3:3333/eureka/
eureka.client.register-with-eureka=true
# 由於註冊中心的職責就是維護服務例項,他並不需要去檢索服務,所以也設定為false
eureka.client.fetch-registry=true
# 關閉自我保護
#eureka.server.enableSelfPreservation=false
#spring.security.basic.enabled=true
#spring.security.user.name=root
#spring.security.user.password=123456

第三個節點application-peer3.properties配置檔案中如下資訊:

spring.application.name=eureka-server
server.port=3333
eureka.instance.hostname=peer3
#指向你的從節點的Eureka
eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/,http://peer2:2222/eureka/
# 由於該應用為註冊中心,所以設定為false,代表不向註冊中心註冊自己
eureka.client.register-with-eureka=true
# 由於註冊中心的職責就是維護服務例項,他並不需要去檢索服務,所以也設定為false
eureka.client.fetch-registry=true
# 關閉自我保護
#eureka.server.enableSelfPreservation=false
#spring.security.basic.enabled=true
#spring.security.user.name=root
#spring.security.user.password=123456

在C:\Windows\System32\drivers\etc下的hosts檔案新增對peer,peer2和peer3的轉換,讓上面的配置的host形式的service-url能夠在本地正確的訪問到:

10.0.45.103 peer1 

10.0.45.103 peer2

10.0.45.103 peer3

通過@EnableEurekaServer註解啟動一個Eureka服務註冊中心提供給其他應用進行對話。這一步非常的簡單,只需要在一個普通的Spring Boot應用中新增這個註解就能開啟此功能.

package com.zy.cn;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer//該註解表示使用Eureka服務註冊中心
@SpringBootApplication
public class SpringcloudEurekaApplication {

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

本地IDEA模擬啟動三臺方式:[三個配置檔案]

選擇Edit Configurations選項:

如下操作:

分別啟動peer1,peer2,peer3三臺伺服器:

當啟動第一臺的eureka伺服器的時候控制檯會丟擲如下異常資訊:

為什麼會出現錯誤呢?

是因為在此係統中,我們要搭建的是叢集環境, 每一臺伺服器在自己啟動之後,都要去連線叢集中的其他伺服器,以便於相互之間通訊傳遞資訊。

但是,我們肯定是按照次序啟動伺服器,我們不管先啟動哪一臺伺服器,其他的伺服器都還沒有準備就緒,所以肯定會出現找不到要連線的伺服器,所以會報錯。

這個錯誤根本不需要解決, 把所有的伺服器全部啟動,整個叢集就可以正常執行(因為出現的是連線錯誤,現在所有的伺服器已經準備就緒,所以不會再一次出現連線錯誤,除非某臺伺服器down掉。)

瀏覽器位址列訪問:http://10.0.45.103:1111/,http://10.0.45.103:2222/,http://10.0.45.103:3333/出現如下頁面即搭建成功。

建立服務的消費者:springcloud-provider[專案在github上],在服務的提供方配置檔案中新增如下配置:

server:
  port: 8081
  servlet:
    context-path: /springcloud-provider
  tomcat:
    uri-encoding: UTF-8
#mybatis相關配置資訊
mybatis:
  mapper-locations: com/zy/cn/mapper/*.xml
  type-aliases-package: com.zy.cn.entity


spring:
  application:
    #應用名稱
    name: springcloud-provider
    #mysql相關配置資訊
  datasource:
    url: jdbc:mysql://10.0.45.103/mysql-test?characterEncoding=utf8&serverTimezone=GMT%2B8
    type: com.alibaba.druid.pool.DruidDataSource
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
    #http編碼格式
  http:
    encoding:
      enabled: true
      charset: UTF-8
      force: true
  messages:
    encoding: UTF-8

#熱部署
devtools:
  restart:
    exclude: static/**,templates/**
    enabled: true
  jpa:
    database: MYSQL
    show-sql: true
#在Eureka註冊中心進行註冊服務
eureka:
  client:
    serviceUrl:
      #註冊地址為叢集.
      defaultZone: http://peer1:1111/eureka/,http://peer3:3333/eureka/,http://peer2:2222/eureka/

此時重新整理註冊中心:出現服務提供者

測試叢集的高可用性:

可以看到服務已經在三個eureka註冊中心都註冊上了,當我們專案已經再生產環境上,會出現單節點宕機,下面我們來模擬下單節點宕機其他兩個節點會出現什麼問題?停掉第三臺伺服器,此時發現第一臺伺服器和第三臺伺服器都會丟擲該異常資訊.多客戶端時,會進入自我保護模式,即一個服務長時間沒有傳送心跳,eureka也不會將其刪除。這就是失效剔除.

此時重新整理頁面會發現第三個節點變成unavailable-replicas不可用的節點,服務的提供者資訊顯示在應用上,其他兩個節點依然可以對外提供服務,說明eureka叢集的高可用性.

失效剔除

有些時候,我們的服務例項並不一定會正常下線,可能由於記憶體溢位、網路故障等原因使服務不能正常運作。而服務註冊中心並未收到“服務下線”的請求,為了從服務列表中將這些無法提供服務的例項剔除,Eureka Server在啟動的時候會建立一個定時任務,預設每隔一段時間(預設為60秒)將當前清單中超時(預設為90秒)沒有續約的服務剔除出去。

 

自我保護

服務註冊到Eureka Server後,會維護一個心跳連線,告訴Eureka Server自己還活著。Eureka Server在執行期間會統計心跳失敗的比例在15分鐘以之內是否低於85%,如果出現低於的情況,Eureka Server會將當前例項註冊資訊保護起來,讓這些例項不會過期。這樣做會使客戶端很容易拿到實際已經不存在的服務例項,會出現呼叫失敗的情況。因此客戶端要有容錯機制,比如請求重試、斷路器。

以下是自我保護相關的屬性:

eureka.server.enableSelfPreservation=true. 可以設定改引數值為false,以確保註冊中心將不可用的例項刪除

 

服務續約

在註冊服務之後,服務提供者會維護一個心跳用來持續高速Eureka Server,“我還在持續提供服務”,否則Eureka Server的剔除任務會將該服務例項從服務列表中排除出去。我們稱之為服務續約。

下面是服務續約的兩個重要屬性:

eureka.instance.lease-expiration-duration-in-seconds

leaseExpirationDurationInSeconds,表示eureka server至上一次收到client的心跳之後,等待下一次心跳的超時時間,在這個時間內若沒收到下一次心跳,則將移除該instance。

預設為90秒

如果該值太大,則很可能將流量轉發過去的時候,該instance已經不存活了。
如果該值設定太小了,則instance則很可能因為臨時的網路抖動而被摘除掉。
該值至少應該大於leaseRenewalIntervalInSeconds

eureka.instance.lease-renewal-interval-in-seconds

leaseRenewalIntervalInSeconds,表示eureka client傳送心跳給server端的頻率。如果在leaseExpirationDurationInSeconds後,server端沒有收到client的心跳,則將摘除該instance。除此之外,如果該instance實現了HealthCheckCallback,並決定讓自己unavailable的話,則該instance也不會接收到流量。

預設30秒

通訊協議

預設情況下,Eureka使用Jersey和XStream配合JSON作為Server與Client之間的通訊協議。也可以選擇實現自己的協議來代替。