白話SpringCloud | 第三章:服務註冊與發現-高可用配置(Eureka)-下

前言

上一章節,講解了在單機模式下的服務註冊與發現的相關知識點及簡單示例。而在實際生產或者在這種微服務架構的分佈式環境中,須要考慮發生故障時,各組件的高可用。而其實高可用,個人簡單粗俗理解就是,經過系統的冗餘進行高可用,或者是進行集羣部署,保證一臺服務不可用時,會進行自動轉移至可用的服務中。今天的章節,就來講說關於Eureka的高可用吧。html

一點知識

講解前,咱們先來聊聊在使用Dubbo時耳聞能詳的ZookeeperEureka之間的區別吧。java

CAP原則

根據百度百科的定義,CAP定理又稱CAP原則,指的是在一個分佈式系統中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分區容錯性),最多隻能同時三個特性中的兩個,三者不可兼得。git

在分佈式領域,你們應該對CAP理論不陌生了吧(瞭解但也是沒有深刻了解過⊙﹏⊙‖∣)。github

如下摘至百度百科:web

● 一致性(C):在分佈式系統中的全部數據備份,在同一時刻是否一樣的值。(等同於全部節點訪問同一份最新的數據副本)spring

● 可用性(A):在集羣中一部分節點故障後,集羣總體是否還能響應客戶端的讀寫請求。(對數據更新具有高可用性)api

● 分區容錯性(P):以實際效果而言,分區至關於對通訊的時限要求。系統若是不能在時限內達成數據一致性,就意味着發生了分區的狀況,必須就當前操做在C和A之間作出選擇。瀏覽器

至於爲什麼不能同時知足,以上在分區容錯性也有簡單的說明了,通常來講,分區容錯沒法避免,所以能夠認爲CAPP老是成立,而對於一致性可用性爲什麼不能同時知足,簡單來講就是:存在可能通訊失敗狀況,即:出現分區容錯,至於更詳細具體的,你們能夠看下大佬阮一峯的這篇文章:CAP 定理的含義。這裏就不闡述了,不是很瞭解~緩存

Eureka與Zookeeper區別

對於Eureka而言,其是知足AP的,而Zookeeper而言,是知足CP的。安全

Eureka是知足AP的:

  • 優先保證可用性
  • 各個節點都是平等的,幾個節點掛掉不會影響正常節點的工做,剩餘的節點依然能夠提供註冊和查詢服務
  • 在向某個Eureka註冊時若是發現鏈接失敗,則會自動切換至其它節點,只要有一臺Eureka還在,就能保證註冊服務可用(保證可用性),只不過查到的信息可能不是最新的(不保證強一致性)

Zookeeper是知足AP的:

  • 任什麼時候刻對ZooKeeper的訪問請求能獲得一致的數據結果,同時系統對網絡分割具有容錯性
  • 不能保證每次服務請求的可用性
  • 當master節點由於網絡故障與其餘節點失去聯繫時,剩餘節點會從新進行leader選舉
  • 選舉leader的時間太長,30 ~ 120s, 且選舉期間整個zk集羣都是不可用的,這就致使在選舉期間註冊服務癱瘓

個人簡單理解:因爲Zookeeperloader選舉策略,使其能夠保證數據的一致性。而Eureka,本事沒有選舉策略,各服務是獨立運行的,至少在某一時刻,各服務之間的數據是不一致的,而在可用性方面,Zookeeper也是因爲選舉策略緣由,在選舉期間是,整個zookeeper是不可用的,會形成短暫(看選舉時長)的服務不可用。而對於Eureka而言,服務是獨立運行的,因此不會由於某臺服務不可用致使了其餘服務不可用狀況。感受上面有點繞,簡單來講,就是Zookeeper經過選舉策略保證數據的一致性,但缺失了可用性,Eureka因爲服務獨立運行,經過心跳等通訊策略進行數據同步,存在數據不一致性,但保證了服務的可用性

因此,綜上所述,做爲服務註冊中心而言,可用性原則是比數據一致性更重要的,同時上一章節也有說過,因爲Eureka自我保護模式,可保護服務註冊表中的信息不被剔除,因此Eureka能夠很好的應對因網絡故障致使節點失去聯繫的狀況

Eurkea高可用介紹及示例

Eureka的高可用

官網中,關於Eureka的高可用部分是這麼描述的:

High Availability, Zones and Regions

Peer Awareness

因此能夠獲悉,Eureka Server能夠運行多個實例來構建集羣,解決單點問題,Eureka Server採用的是Peer to Peer對等通訊。這是一種去中心化的架構,無master/slave區分,每個Peer都是對等的。在這種架構中,節點經過彼此互相註冊來提升可用性,每一個節點須要添加一個或多個有效的serviceUrl指向其餘節點。每一個節點均可被視爲其餘節點的副本。

