Eureka(服務注冊中心)


1、Eureka(服務注冊中心)的基本介紹

1.1、服務注冊中心的基本介紹

服務注冊中心是用於管理微服務提供者的注冊與發現的組件。在分布式微服務架構中,服務注冊中⼼用於存儲服務提供者的地址信息、服務發布相關的屬性信息,消費者通過主動查詢和被動通知的方式獲取服務提供者的地址信息,不再需要通過硬編碼⽅式得到提供者的地址信息。

對於任何⼀個微服務,原則上都應存在或者⽀持多個提供者(provider),這是由微服務的分布式屬性決定的。所以為了⽀持彈性擴縮容特性,微服務的同一個提供者的數量和分布往往是動態變化的,也是無法預先確定的。這個時候就需要服務注冊中心來管理微服務提供者的注冊與發現了。服務注冊中心本質上就是為了解耦服務提供者和服務消費者。

 

1.2、Eureka注冊中心

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

Eureka架構中的三個核心角色:

  • 服務注冊中心(Eureka 服務端,即Eureka  Server):Eureka的服務端應用,提供服務注冊和發現功能
  • 服務提供者(Eureka 客戶端,即Eureka  client):提供服務的應用,可以是SpringBoot應用,也可以是其它任意技術實現,只要對外提供的是Rest風格服務即可
  • 服務消費者(Eureka 客戶端,即Eureka  client):消費應用從注冊中心獲取服務列表,從而得知每個服務方的信息,知道去哪里調用服務方。

Eureka包含兩個組件:Eureka Server和Eureka Client。Eureka客戶端就是指我們的開發的服務,包括服務提供者和消費者。

Eureka Server提供服務注冊服務,各個節點啟動后,會在Eureka Server中進行注冊,這樣EurekaServer中的服務注冊表中將會存儲所有可用服務節點的信息,服務節點的信息可以在界面中直觀的看到。

Eureka Client是一個java客戶端,用於簡化與Eureka Server的交互,客戶端同時也就是一個內置的、使用輪詢(round-robin)負載算法的負載均衡器。

 

1.2.1、Eureka的運行機制

Eureka 的整個運行機制大致如下:

  • 注冊:Eureka client 一次次反復連接eureka,直到注冊成功為止。
  • 拉取或訂閱:消費者會把注冊中心的整個注冊表都全部拉取過來緩存到本地,會每隔30秒拉取一次注冊表,更新注冊信息,最終服務消費者會基於服務列表做負載均衡,選中一個微服務后發起遠程調用服務。
  • 心跳:消費提供者每30秒發送一次心跳,Eureka 在每次收不到心跳后就會記一個數,如果3次沒有收到心跳 eureka 會刪除這個服務(將地址從注冊表中刪除)
  • 自我保護模式:特殊情況,由於網絡不穩定15秒內85%服務器出現心跳異常(一次收不到就算心跳一次),會保護所有的注冊信息不刪除,就算3次沒有收到心跳的情況也不會刪除,網絡恢復后,可以自動退出保護模式,在開發測試期間,可以關閉保護模式。

服務提供方與Eureka之間通過“心跳”機制進行監控,當某個服務提供方出現問題,Eureka自然會把它從服務列表中剔除,這就實現了服務的自動注冊、發現、狀態監控。

原理圖如下:

 

2、搭建一個Eureka Server

我們新建一個 springboot 項目,比如 springclouddemo 作為父工程,在該項目里建立 eureka-server 模塊作為 Eureka 的服務端,建立 order-service 作為 Eureka 的客戶端,在 springclouddemo 父工程里管理着子模塊的依賴。

項目目錄結構如下:

父工程 springclouddemo 實際只作為一個依賴管理,不需要一些代碼模塊,比如src、resource等目錄可以刪除。父工程的 pom.xml 文件內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <packaging>pom</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>springclouddemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springclouddemo</name>
    <description>Demo project for Spring Boot</description>

    <modules>
        <module>eureka-server</module>
    </modules>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR9</spring-cloud.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- springCloud -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.5.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

 

搭建 Eureka server 實際只需引入 Eureka 的服務依賴,添加一些配置,然后給啟動類加上 @EnableEurekaServer 注解即可,該注解標記了該服務是一個Eureka服務。

eureka-server 模塊的 pom.xml 文件內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.example</groupId>
        <artifactId>springclouddemo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>eureka-server</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--eureka服務端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            <!--<version>2.2.5.RELEASE</version>-->
        </dependency>
    </dependencies>

</project>

application.properties 配置如下:

spring.application.name=myeurekaserver

