06-SpringCloud Eureka

Eureka基礎知識

什麼是服務治理

Spring Cloud封裝了Netflix 公司開發的Eureka模組來實現服務治理

在傳統的RPC遠程調用框架中,管理每個服務與服務之間依賴關係比較複雜,管理比較複雜,所以需要使用服務治理,管理服務於服務之間依賴關係,可以實現服務調用、負載均衡、容錯等,實現服務發現與註冊。

什麼是服務註冊與發現

Eureka採用了CS的設計架構,Eureka Sever作為服務註冊功能的伺服器,它是服務註冊中心。而系統中的其他微服務,使用Eureka的客戶端連接到 Eureka Server並維持心跳連接。這樣系統的維護人員就可以通過Eureka Server來監控系統中各個微服務是否正常運行。

在服務註冊與發現中,有一個註冊中心。當伺服器啟動的時候,會把當前自己伺服器的資訊比如服務地址通訊地址等以別名方式註冊到註冊中心上。另一方(消費者服務提供者),以該別名的方式去註冊中心上獲取到實際的服務通訊地址,然後再實現本地RPC調用,RPC遠程調用框架核心設計思想:在於註冊中心,因為使用註冊中心管理每個服務與服務之間的一個依賴關係(服務治理概念)。在任何RPC遠程框架中,都會有一個註冊中心存放服務地址相關資訊(介面地址)

Eureka包含兩個組件:

Eureka Server

  • Eureka Server提供服務註冊服務
    • 各個微服務節點通過配置啟動後,會在EurekaServer中進行註冊,這樣EurekaServer中的服務註冊表中將會存儲所有可用服務節點的資訊,服務節點的資訊可以在介面中直觀看到。

Eureka Client

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

單機版Eureka搭建

搭建EurekaServer

創建Maven項目

 

 

 

1.x 與 2.x 對比

<!-- eureka新舊版本 -->
<!-- 以前的老版本(2018)-->
<dependency>
    <groupid>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- 現在新版本(2020.2)--><!-- 我們使用最新的 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

修改POM.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="//maven.apache.org/POM/4.0.0"
         xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="//maven.apache.org/POM/4.0.0 //maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>com.dance</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-eureka-server7001</artifactId>

    <dependencies>
        <!--eureka-server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!-- dependent on common modules -->
        <dependency>
            <groupId>com.dance</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--boot web actuator-->
        <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.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>


</project>

添加yml配置

server:
  port: 7001

eureka:
  instance:
    hostname: locathost #eureka服務端的實例名稱
  client:
    #false表示不向註冊中心註冊自己。
    register-with-eureka: false
    #false表示自己端就是註冊中心,我的職責就是維護服務實例,並不需要去檢索服務
    fetch-registry: false
    service-url:
      #設置與Eureka server交互的地址查詢服務和註冊服務都需要依賴這個地址。
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

新增主啟動類

package com.dance.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {

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

}

5.測試運行EurekaMain7001,瀏覽器輸入//localhost:7001/回車,會查看到Spring Eureka服務主頁。

服務端搭建成功

提供者(支付微服務8001工程)註冊服務到EurekaServer

修改提供者POM.xml

添加spring-cloud-starter-netflix-eureka-client依賴

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

修改yml配置

eureka:
  client:
    #表示是否將自己註冊進Eurekaserver默認為true。
    register-with-eureka: true
    #是否從EurekaServer抓取已有的註冊資訊,默認為true。單節點無所謂,集群必須設置為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

修改主啟動類

@EnableEurekaClient//<-----添加該註解

重啟提供者服務,並查看Eureka控制台

 

提供者註冊成功

Eureka自我保護機制

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY』RE NOT. RENEWALS ARELESSER THAN THRESHOLD AND HENCFT ARE NOT BEING EXPIRED JUST TO BE SAFE.

緊急情況!EUREKA可能錯誤地聲稱實例在沒有啟動的情況下啟動了。續訂小於閾值,因此實例不會為了安全而過期。

消費者(訂單微服務80工程)註冊服務到EurekaServer

修改消費者POM.xml

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

修改yml配置

eureka:
  client:
    #表示是否將自己註冊進Eurekaserver默認為true。
    register-with-eureka: true
    #是否從EurekaServer抓取已有的註冊資訊,默認為true。單節點無所謂,集群必須設置為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