若是某臺Eureka Server宕機,Eureka Client的請求會自動切換到新的Eureka Server節點,當宕機的服務器從新恢復後,Eureka會再次將其歸入到服務器集羣管理之中。當節點開始接受客戶端請求時,全部的操做都會進行replicateToPeer(節點間複製)操做,將請求複製到其餘Eureka Server當前所知的全部節點中。

因此,簡單來講,Eureka Server的高可用,實際上就是將本身也做爲服務向其餘服務註冊中心進行註冊,這樣就能夠造成一組相互註冊的服務註冊中心,以實現服務清單的互相同步,達到高可用的效果。

另外,從官網文檔中有提到ZonesRegionsRegionZone(或者Availability Zone)均是AWS的概念。在非AWS環境下,咱們能夠先簡單地將region理解爲Eureka集羣,zone理解成機房。下圖就能夠理解爲一個Eureka集羣被部署在了zone1機房和zone2機房中。

Eureka集羣

對這些概念的其餘相關知識,也深刻了解,你們感興趣,可自行搜索下吧。

示例前,先看看集羣模式下,Eureka的架構圖。

集羣模式

  1. Service Provider會向Eureka Server作Register(服務註冊)、Renew(服務續約)、Cancel(服務下線)等操做。
  2. Eureka Server之間會作註冊服務的同步,從而保證狀態一致
  3. Service Consumer會向Eureka Server獲取註冊服務列表,並消費服務

具體的原理分析,能夠看看這篇比較早的文章:https://nobodyiam.com/2016/06/25/dive-into-eureka/,雖然是比較早的文章,但寫的比較詳細,能夠看看。

接下來,以官網文檔demo,示例下。

Eureka服務端高可用

經過點對點配置,註冊中心經過相互註冊來實現高可用配置。如下構建一個雙節點的集羣模式。

修改spring-cloud-eureka-server項目。

0.建立一個application-ha.properties配置文件,同時修改application.properties文件。

application-ha.properties

spring.application.name=eureka-service-ha
# 修改端口
server.port=1001

# 實例的主機名稱
eureka.instance.hostname=myPeer2

## 不要向註冊中心註冊本身
#eureka.client.register-with-eureka=false
## 表示不去檢索其餘的服務,由於服務註冊中心自己的職責就是維護服務實例,它也不須要去檢索其餘服務
#eureka.client.fetch-registry=false

# 指定服務註冊中心地址
eureka.client.service-url.defaultZone=http://myPeer1:1000/eureka

application.properties

spring.application.name=eureka-service-ha
# 修改端口
server.port=1000

# 實例的主機名稱
eureka.instance.hostname=myPeer1

## 不要向註冊中心註冊本身
#eureka.client.register-with-eureka=false
## 表示不去檢索其餘的服務,由於服務註冊中心自己的職責就是維護服務實例,它也不須要去檢索其餘服務
#eureka.client.fetch-registry=false

# 指定服務註冊中心地址
eureka.client.service-url.defaultZone=http://myPeer2:1000/eureka

#spring.profiles.active=ha

1.因爲是在同一臺進行模擬,首先修改hosts文件,當瀏覽器請求一個地址時,首先會今後文件選擇對應對應的IP地址,找不到時才請求CDS域名解析服務器進行解析

‪C:\Windows\System32\drivers\etc\hosts文件:

127.0.0.1 myPeer1
127.0.0.1 myPeer2

友情提示:若提示無修改權限,可根據如下網址進行相應修改:編輯hosts文件沒法保存怎麼辦

2.啓動應用,咱們這裏直接啓動兩個,可經過修改spring.profiles.active值來啓動不一樣環境的應用,或者使用-jar xx.jar --spring.profiles.active=xx來啓動。

訪問:http://myPeer1:1000

myPeer1

訪問:http://myPeer2:1001

myPeer2

題外話:第一個啓動的應用,後臺會報錯Connection refused: connect,等第二個應用啓動後,就正常了~

Eureka客戶端註冊至集羣上

客戶端只須要經過修改配置文件的eureka.client.service-url.defaultZone值便可。

修改spring-cloud-eureka-client項目

0.修改配置文件application.properties

spring.application.name=eureka-client
server.port=2000

# 註冊中心地址
eureka.client.service-url.defaultZone=http://myPeer1:1000/eureka,http://myPeer2:1001/eureka
# 啓用ip配置 這樣在註冊中心列表中看見的是以ip+端口呈現的
eureka.instance.prefer-ip-address=true
# 實例名稱  最後呈現地址:ip:2000
eureka.instance.instance-id=${spring.cloud.client.ip-address}:${server.port}