server.port=10086
# 表示是否將自己注冊到Eureka Server,默認為true,即也會將本身自己的服務注冊到Eureka Server中,可以設置為false
eureka.client.register-with-eureka=true
# 表示是否從Eureka Server獲取注冊信息,默認為true,可以設置為false
eureka.client.fetch-registry=true
# 設置與Eureka Server交互的地址
eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/

啟動類代碼如下:

package com.example.eurekaserver;

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

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {

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

}

啟動Eureka Server的入口,訪問 http://localhost:10086/,即可看到類似以下頁面:

紅色提示信息:自我保護模式被關閉,在網絡或其他問題的情況下可能不會保護實例失效。項目在開發階段可關閉。 

Eureka 服務端照上述步驟已搭建好,實際上上面的 eureka server 自己本身也作為一個 eureka client 注冊在了服務端,由此在頁面上的已注冊實例才可以看到  eureka server 本身。

 

2.1、搭建 Eureka client客戶端並注冊服務

下面在 springclouddemo 父工程下建立 order-service 模塊作為 Eureka 的客戶端。搭建 Eureka client 只需引入客戶端依賴,添加相應配置,給啟動類添加 @EnableEurekaClient 注解即可。

創建一個 springboot 項目 order-service,引入 eureka 客戶端依賴,當然如果該項目需要操作數據庫等還需要引入其他依賴,比如 mysql、mybatis。

pom.xml 文件內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.example</groupId>
        <artifactId>springclouddemo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>order-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>order-service</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!--eureka服務端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>com.example</groupId>
            <artifactId>feign-api</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

配置文件 application.yaml 如下:

server:
  port: 8081

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/cloud_order?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: eureka-provider #設置當前應用名稱,在eureka中Aoolication顯示,通過該名稱可以獲取路徑

mybatis:
  mapper-locations: classpath:mapping/*Mapper.xml

eureka:
  client:
    service-url:  # eureka的地址信息
      defaultZone: http://127.0.0.1:10086/eureka

啟動類代碼如下:

package com.example.orderservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient //啟用eurekaclient,新版本可以不用加該注解
public class OrderServiceApplication {

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

}

啟動該項目,在 eureka server的頁面下就可以看到實例列表下新增了該項目,也就是該項目作為 eureka client 注冊到了 eureka server 端。

 

2.2、Eureka client 調用另一服務

啟動一個 web 服務后,我們可以通過 http://ip:port/xxx 的方式來調用該服務的接口,但是這樣需要通過硬編碼的方式寫死需調用的服務的 ip 和端口號。當服務的提供者和消費者注冊到同一個注冊中心時,服務的消費者就可以拉取注冊中心的服務列表,由此來給指定的服務發起請求。

假設有模塊 order-service 和 user-service,兩個模塊都在注冊中心注冊了服務,即都是作為 eureka client,下面演示在 order-service 中調用 user-service的服務。

在 order-service 的啟動類中創建 RestTemplate,並添加負載均衡注解,如下:

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaClient //啟用eurekaclient,新版本可以省略
public class OrderServiceApplication {

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


    /**
     * 創建RestTemplate並注入Spring容器
     */
    @Bean
    @LoadBalanced  //負載均衡注解
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

上面通過添加 @LoadBalanced 注解即可實現負載均衡,即如果目標服務有多個服務器時,會負載均衡到目標服務器的其中一台中。

(實際上 RestTemplate 是用於發起http請求的,就算不是通過服務調用的方式發起,我們也可以通過該依賴來發起http請求)

通過注冊中心服務調用跟普通的 http 請求調用方式差不多,只不過在調用時,可以將目標服務在注冊中心注冊的服務名來替換目標服務具體的 ip 和端口。如下:

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.client.RestTemplate;

@Service
public class OrderService {

    @Autowired
    private RestTemplate restTemplate;

    public Order queryOrderById(Long orderId) {
        ...

        // 利用RestTemplate發起http請求
        // 目標服務的ip和端口無需寫死,只需寫上目標服務在注冊中心注冊的服務名即可。下面假設user-service模塊在注冊中心注冊的服務名為userservice
        String url = "http://userservice/user/" + orderId;

        // 發送http get請求,實現遠程調用服務
        User user = restTemplate.getForObject(url, User.class);
        
        ...
    }
}
    

上面通過寫服務名而不是具體的 ip 來發出請求,實際上 spring 會自動幫助我們從 eureka-server 端,根據 userservice 這個服務名稱來獲取服務的實例列表,而后完成負載均衡。springcloud 默認的負載均衡策略是輪詢。

 

3、Eureka調用服務的負載均衡原理(Ribbon 組件)