修改主啟動類

@EnableEurekaClient//<--- 添加該標籤

測試

  • 啟動cloud-provider-payment8001、cloud-eureka-server7001和cloud-consumer-order80這三工程。
  • 瀏覽器輸入 //localhost:7001 , 在主頁的Instances currently registered with Eureka將會看到cloud-provider-payment8001、cloud-consumer-order80兩個工程名。

注意,application.yml配置中層次縮進和空格,兩者不能少,否則,會拋出異常Failed to bind properties under ‘eureka.client.service-url’ to java.util.Map <java.lang.String, java.lang.String>。

Eureka集群原理說明

Eureka集群原理說明

 

服務註冊:將服務資訊註冊進註冊中心

服務發現:從註冊中心上獲取服務資訊

實質:存key服務命取value閉用地址

  1. 先啟動eureka注主冊中心
  2. 啟動服務提供者payment支付服務
  3. 支付服務啟動後會把自身資訊(比服務地址L以別名方式注朋進eureka
  4. 消費者order服務在需要調用介面時,使用服務別名去註冊中心獲取實際的RPC遠程調用地址
  5. 消去者導調用地址後,底屋實際是利用HttpClient技術實現遠程調用
  6. 消費者實癸導服務地址後會快取在本地jvm記憶體中,默認每間隔30秒更新—次服務調用地址

問題:微服務RPC遠程服務調用最核心的是什麼

高可用:試想你的註冊中心只有一個only one,萬一它出故障了,會導致整個為服務環境不可用。

解決辦法:搭建Eureka註冊中心集群,實現負載均衡+故障容錯。

互相註冊,相互守望。

Eureka集群環境搭建

搭建7002

創建cloud-eureka-server7002模組,參考搭建EurekaServer

 

修改Hosts文件

  • 找到C:\Windows\System32\drivers\etc路徑下的hosts文件,修改映射配置添加進hosts文件
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com

修改7001配置文件

server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com #eureka服務端的實例名稱
  client:
    register-with-eureka: false     #false表示不向註冊中心註冊自己。
    fetch-registry: false     #false表示自己端就是註冊中心,我的職責就是維護服務實例,並不需要去檢索服務
    service-url:
      #集群指向其它eureka
      defaultZone: http://eureka7002.com:7002/eureka/
      #單機就是7001自己
      #defaultZone: http://eureka7001.com:7001/eureka/

修改7002配置文件

server:
  port: 7002

eureka:
  instance:
    hostname: eureka7002.com #eureka服務端的實例名稱
  client:
    register-with-eureka: false     #false表示不向註冊中心註冊自己。
    fetch-registry: false     #false表示自己端就是註冊中心,我的職責就是維護服務實例,並不需要去檢索服務
    service-url:
      #集群指向其它eureka
      defaultZone: http://eureka7001.com:7001/eureka/
      #單機就是7002自己
      #defaultZone: http://eureka7002.com:7002/eureka/

測試集群

啟動7002和7001,先停掉8001和80

通過域名訪問7001,可以看到它指向了7002

//eureka7001.com:7001/

 

 

 

通過域名訪問7002.可以看到它指向了7001

//eureka7002.com:7002

 

 

 

測試完成,集群搭建成功!

 

提供者(支付微服務8001工程)註冊服務到Eureka集群

修改yml配置

#將單一地址修改為多地址通過逗號分割
defaultZone: http://eureka7001.com:7001/eureka, //eureka7002.com:7002/eureka

消費者(訂單微服務80工程)註冊服務到Eureka集群

修改yml配置

#將單一地址修改為多地址通過逗號分割
defaultZone: http://eureka7001.com:7001/eureka, //eureka7002.com:7002/eureka  

測試註冊服務

  1. 啟動集群7001和7002
  2. 啟動提供者和消費者
  3. 訪問Eureka查看

7001註冊成功

 

7002註冊成功

提供者集群搭建

參考cloud-provider-payment8001搭建cloud-provider-payment8002,應為在微服務當中為了解決單點故障問題,提供者應該是多台而不是一台

修改yml配置

server:
  port: 8002

測試

  1. 啟動Eureka集群
  2. 啟動提供者集群8001 8002
  3. 訪問Eureka測試

 

7001集群註冊成功

 

7002集群註冊成功

消費者通過別名調用提供者集群

修改消費者Controller

//public static final String PAYMENT_URL = "//localhost:8001";

// 將指定的地址修改為服務別名 服務別名從Eureka控制台查看

public static final String PAYMENT_URL = "//CLOUD-PAYMENT-SERVICE";

 

修改RestTemplate配置

@Bean
// 應為是集群提供服務,所以需要本地負載
@LoadBalanced
public RestTemplate getRestTemplate(){
  return new RestTemplate();
}

測試調用

重啟80工程,第一次調用 8002 提供者提供服務

 

刷新幾次可以看到 8001 提供者提供服務

 

測試成功

actuator微服務資訊完善

主機名稱:服務名稱修改(也就是將IP地址,換成可讀性高的名字)

修改cloud-provider-payment8001,cloud-provider-payment8002

修改部分 – YML – eureka.instance.instance-id

也就是修復Eureka展示的問題

修改提供者8001配置

eureka: 
    instance:
        instance-id: cloud-payment-service8001

修改提供者8002配置

eureka: 
    instance:
        instance-id: cloud-payment-service8002

重啟測試

 

 

 

 可以了,但是現在滑鼠懸浮在上面左下角出現的還是域名,而不是IP

訪問資訊有IP資訊提示

(就是將滑鼠指針移至payment8001,payment8002名下,會有IP地址提示)

修改8001和8002

eureka:
  instance:
    prefer-ip-address: true #添加此處

服務發現 Discovery

對於註冊進eureka裡面的微服務,可以通過服務發現來獲得該服務的資訊

修改8001的Controller

@RestController
@Slf4j
public class PaymentController {

    ..................

    @Resource
    private DiscoveryClient discoveryClient;

    @GetMapping("/payment/discovery")
    public Object discovery(){
        // 獲取描述
        String description = discoveryClient.description();
        log.info(description);
        // 獲取所有服務別名
        List<String> services = discoveryClient.getServices();
        services.forEach(System.out::println);
        // 根據別名獲取具體提供者資訊
        services.forEach(x -> {
            List<ServiceInstance> instances = discoveryClient.getInstances(x);
            for (ServiceInstance instance : instances) {
                log.info("{instanceId:"+instance.getInstanceId()+",scheme:"+instance.getScheme()+
                        ",serviceId:"+instance.getServiceId()+",host:"+instance.getHost()+
                        ",port:"+instance.getPort()+",uri:"+instance.getUri()+
                        ",metadata:"+instance.getMetadata()+"}");
            }
        });
        return services;
    }

    .......................
}

修改主啟動類

@EnableDiscoveryClient//添加該註解

測試

重啟8001,8002提供者

測試

//localhost:8001/payment/discovery

前台返回:

 

後端列印:

2021-08-22 01:12:33.434  INFO [cloud-payment-service,912fc71f3a7277d4,912fc71f3a7277d4,true] 8088 --- [nio-8001-exec-2] c.d.s.controller.PaymentController       : Spring Cloud Eureka Discovery Client
cloud-payment-service
cloud-order-service
2021-08-22 01:12:33.442  INFO [cloud-payment-service,912fc71f3a7277d4,912fc71f3a7277d4,true] 8088 --- [nio-8001-exec-2] c.d.s.controller.PaymentController       : {instanceId:cloud-payment-service8002,scheme:null,serviceId:CLOUD-PAYMENT-SERVICE,host:192.168.0.101,port:8002,uri:http://192.168.0.101:8002,metadata:{management.port=8002}}
2021-08-22 01:12:33.442  INFO [cloud-payment-service,912fc71f3a7277d4,912fc71f3a7277d4,true] 8088 --- [nio-8001-exec-2] c.d.s.controller.PaymentController       : {instanceId:ZB-PF2P9QVH.360buyAD.local:cloud-order-service:80,scheme:null,serviceId:CLOUD-ORDER-SERVICE,host:ZB-PF2P9QVH.360buyAD.local,port:80,uri:http://ZB-PF2P9QVH.360buyAD.local:80,metadata:{management.port=80}}

Eureka 自我保護機制

概述

保護模式主要用於一組客戶端和Eureka Server之間存在網路分區場景下的保護。一旦進入保護模式,Eureka Server將會嘗試保護其服務註冊表中的資訊,不再刪除服務註冊表中的數據,也就是不會註銷任何微服務。

如果在Eureka Server的首頁看到以下這段提示,則說明Eureka進入了保護模式:

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY』RE NOT. RENEWALS ARE LESSER THANTHRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUSTTO BE SAFE

導致原因

一句話:某時刻某一個微服務不可用了,Eureka不會立刻清理,依舊會對該微服務的資訊進行保存。

屬於CAP裡面的AP分支。

為什麼會產生Eureka自我保護機制

為了EurekaClient可以正常運行,防止與EurekaServer網路不通情況下,EurekaServer不會立刻將EurekaClient服務剔除

什麼是自我保護模式

默認情況下,如果EurekaServer在一定時間內沒有接收到某個微服務實例的心跳,EurekaServer將會註銷該實例(默認90秒)。但是當網路分區故障發生(延時、卡頓、擁擠)時,微服務與EurekaServer之間無法正常通訊,以上行為可能變得非常危險了——因為微服務本身其實是健康的,此時本不應該註銷這個微服務。Eureka通過「自我保護模式」來解決這個問題——當EurekaServer節點在短時間內丟失過多客戶端時(可能發生了網路分區故障),那麼這個節點就會進入自我保護模式。

 

自我保護機制∶默認情況下EurekaClient定時向EurekaServer端發送心跳包,如果EurekaServer端在一定時間內(默認90秒)沒有收到EurekaClient發送的心跳包,便會直接從服務註冊列表中剔除該服務,但是在短時間( 90秒中)內丟失了大量的服務實例心跳,這時候Eurekaserver會開啟自我保護機制,不會剔除該服務(該現象可能出現在如果網路不通但是EurekaClient為出現宕機,此時如果換做別的註冊中心如果一定時間內沒有收到心跳會將剔除該服務,這樣就出現了嚴重失誤,因為客戶端還能正常發送心跳,只是網路延遲問題,而保護機制是為了解決此問題而產生的)。

在自我保護模式中,Eureka Server會保護服務註冊表中的資訊,不再註銷任何服務實例。

它的設計哲學就是寧可保留錯誤的服務註冊資訊,也不盲目註銷任何可能健康的服務實例。一句話講解:好死不如賴活著。

綜上,自我保護模式是一種應對網路異常的安全保護措施。它的架構哲學是寧可同時保留所有微服務(健康的微服務和不健康的微服務都會保留)也不盲目註銷任何健康的微服務。使用自我保護模式,可以讓Eureka集群更加的健壯、穩定。

怎麼禁止自我保護

在7001處設置關閉自我保護機制

默認,自我保護機制是開啟的

使用eureka.server.enable-self-preservation = false可以禁用自我保護模式

eureka:
  ...
  server:
    #關閉自我保護機制,保證不可用服務被及時踢除
    enable-self-preservation: false
    eviction-interval-timer-in-ms: 2000

關閉效果:

spring-eureka主頁會顯示出一句:

 

 

THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.

在8001設置心跳檢測和續約時間

  • 默認心跳時間為30秒
    • eureka.instance.lease-renewal-interval-in-seconds=30
    • 續約時間為90秒
      • eureka.instance.lease-expiration-duration-in-seconds=90
eureka:
  ...
  instance:
    instance-id: payment8001
    prefer-ip-address: true
    #心跳檢測與續約時間
    #開發時沒置小些,保證服務關閉後註冊中心能即使剔除服務
    #Eureka客戶端向服務端發送心跳的時間間隔,單位為秒(默認是30秒)
    lease-renewal-interval-in-seconds: 1
    #Eureka服務端在收到最後一次心跳後等待時間上限,單位為秒(默認是90秒),超時將剔除服務
    lease-expiration-duration-in-seconds: 2

測試

  • 重啟Eureka集群
  • 重啟提供者集群
  • 訪問控制台查看

因為修改的是7001,所以查看7001控制台

註冊成功

現在停止8001提供者,刷新7001控制台

 

已經被踢出了

 

7002也踢出了8001

ok 到這裡Eureka就寫完啦,完結撒花~,這些知識應該夠日常開發用了,而且用不用Eureka還另說,為啥不用凡是關注這一塊的差不多都知道

作者:彼岸舞

時間:2021\08\22

內容關於:Spring Cloud H版

本文屬於作者原創,未經允許,禁止轉發