固然,也可只註冊到某個節點上,其餘的節點也會有此服務列表的,通常建議以集羣方式進行配置,即多註冊中心配置。避免單點故障,Eureka在搜索註冊中心時,根據defaultZone列表,找到一個可用的,以後就不會繼續去下一個註冊中心地址拉取服務列表了,此時若其中一個註冊中心掛了,這個時候客戶端會繼續去第二個註冊中心拉取服務列表的。

啓動後,能夠看見eureka-client註冊上去了。

如今咱們中止一個應用,能夠看出不可用的服務列表中已經有相關信息了

高可用測試

爲了驗證高可用性是否成功,建立一個spring-cloud-eureka-server-ha-test項目,做爲服務消費者使用RestTemplate+ribbon進行調用spring-cloud-eureka-client的服務。

建立spring-cloud-eureka-server-ha-test項目 0.引入pom依賴

<!-- 客戶端依賴 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>    
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    </dependency>    
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>    
    <!-- 引入web,提供一個簡單的api接口 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

1.配置文件,配置註冊中心地址

spring.application.name=eureka-ha-test
server.port=8888

#指定註冊中心地址
eureka.client.serviceUrl.defaultZone=http://myPeer1:1000/eureka/,http://myPeer2:1001/eureka/
# 啓用ip配置 這樣在註冊中心列表中看見的是以ip+端口呈現的
eureka.instance.prefer-ip-address=true
# 實例名稱  最後呈現地址:ip:2000
eureka.instance.instance-id=${spring.cloud.client.ip-address}:${server.port}

2.啓動類,配置RestTemplateBean類,同時加入@LoadBalanced註解實現服務調用。

@SpringCloudApplication
@EnableDiscoveryClient
@Slf4j
public class EurekaServiceHaApplication {
    
    public static void main(String[] args) throws Exception {
        SpringApplication.run(EurekaServiceHaApplication.class, args);
        log.info("spring-cloud-eureka-server-ha-test啓動!");
    }
    
    //加入負載均衡能力
    //同時可根據applicationName 來訪問服務
    //如http://EUREKA-CLIENT/add
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

3.編寫一個控制類,簡單調用EUREKA-CLIENT服務方法。

/**
 * 調用簡單示例
 * @author oKong
 *
 */
@RestController
public class DemoController {

    @Autowired
    private RestTemplate testTemplate;
    
    @GetMapping("/")
    public String index() {
        //訪問服務提供者
        return testTemplate.getForObject("http://EUREKA-CLIENT/", String.class);
    }
}

4.啓動應用類,訪問註冊中心和http://127.0.0.1:8888

註冊中心

遠程調用

可看見輸出spring-cloud-eureka-client!代表調用成功。在控制檯也能夠看見,從註冊中心拉取了EUREKA-CLIENT的服務地址:

這個時候,能夠試着中止其中一個甚至所有的Eureka Server,再繼續訪問,能夠看見服務仍是能夠調用的,但要知道此時調用方是多是根據本地的緩存列表中直接獲取地址的,而不是從註冊服務中心,等待下次心跳機制時間到時,纔會去進行拉取最新的服務列表的。


關於RestTemplateRibbon相關知識點,會在下一章節進行闡述的,這裏就不敞開了。


一點疑問

第一次看官網文檔進行demo時,還在想經過設置端口號不就能夠了嗎,爲什麼還須要指定eureka.instance.hostname呢,多麻煩?嘗試下使用ip或者不配置試試看。

  • 修改eureka.instance.hostnamelocalhost或者127.0.0.1時,

localhost

能夠看見,在DS Replicas中是空的,說明沒有可複製的服務,registered-replicasavailable-replicas都是空的。

客戶端配置eureka.client.service-url.defaultZone

eureka.client.service-url.defaultZone=http://127.0.0.1:1001/eureka,http://127.0.0.1:1000/eureka

會發現,在1001服務上有註冊上去,1000服務沒有服務信息。(客戶端註冊是按順序進行優先註冊和獲取服務列表的)

1001服務:

1001服務

1000服務:

1000服務

這說明集羣模式是沒有生效的,註冊中心之間沒有相互複製服務列表。

  • 修改配置文件,一個修改爲127.0.0.1,另外一個修改爲localhost或者實際內網ip地址

說明下: 1001對應hostname爲:127.0.0.1 1000對應hostname爲:192.168.81.1

1001服務:

1001服務

1000服務:

1000服務服務

會發現,集羣模式成功了。 接着咱們啓動客戶端。

1000服務: 1000服務

1001服務: 1001服務

能夠看見,和上面設置myPeer1myPeer2效果是同樣的,都有被複制了。

集羣配置的一點淺談

從上面能夠大體獲悉,Eureka互相註冊要求各個Eureka server實例的eureka.instance.hostname不一樣,若是相同,則會被Eureka標記爲unavailable-replicas(像本地設置爲127.0.0.1,乾脆就不顯示了,具體不明。。⊙﹏⊙‖∣),以前的同步就失效了。而對於客戶端的defaultZone配置而言,是優先從第一個開始註冊和拉取服務的,成功聯通後就不會再繼續找下一個註冊服務了。


綜上所述:

  • 如果一臺服務器,部署多個Eureka server服務時,設置每一個服務 的hostname不一致,同時要設置eureka.instance.prefer-ip-addressfalse,不使用IP地址進行註冊。
  • 如果多臺服務器部署時,設置hostname爲本機的ip地址,可以使用spring.cloud.client.ip-address變量進行賦值。同時設置eureka.instance.prefer-ip-addresstrue。這樣的話,客戶端使用ip進行鏈接就方便了,否則還要去配置host有點坑了。
  • 對於客戶端的defaultZone建議仍是配置多個註冊中心地址。就算自己集羣模式無效,好歹其中一個不可用了,還能連其餘的註冊中心呀。

在最後收尾時,搜索到一篇文章,也是大體的意思,你們能夠點擊看一看:構建高可用Eureka註冊中心

其源碼地址:https://github.com/wangfei0904306/eureka-HA

以上可能理解有誤差,還但願知道的同窗能不吝賜教下,謝謝了!


Eureka註冊中心訪問認證

默認狀況下,訪問註冊中心頁面是匿名訪問的,不須要一些認證。在生產中,爲了安全性,可加入身份認證功能。

添加認證很簡單,官網文檔也有說明:

Securing The Eureka Server

0.加入pom依賴:

<!-- 開啓認證 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

1.修改配置文件,設置用戶名和密碼及集羣狀態下,註冊至其餘服務中心時,加入用戶名和密碼:

# 設置賬號密碼
# 若不設置 默認賬號是user,密碼隨機,啓動時會打印在控制檯上
spring.security.user.name=oKong
spring.security.user.password=123456

# 加入用戶名和密碼
eureka.client.service-url.defaultZone=http://oKong:123456@127.0.0.1:1001/eureka

友情提示:不設置name和password時,默認用戶是user,密碼是隨機的,啓動時會打印在控制檯上的:

默認密碼

2.修改啓動類,根據官網的提示,關閉/eureka/**的CSRF的令牌。

/**
 * Eureka服務端
 * @author oKong
 *
 */
@SpringBootApplication
@EnableEurekaServer
@Slf4j
public class EureakServiceApplication {
    
    public static void main(String[] args) throws Exception {
        SpringApplication.run(EureakServiceApplication.class, args);
        log.info("spring-cloud-eureka-service啓動!");
    }
    
    /**
     * 忽略此路徑下的CSRF令牌
     * @author oKong
     *
     */
    @EnableWebSecurity
    static class WebSecurityConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().ignoringAntMatchers("/eureka/**");
            super.configure(http);
        }
    }    
}

3.啓動應用。再次訪問:http:127.0.0.1:1000 時,就須要輸入用戶名和密碼了。

4.客戶端也是相似的,修改defaultZone加入用戶名和密碼便可:

eureka.client.service-url.defaultZone=http://oKong:123456@127.0.0.1:1001/eureka,http://oKong:123456@127.0.0.1:1000/eureka

參考資料

  1. https://cloud.spring.io/spring-cloud-static/Finchley.SR1/single/spring-cloud.html#spring-cloud-eureka-server

  2. https://nobodyiam.com/2016/06/25/dive-into-eureka/

  3. http://tech.lede.com/2017/03/15/rd/server/SpringCloud1/

  4. https://blog.csdn.net/wangfei0904306/article/details/79056083

總結

本章節主要是Eureka服務的高可用進行了簡單的介紹了下。對於集羣模式下的一些最佳實踐仍是有待商討的,還但願你們能說說自個的方案!至於一些其餘的特性,如元數據配置等,這裏就不闡述了,用的很少。同時,本章節利用RestTemplate+ribbon進行了簡單的服務調用,沒有敞開說,下一章節就是開始講解服務消費者相關知識點,應該也會分紅兩章節來描述,由於涉及到了RibbonFeign相關知識點。我一直以爲使用都是很簡單的,只有理解裏面的原理之類的,這樣纔是一通百通吧,加深印象!使用起來也能比較順利。

最後

目前互聯網上大佬都有分享SpringCloud系列教程,內容可能會相似,望多多包涵了。原創不易,碼字不易,還但願你們多多支持。若文中有錯誤之處,還望提出,謝謝。

老生常談

  • 我的QQ:499452441
  • 微信公衆號:lqdevOps

公衆號

我的博客:http://blog.lqdev.cn

源碼示例:https://github.com/xie19900123/spring-cloud-learning

原文地址:http://blog.lqdev.cn/2018/09/09/SpringCloud/chapter-three/

相關文章
相關標籤/搜索