springcloud底層利用了一個名為Ribbon的組件來實現的負載均衡的功能。

 

 

Ribbon的底層采用了一個攔截器,攔截了RestTemplate發出的請求,對地址做了修改。用一幅圖來總結一下:

 

 

基本流程如下:( 這里模擬訂單服務 order-service 需要請求用戶服務 user-service )

  • 攔截我們的 RestTemplate 請求 http://userservice/user/1

  • RibbonLoadBalancerClient 會從請求url中獲取服務名稱,也就是 userservice

  • DynamicServerListLoadBalancer 根據 userservice 到 eureka 拉取服務列表

  • eureka 返回列表,localhost:8081、localhost:8082

  • IRule 利用內置負載均衡規則,從列表中選擇一個,例如 localhost:8081

  • RibbonLoadBalancerClient 修改請求地址,用 localhost:8081 替代userservice,得到http://localhost:8081/user/1,發起真實請求

具體原理可參考:https://www.cnblogs.com/chencan/p/16057356.html

 

3.1、負載均衡策略

springcloud 默認的負載均衡策略是輪詢,但我們可以指定使用哪種策略。

負載均衡的規則都定義在IRule接口中,而IRule有很多不同的實現類:

 

不同規則的含義如下所示:

內置負載均衡規則類 規則描述
RoundRobinRule 簡單輪詢服務列表來選擇服務器。它是Ribbon默認的負載均衡規則。
AvailabilityFilteringRule 對以下兩種服務器進行忽略: (1)在默認情況下,這台服務器如果3次連接失敗,這台服務器就會被設置為“短路”狀態。短路狀態將持續30秒,如果再次連接失敗,短路的持續時間就會幾何級地增加。 (2)並發數過高的服務器。如果一個服務器的並發連接數過高,配置了AvailabilityFilteringRule規則的客戶端也會將其忽略。並發連接數的上限,可以由客戶端的<clientName>.<clientConfigNameSpace>.ActiveConnectionsLimit屬性進行配置。
WeightedResponseTimeRule 為每一個服務器賦予一個權重值。服務器響應時間越長,這個服務器的權重就越小。這個規則會隨機選擇服務器,這個權重值會影響服務器的選擇。

ZoneAvoidanceRule

(默認的)

以區域可用的服務器為基礎進行服務器的選擇。使用Zone對服務器進行分類,這個Zone可以理解為一個機房、一個機架等。而后再對Zone內的多個服務做輪詢。
BestAvailableRule                 忽略那些短路的服務器,並選擇並發數較低的服務器。
RandomRule 隨機選擇一個可用的服務器。
RetryRule 重試機制的選擇邏輯

 

3.2、指定使用的負載均衡策略

通過定義IRule實現可以修改負載均衡的規則,有兩種方式:

方式一:在配置類中注冊一個IRule組件,注冊需要的規則。

比如在啟動類(啟動類實際也是一個配置類)中注冊 IRule 組件,代碼如下:

@SpringBootApplication
@EnableEurekaClient //啟用eurekaclient,新版本可以省略
public class OrderServiceApplication {

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


    /**
     * 創建RestTemplate並注入Spring容器
     */
    @Bean
    @LoadBalanced  //負載均衡注解
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
     @Bean
    public IRule randomRule() {
        return new RandomRule();   //指定負載均衡策略為隨機
    }
}

使用這種方式修改負載均衡策略是全局生效的,也就是該消費者即 order-service 往所有的服務發起請求時,使用的都是指定的隨機策略。

 

方式二: 在服務消費者的配置文件如 application.yml 中設置負載均衡的策略,如下:

userservice: #指定給某個目標服務發起請求時的負載均衡規則,這里是目標服務的服務名稱
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule  #負載均衡規則,這里指定的是隨機訪問

使用這種方式可針對具體某一個服務進行配置。上面的配置指定了只有往 userservice 服務發起服務調用時就使用隨機策略。

 

可參考:https://www.cnblogs.com/chencan/p/16057356.html

 

3.3、Ribbon 的飢餓加載

Ribbon 默認是采用懶加載,即第一次訪問時才會去創建LoadBalanceClient,這樣的話在第一次調用另一服務時請求時間會很長。此時我們可以配置為飢餓加載,飢餓加載會在項目啟動時就創建,這樣可以降低第一次另一服務的耗時。

通過在服務消費者配置文件中添加下面配置開啟飢餓加載:

ribbon:
  eager-load:
    enabled: true
    clients: userservice  #指定該服務需要進行飢餓加載的目標服務的服務名稱

可參考:https://www.cnblogs.com/chencan/p/16057356.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM