1. 程式人生 > 實用技巧 >【SpringCloud】Eureka服務註冊與發現

【SpringCloud】Eureka服務註冊與發現

Eureka服務註冊與發現

Eureka基礎知識

什麼是服務治理

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

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

什麼是服務註冊與發現

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

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

Eureka兩元件

Eureka包含兩個元件: Eureka Server和Eureka Client

  • Eureka Server提供服務註冊服務
    各個微服務節點通過配置啟動後,會在EurekaServer中進行註冊,這樣EurekaServer中的服務登錄檔中將會儲存所有可用服務節點的資訊,服務節點的資訊可以在介面中直觀看到。
  • EurekaClient通過註冊中心進行訪問
    是一個Java客戶端,用於簡化Eureka Server的互動,客戶端同時也具備-個內建的、 使用輪詢(round-robin)負載演算法的負載均衡器。在應用啟動後,將會向Eureka Server傳送心跳(預設週期為30秒)。 如果Eureka Server在多個心跳週期內沒有接收到某個節點的心跳,EurekaServer將 會從服務登錄檔中把這個服務節點移除(預設90秒)

單機Eureka構建步驟

IDEA生成EurekaServer端服務註冊中心# 類似物業公司

建Module

cloud-eureka-server7001

改POM

<dependencies>
    <!--eureka-server-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
    <dependency>
        <groupId>com.atguigu.springcloud</groupId>
        <artifactId>cloud-api-common</artifactId>
        <version>${project.version}</version>
    </dependency>
    <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>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

1.x和2.x的對比說明

寫YML

server:
  port: 7001

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

主啟動

@EnableEurekaServer

