02.Spring Cloud 之 Eureka

1. 概述

1.1 Eureka 是什麼

Eureka 是 Netflix 的一個子模塊,也是核心模塊之一。Eureka 是一個基於 REST 的服務,用於定位服務,以實現雲端中間層服務發現和故障轉移。服務註冊與發現對於微服務架構來說是非常重要的,有了服務與發現與註冊,只需要使用服務的標識符,就可以訪問到服務,而不需要修改服務調用的配置文件了。功能類似於 dubbo 的註冊中心,比如 zookeeper。

1.2 Eureka 組件

1.2.1 Eureka Server

各個節點啓動後,會在 EurekaServer 中進行註冊,這樣 EurekaServer 中的服務註冊表中將會存儲所有可用服務節點的信息,服務節點的信息可以在界面中直觀的看到

1.2.2 Eureka Client

EurekaClient 是一個 Java 客戶端,用於簡化 EurekaServer 的交互,客戶端同時也具備一個內置的、使用輪詢(round-robin)負載算法的負載均衡器。在應用啓動後,將會在 EurekaServer 發送心跳(默認週期爲 30 秒)。如果 EurekaServer 在多個心跳週期內沒有接收到某個節點的心跳,EurekaServer 將會從服務註冊中把這個節點移除(默認 90 秒)

1.3 Eureka 自我保護機制

自我保護機制

1.3.1 自我保護機制

默認情況下,EurekaServer 在 90 秒內沒有檢測到服務列表中的某微服務,則會自動將該微服務從服務列表中刪除。但很多情況下並不是該微服務節點(主機)出了問題,而是由於網絡抖動等原因使該微服務無法被 EurekaServer 發現,即無法檢測到該微服務主機的心跳。若在短暫時間內網絡恢復正常,但由於 EurekaServer 的服務列表中已經沒有該微服務,所以該微服務已經無法提供服務了。

在短時間內若 EurekaServer 丟失較多微服務,即 EurekaServer 收到的心跳數量小於閾值,爲了保證系統的可用性(AP),給那些由於網絡抖動而被認爲宕機的客戶端 “重新復活” 的機會,Eureka 會自動進入自我保護模式:服務列表只可讀取、寫入,不可執行刪除操作。當 EurekaServer 收到的心跳數量恢復到閾值以上時,其會自動退出 Self Preservation 模式。

1.3.2 默認值修改

啓動自我保護的閾值因子默認爲 0.85,即 85%。即 EurekaServer 收到的心跳數量若小於應該收到數量的 85% 時,會啓動自我保護機制,使用 eureka.server.renewal-percent-threshold=0.75 可以更改自我保護機制的閾值

自我保護機制默認是開啓的,可以通過修改 EurekaServer 中配置文件來關閉,但不建議關閉,使用 erueka.server.enable-self-preervation=false 禁用自我保護模式。

1.3.3 自我保護機制閾值

Renews threshold:Eureka Server 期待每分鐘收到 client 端發送的續約總數,爲了計算這個數,系統會統計從當前時刻開始向前的這 15 分鐘內接收到的總續約數(一個瞬時值),假設其爲 count,然後再根據閾值因子計算出其閾值續約數爲 num = count * 0.85,然後在平均到每分鐘即是這個值,Renews threshold = num / 15

Renews (last min):Eureka Server 實際在最後一分鐘收到的 client 端發送的續約的數量,其也是一個瞬時值

當 Renews (last min) < Renews threshold 時,會啓動自我保護機制

1.4 Eureka VS ZooKeeper

1.4.1 ZooKeeper 保證 CP

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

1.4.2 Eureka 保證 AP

Eureka 看明白了這一點,因此在設計時就優先保證可用性。Eureka 各個節點都是平等的,幾個節點掛掉不會影響正常節點的工作,剩餘的節點依然可以提供註冊和查詢服務。而 Eureka 的客戶端在向某個 Eureka 註冊時發現連接失敗,則會自動切換至其他節點,只要有一臺 Eureka 在,就能保證註冊服務可用(保證可用性),只不過查到的信息可能不是最新的(不保證強一致性)。除此之外,Eureka 還有一種自我保護機制,如果在 15 分鐘內超過 85% 的節點都沒有正常的心跳,那麼 Eureka 就認爲客戶端與註冊中心出現了網絡故障,此時會出現以下幾種情況:

  1. Eureka 不再從註冊列表中移除因爲長時間沒收到心跳而應該過期的服務

  2. Eureka 仍然能夠接受新服務的註冊和查詢請求,但是不會被同步到其它節點上(即保證當前節點依然可用)

  3. 當網絡穩定時,當前實例新的註冊信息會被同步到其它節點上

因此,Eureka 可以很好的應對因網絡故障導致部分節點失去聯繫的情況,而不會像 ZooKeeper 那樣使整個註冊服務癱瘓。

2. Eureka 環境搭建

2.1 Eureka server 環境搭建

代碼已經上傳至 https://github.com/masteryourself-tutorial/tutorial-spring ,詳見 tutorial-spring-cloud/tutorial-spring-cloud-eureka/tutorial-spring-cloud-eureka-single-7001 工程

2.1.1 配置文件
1. pom.xml
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.1.4.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.SR6</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
2. application.properties
# 端口號
server.port=7001
# 此實例註冊到 eureka 服務端的 name
spring.application.name=tutorial-spring-cloud-eureka-single
# 禁用自我保護機制(默認開啓)
eureka.server.enable-self-preservation=false
# 設置自我保護機制的閾值,默認是 0.85
eureka.server.renewal-percent-threshold=0.75
# 設置清理間隔(單位:毫秒 默認是 60*1000)
eureka.server.eviction-interval-timer-in-ms=6000
# eureka 服務端的實例名稱
eureka.instance.hostname=localhost
# 指定是否向註冊中心註冊本機,false 表示不註冊,因爲本身就是 server
eureka.client.register-with-eureka=false
# 指定當前主機是否能夠獲取註冊中心的服務註冊列表,server 並不需要去檢索服務
eureka.client.fetch-registry=false
# 設置 eureka server 地址
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka
2.1.2 代碼
1. EurekaApplication7001
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication7001 {

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

}

2.2 provider 環境搭建

代碼已經上傳至 https://github.com/masteryourself-tutorial/tutorial-spring ,詳見 tutorial-spring-cloud/tutorial-spring-cloud-provider/tutorial-spring-cloud-eureka-single-7001 工程

2.2.1 配置文件
1. pom.xml
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.1.4.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.SR6</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
2. application.properties
# 端口號
server.port=5002
# 此實例註冊到 eureka 服務端的 name
spring.application.name=tutorial-spring-cloud-provider-eureka
# 指定 eureka 服務註冊中心地址
eureka.client.service-url.defaultZone=http://localhost:7001/eureka
# 此實例註冊到 eureka 服務端的唯一的實例 ID
eureka.instance.instance-id=tutorial-spring-cloud-provider-eureka-5002
# 是否顯示 IP 地址
eureka.instance.prefer-ip-address=true
# eureka 客戶需要多長時間發送心跳給 eureka 服務器,表明它仍然活着,默認爲 30 秒
eureka.instance.lease-renewal-interval-in-seconds=10
# eureka 服務器在接收到實例的最後一次發出的心跳後,需要等待多久纔可以將此實例刪除,默認爲 90 秒
eureka.instance.lease-expiration-duration-in-seconds=30
# 設置 info 信息
info.app.name=tutorial-spring-cloud-provider-eureka

2.3 consumer 環境搭建

代碼已經上傳至 https://github.com/masteryourself-tutorial/tutorial-spring ,詳見 tutorial-spring-cloud/tutorial-spring-cloud-consumer/tutorial-spring-cloud-consumer-eureka-6002 工程

2.3.1 配置文件
1. pom.xml
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.1.4.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.SR6</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
2. application.properties
# 端口號
server.port=6002
# 此實例註冊到 eureka 服務端的 name
spring.application.name=tutorial-spring-cloud-consumer-eureka
# 指定 eureka 服務註冊中心地址
eureka.client.service-url.defaultZone=http://localhost:7001/eureka
# 此實例註冊到 eureka 服務端的唯一的實例 ID
eureka.instance.instance-id=tutorial-spring-cloud-consumer-eureka-6002
# 是否顯示 IP 地址
eureka.instance.prefer-ip-address=true
# eureka 客戶需要多長時間發送心跳給 eureka 服務器,表明它仍然活着,默認爲 30 秒
eureka.instance.lease-renewal-interval-in-seconds=10
# eureka 服務器在接收到實例的最後一次發出的心跳後,需要等待多久纔可以將此實例刪除,默認爲 90 秒
eureka.instance.lease-expiration-duration-in-seconds=30
# 設置 info 信息
info.app.name=tutorial-spring-cloud-consumer-eureka
2.3.2 代碼
1. ConsumerApplication6002
@SpringBootApplication
public class ConsumerApplication6002 {

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

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

}
2. ConsumerController
@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    private static final String SERVICE_PROVIDER = "http://TUTORIAL-SPRING-CLOUD-PROVIDER-EUREKA/";

    @RequestMapping("/info")
    public Map<String, String> info() {
        String url = SERVICE_PROVIDER + "provider/info";
        return restTemplate.exchange(url, HttpMethod.GET, null, new ParameterizedTypeReference<Map<String, String>>() {
        }).getBody();
    }

    @RequestMapping("/discovery")
    public Map<String, List<String>> discoveryClient() {
        Map<String, List<String>> result = new HashMap<>(10);
        List<String> serviceIds = discoveryClient.getServices();
        serviceIds.forEach(serviceId -> {
            List<String> temp = new ArrayList<>();
            List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
            instances.forEach(serviceInstance -> temp.add(serviceInstance.getHost() + ":" + serviceInstance.getPort()));
            result.put(serviceId, temp);
        });
        return result;
    }

}

3. Eureka 集羣環境搭建

代碼已經上傳至 https://github.com/masteryourself-tutorial/tutorial-spring ,詳見 tutorial-spring-cloud/tutorial-spring-cloud-eureka/tutorial-spring-cloud-eureka-cluster-7002tutorial-spring-cloud/tutorial-spring-cloud-eureka/tutorial-spring-cloud-eureka-cluster-7003tutorial-spring-cloud/tutorial-spring-cloud-eureka/tutorial-spring-cloud-eureka-cluster-7004 工程

3.1 配置文件

1. 第一臺 server 的 application.properties
# 端口號
server.port=7002
# 此實例註冊到 eureka 服務端的 name
spring.application.name=tutorial-spring-cloud-eureka-cluster
# eureka 服務端的實例名稱
eureka.instance.hostname=eureka-7002.com
# 指定是否向註冊中心註冊本機,false 表示不註冊,因爲本身就是 server
eureka.client.register-with-eureka=false
# 指定當前主機是否能夠獲取註冊中心的服務註冊列表,server 並不需要去檢索服務
eureka.client.fetch-registry=false
# 指定 eureka 服務註冊中心地址
eureka.client.service-url.defaultZone=http://eureka-7002.com:7002/eureka,http://eureka-7003.com:7003/eureka,http://eureka-7004.com:7004/eureka
2. 第二臺 server 的 application.properties
# 端口號
server.port=7003
# 此實例註冊到 eureka 服務端的 name
spring.application.name=tutorial-spring-cloud-eureka-cluster
# eureka 服務端的實例名稱
eureka.instance.hostname=eureka-7003.com
# 指定是否向註冊中心註冊本機,false 表示不註冊,因爲本身就是 server
eureka.client.register-with-eureka=false
# 指定當前主機是否能夠獲取註冊中心的服務註冊列表,server 並不需要去檢索服務
eureka.client.fetch-registry=false
# 指定 eureka 服務註冊中心地址
eureka.client.service-url.defaultZone=http://eureka-7002.com:7002/eureka,http://eureka-7003.com:7003/eureka,http://eureka-7004.com:7004/eureka
3. 第三臺 server 的 application.properties
# 端口號
server.port=7004
# 此實例註冊到 eureka 服務端的 name
spring.application.name=tutorial-spring-cloud-eureka-cluster
# eureka 服務端的實例名稱
eureka.instance.hostname=eureka-7004.com
# 指定是否向註冊中心註冊本機,false 表示不註冊,因爲本身就是 server
eureka.client.register-with-eureka=false
# 指定當前主機是否能夠獲取註冊中心的服務註冊列表,server 並不需要去檢索服務
eureka.client.fetch-registry=false
# 指定 eureka 服務註冊中心地址
eureka.client.service-url.defaultZone=http://eureka-7002.com:7002/eureka,http://eureka-7003.com:7003/eureka,http://eureka-7004.com:7004/eureka

3.2 集羣效果

服務註冊

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章