@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {

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

測試

http://localhost:7001/

結果頁面


No application available沒有服務被發現
因為沒有註冊服務進來當前不可能有服務被發現

EurekaClient端cloud-provider-payment8001 將註冊進EurekaServer成為服務提供者provider,類似於尚矽谷學校對外提供授課服務

建Module

cloud-provider-payment8001

改POM

新增該eureka-client元件

<!--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使用負載均衡
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

主啟動

@EnableEurekaClient

@SpringBootApplication
@EnableEurekaClient
public class PaymentMain8001 {

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

測試

先要啟動EurekaServer

http://localhost:7001/

微服務註冊名配置說明

自我保護機制

EurekaClient端cloud-consumer-order80 將註冊進EurekaServer成為服務消費者consumer,類似於尚矽谷學校上課消費的各位同學

建Module

cloud-consumer-order80

改POM

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

寫YML

server:
  port: 80

spring:
  application:
    name: cloud-order-service

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

主啟動

@EnableEurekaClient

@SpringBootApplication
@EnableEurekaClient
public class OrderMain80 {

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

測試

先要啟動EurekaServer 7001服務

再啟動服務提供者provider 8001服務

eureka伺服器

http://localhost/consumer/payment/get/31

bug

failed to bind properties under 'eureka.client.service-url' to java.util.Map

叢集Eureka構建步驟

Eureka叢集原理說明


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

Eureka叢集環境構建步驟

參考cloud-eureka-server7001

新建cloud-eureka-server7002

改POM

修改對映配置

找到C:\Windows\System32\drivers\etc路徑下的hosts檔案

修改對映配置新增hosts檔案

127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com

修改原因:主機名一個名字(127.0.0.1)還能叫叢集嗎。

重新整理hosts檔案

ipconfig /flushdns

寫YMl(以前單機)

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

7001

server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com #eureka服務端的例項名稱
  client:
    # false表示不向註冊中心註冊自己
    register-with-eureka: false
    # false表示自己端就是註冊中心,我的職責就是維護服務例項,並不需要檢索服務
    fetch-registry: false
    service-url:
      # 設定與Eureka Server互動的地址查詢服務和註冊服務都需要依賴這個地址
      # 單機 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      # 相互註冊
      defaultZone: http://eureka7002.com:7002/eureka/

7002

server:
  port: 7002

eureka:
  instance:
    hostname: eureka7002.com #eureka服務端的例項名稱
  client:
    # false表示不向註冊中心註冊自己
    register-with-eureka: false
    # false表示自己端就是註冊中心,我的職責就是維護服務例項,並不需要檢索服務
    fetch-registry: false
    service-url:
      # 設定與Eureka Server互動的地址查詢服務和註冊服務都需要依賴這個地址
      # 單機 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      # 相互註冊
      defaultZone: http://eureka7001.com:7001/eureka/

主啟動

將支付服務8001微服務釋出到上面2臺Eureka叢集配置中

YAML

eureka:
  client:
    #表示是否將自己註冊進EurekaServer預設為true
    register-with-eureka: true
    #是否從EurekaServer抓取已有的註冊訊息,預設為true,單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
    fetch-registry: true
    service-url:
      #defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

將訂單服務80微服務釋出到上面2臺Eureka叢集配置中

YML,一樣的修改

測試01

先要啟動EurekaServer,7001/7002服務

再要啟動服務提供者provider,8001

再要啟動消費者,80

http://localhost/consumer/payment/get/31

支付服務提供者8001叢集環境搭建

參考cloud-provider-payment8001

新建cloud-provider-payment8002

改POM

YAML

server:
  port: 8002
spring:
  application:
    name: cloud-payment-service8002
  datasource:
    # 當前資料來源操作型別
    type: com.alibaba.druid.pool.DruidDataSource
    # mysql驅動類
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: root
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com/eureka,http://eureka7002.com/eureka
mybatis:
  mapper-locations: classpath*:mapper/*.xml
  type-aliases-package: com.atguigu.springcloud.entities

主啟動

業務類

直接從8001粘

修改8001/8002的controller

8001

@RestController
@Slf4j
public class PaymentController {

    @Resource
    private PaymentService paymentService;

    @Value("${server.port}")
    private String serverPort;

    @PostMapping(value="/payment/create")
    public CommonResult create(@RequestBody Payment payment) {
        int result = paymentService.create(payment);
        log.info("****插入結果:" + result);

        if(result > 0){
            return  new CommonResult(200,"插入資料庫成功,serverPort: " + serverPort,result);
        } else {
            return new CommonResult(444,"插入資料庫失敗",null);
        }
    }

    @GetMapping(value="/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
        Payment payment = paymentService.getPaymentById(id);
        log.info("****插入結果:" + payment);

        if(payment != null){
            return  new CommonResult(200,"查詢成功,serverPort: " + serverPort,payment);
        } else {
            return new CommonResult(444,"沒有對應記錄,查詢ID: " + id,null);
        }
    }
}

8002

主要是在log.info中列印埠號,驗證負載均衡

取消IDEA中重複程式碼提示

File -> Setting -> Inspections -> General -> Duplicated Code 設定為不打勾√即可。

負載均衡

bug

訂單服務訪問地址不能寫死

使用服務名訪問http://CLOUD-PAYMENT-SERVICE

public class OrderController {

//    public static final String PAYMENT_URL = "http://localhost:8001";
   public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

使用@LoadBalanced註解賦予RestTemplate負載均衡的能力

ApplicationContextBean

@Configuration
public class ApplicationContextConfig {

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

測試02

先要啟動EurekaServer,7001/7002服務

再要啟動服務提供者provider,8001/8002服務

http://localhost/consumer/payment/get/31

結果

  • 負載均衡效果達到
  • 8001/8002埠交替出現

Ribbon和Eureka整合後Consumer可以直接呼叫服務而不用再關心地址和埠號,且該服務還有負載功能了。

actuator微服務資訊完善

主機名稱:服務名稱修改

當前問題

修改cloud-provoder-payment8001

yaml

效果


訪問資訊有IP資訊提示

當前問題

沒有IP提示

修改cloud-provoder-payment8001

YAML

效果圖

服務發現Discovery

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

修改cloud-provider-payment8001的Controller

@Resource
private DiscoveryClient discoveryClient;
 
@GetMapping(value = "/payment/discovery")
public Object discovery() {
    List<String> services = discoveryClient.getServices();

    for (String service : services) {
        log.info("*****element:" + service);
    }

    List< ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
    for (ServiceInstance instance : instances) {
        log.info(instance.getServiceId()+"\t" + instance.getHost() +
                "\t" + instance.getPort() +"\t" + instance.getUri());
    }

    return  this.discoveryClient;
}

8001的啟動類

@EnableDiscoveryClient

自測

先要啟動EurekaServer

再啟動8001主啟動類,需要稍等一會

http://localhost:8001/payment/discovery

效果圖

eureka自我保護

故障現象

概述
保護模式主要用於一組客戶端和Eureka Server之間存在網路分割槽場景下的保護。一旦進入保護模式。
Eureka Server將會嘗試保護其服務登錄檔中的資訊,不再刪除服務登錄檔中的資料,也就是不會登出任何微服務。

如果在Eureka Server的首頁看到以下這段提示,則說明Eureka進入了保護模式:
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT.
RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

導致原因

為什麼會產生Eureka自我保護機制?
為了防止EurekaClient可以正常執行,但是與EurekaServer網路不通情況下,EurekaServer不會立刻將EurekaClient服務剔除

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

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

屬於CAP裡面的AP分支

怎麼禁止自我保護

註冊中心eurekaServer端7001

出產預設,自我保護機制是開啟的

eureka.server.enable-self-preservation=true

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

關閉效果

在eurekaServer端7001處設定關閉自我保護機制

生產者客戶端eurekaClient端8001

預設

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

Eureka客戶端向服務端傳送心跳的時間間隔,單位為秒(預設是30秒)

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

Eureka服務端在收到最後一次心跳後等待時間上限 ,單位為秒(預設是90秒),超時剔除服務

配置

Eureka服務端
eureka:
   server:
     #關閉自我保護模式,保證不可用服務被及時刪除
     enable-self-preservation: false
     eviction-interval-timer-in-ms: 2000
 
 
服務消費者 Payment
eureka:
  client:
    #表示是否將自己註冊進EurekaServer預設為true
    register-with-eureka: true
    #是否從EurekaServer抓取已有的註冊訊息,預設為true,單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
    fetch-registry: true
    service-url:
      #叢集版
      #defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
      #單機版
      defaultZone: http://eureka7001.com:7001/eureka/
  instance:
    instance-id: payment8001
    prefer-ip-address: true #訪問路徑可以顯示ip
    #Eureka客戶端向服務端傳送心跳的實際間隔,單位為秒(預設為30秒)
    lease-renewal-interval-in-seconds: 1
    #Eureka服務端收到最後一次心跳後等待時間上線,單位為秒(預設為90秒) 超時將剔除服務
    lease-expiration-duration-in-seconds: 2

測試

7001和8001都配置成功

先啟動7001再啟動8001

先關閉8001


馬上被刪除了