关于java:Spring-Cloud-Neflix之Eureka入门和实战

4次阅读

共计 33679 个字符,预计需要花费 85 分钟才能阅读完成。

文章首发:Spring Cloud Neflix 之 Eureka 入门和实战

spring cloud 组件列表

组件名称 所属醒目 组件分类
Eureka spring-cloud-neflix 注册核心
Zuul spring-cloud-nexflix 第一代网关
Sidecar spring-cloud-nexflix 多语言
Ribbon spring-cloud-nexflix 负载平衡
Hystrix spring-cloud-nexflix 熔断器
Turbine spring-cloud-nexflix 集群监控
Feign spring-cloud-openfeign 申明式 HTTP 客户端
Consul spring-cloud-consul 注册核心
Gateway spring-cloud-gateway 第二代网关
Sleuth spring-cloud-sleuth 链路追踪
Config spring-cloud-config 配置核心
Bus spring-cloud-bus 总线
Pipeline spring-cloud-pipelines 部署管道
Dataflow spring-cloud-dataflow 数据处理
Nacos spring-cloud-alibaba 注册核心、配置核心
Sentinel spring-cloud-alibaba 流量管制和服务降级
Seata spring-cloud-alibaba 分布式事务
Dubbo RPC spring-cloud-alibaba RPC

spring cloud 和 spring boot 的版本对应

Spring Cloud 版本 Spring Boot 版本
Hoxton 2.2.x, 2.3.x (Starting with SR5)
Greenwich 2.1.x
Finchley 2.0.x
Edgware 1.5.x
Dalston 1.5.x

Eureka 入门案例

  1. 创立 Maven 父级 pom 工程
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <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.SR3</spring.cloud-version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud-version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

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

2. 在父级工程下创立 Eureka Server 子模块

           <dependencies>
           <dependency>
               <groupId>org.springframework.cloud</groupId>
               <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
           </dependency>
       </dependencies>
   
       <build>
           <plugins>
               <plugin>
                   <groupId>org.springframework.boot</groupId>
                   <artifactId>spring-boot-maven-plugin</artifactId>
               </plugin>
           </plugins>
       </build>

启动类

package com.msr.better.registry;

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);
    }
}

配置文件

application.yml(单机版)

server:
  port: 8761
eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  server:
    waitTimeInMsWhenSyncEmpty: 0
    enableSelfPreservation: false

3. 在父级工程下创立 Eureka Client 子模块

           <dependencies>
           <dependency>
            <groupId>org.springframework.cloud</groupId>
               <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
       </dependencies>
   
       <build>
           <plugins>
               <plugin>
                   <groupId>org.springframework.boot</groupId>
                   <artifactId>spring-boot-maven-plugin</artifactId>
               </plugin>
           </plugins>
        </build>

启动类

package com.msr.better.client;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class ServiceApplication {public static void main(String[] args) {SpringApplication.run(ServiceApplication.class, args);
    }
}

配置文件:resources/application.yml

server:
  port: 8081

spring:
  application:
    name: cloud-eureka-client1

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/ # 一个 Eureka Server

4. 演示成果

启动 Eureka Server 和 Eureka Client。在浏览器拜访 http://localhost:8761。能够看 … Client 胜利注册到 Eureka Server 中来了。

拜访 Eureka Server 的 REST API 接口:http://localhost:8761/eureka/…

<applications> 
  <versions__delta>1</versions__delta>  
  <apps__hashcode>UP_1_</apps__hashcode>  
  <application> 
    <name>CLOUD-EUREKA-CLIENT1</name>  
    <instance> 
      <instanceId>DESTOP-IUCSN852:cloud-eureka-client1:8081</instanceId>  
      <hostName>DESTOP-IUCSN852</hostName>  
      <app>CLOUD-EUREKA-CLIENT1</app>  
      <ipAddr>192.168.3.2</ipAddr>  
      <status>UP</status>  
      <overriddenstatus>UNKNOWN</overriddenstatus>  
      <port enabled="true">8081</port>  
      <securePort enabled="false">443</securePort>  
      <countryId>1</countryId>  
      <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo"> 
        <name>MyOwn</name> 
      </dataCenterInfo>  
      <leaseInfo> 
        <renewalIntervalInSecs>30</renewalIntervalInSecs>  
        <durationInSecs>90</durationInSecs>  
        <registrationTimestamp>1612541898071</registrationTimestamp>  
        <lastRenewalTimestamp>1612541898071</lastRenewalTimestamp>  
        <evictionTimestamp>0</evictionTimestamp>  
        <serviceUpTimestamp>1612541898071</serviceUpTimestamp> 
      </leaseInfo>  
      <metadata> 
        <management.port>8081</management.port> 
      </metadata>  
      <homePageUrl>http://DESTOP-IUCSN852:8081/</homePageUrl>  
      <statusPageUrl>http://DESTOP-IUCSN852:8081/actuator/info</statusPageUrl>  
      <healthCheckUrl>http://DESTOP-IUCSN852:8081/actuator/health</healthCheckUrl>  
      <vipAddress>cloud-eureka-client1</vipAddress>  
      <secureVipAddress>cloud-eureka-client1</secureVipAddress>  
      <isCoordinatingDiscoveryServer>false</isCoordinatingDiscoveryServer>  
      <lastUpdatedTimestamp>1612541898071</lastUpdatedTimestamp>  
      <lastDirtyTimestamp>1612541897993</lastDirtyTimestamp>  
      <actionType>ADDED</actionType> 
    </instance> 
  </application> 
</applications>

Eureka 的 Rest API 简介

​ Eureka 是 Netflix 公司开源的一款服务发现组件,该组件提供的服务发现能够为负载平衡、failover 等提供反对。Eureka 包含 Eureka Server 和 Eureka Client。Eureka Server 提供 REST 服务,Eureka Client 则是 Java 编写的客户端。

REST API 列表

操作 http 动作 形容
注册新的实例 POST /eureka/apps/{appId} 能够输出 json 或 xml 格局的 body
登记利用实例 DELETE /eureka/apps/{appId}/{instanceId} 胜利返回 200
向利用实例发送心跳 PUT /eureka/apps/{appId}/{instanceId} 胜利返回 200,如果 instanceId 不存在返回 404
查问所有实例 GET /eureka/apps 胜利返回 200,输入 json 或 xml 格局
查问指定 appId 的实例 GET /eureka/apps/{appId} 胜利返回 200,输入 json 或 xml 格局
依据指定 appId 和 instanceId 查问 GET /eureka/apps/{appId}/{instanceId} 胜利返回 200,输入 json 或 xml 格局
依据指定 instanceId 查问 GET /eureka/instances/{instanceId} 胜利返回 200,输入 json 或 xml 格局
暂停利用实例 PUT /eureka/apps/{appId}/{instanceId}/status?value=OUT_OF_SERVICE 胜利返回 200,失败返回 500
复原利用实例 DELETE /eureka/apps/{appId}/{instanceId}/status?value=UP(value 参数能够不传) 胜利返回 200,失败返回 500
更新元数据 PUT /eureka/apps/{appId}/{instanceId}/metadata?key=value 胜利返回 200,失败返回 500
依据 vip 地址查问 GET /eureka/vips/{vipAddress} 胜利返回 200,输入 json 或 xml 格局
依据 svip 地址查问 GET /eureka/svips/{svipAddress} 胜利返回 200,输入 json 或 xml 格局

Eureka 外围类

InstanceInfo(com.netflix.appinfo.InstanceInfo)

public class InstanceInfo {
    private static final String VERSION_UNKNOWN = "unknown";
    // 日志
    private static final Logger logger = LoggerFactory.getLogger(InstanceInfo.class);
    // 默认端口
    public static final int DEFAULT_PORT = 7001;
    // https 默认端口
    public static final int DEFAULT_SECURE_PORT = 7002;
    public static final int DEFAULT_COUNTRY_ID = 1;
    // 实例 id
    private volatile String instanceId;
    // 利用名
    private volatile String appName;
    // 利用所属群组
    @Auto
    private volatile String appGroupName;
    // IP 地址
    private volatile String ipAddr;
    private static final String SID_DEFAULT = "na";
    // 被废除的属性,默认为 na
    /** @deprecated */
    @Deprecated
    private volatile String sid;
    // 端口号
    private volatile int port;
    // https 端口号
    private volatile int securePort;
    // 利用实例的首页 url
    @Auto
    private volatile String homePageUrl;
    // 利用实例的状态页的 url
    @Auto
    private volatile String statusPageUrl;
    // 利用实例健康检查的 url
    @Auto
    private volatile String healthCheckUrl;
    // 利用实例健康检查 https 的 url
    @Auto
    private volatile String secureHealthCheckUrl;
    // 虚构 ip 地址
    @Auto
    private volatile String vipAddress;
    // https 的虚构 ip 地址
    @Auto
    private volatile String secureVipAddress;
    // 
    @XStreamOmitField
    private String statusPageRelativeUrl;
    // 
    @XStreamOmitField
    private String statusPageExplicitUrl;
    // 
    @XStreamOmitField
    private String healthCheckRelativeUrl;
    // 
    @XStreamOmitField
    private String healthCheckSecureExplicitUrl;
    // 
    @XStreamOmitField
    private String vipAddressUnresolved;
    // 
    @XStreamOmitField
    private String secureVipAddressUnresolved;
    // 
    @XStreamOmitField
    private String healthCheckExplicitUrl;
    // 
    /** @deprecated */
    @Deprecated
    private volatile int countryId;
    // 是否开启 https 端口
    private volatile boolean isSecurePortEnabled;
    // 是否开启 http 端口
    private volatile boolean isUnsecurePortEnabled;
    // dataCenter 信息,如 Neflix 或者 Amazon 或者 MyOwn
    private volatile DataCenterInfo dataCenterInfo;
    // 主机名称
    private volatile String hostName;
    // 实例状态,如:UP,DOWN、STARTING、OUT_OFSERVICE、UNKNOWN
    private volatile InstanceInfo.InstanceStatus status;
    // 外界须要强制笼罩的状态值,默认为 UNKNOWN
    private volatile InstanceInfo.InstanceStatus overriddenStatus;
    @XStreamOmitField
    private volatile boolean isInstanceInfoDirty;
    // 租约信息
    private volatile LeaseInfo leaseInfo;
    // 首先标识是否 discoveryServer,其实标识改 discoveryServer 是否是响应你申请的实例
    @Auto
    private volatile Boolean isCoordinatingDiscoveryServer;
    // 利用实例的元数据信息
    @XStreamAlias("metadata")
    private volatile Map<String, String> metadata;
    // 状态信息最初更新工夫
    @Auto
    private volatile Long lastUpdatedTimestamp;
    // 实例信息最新的过期工夫,在 Client 端用于示意给实例信息是否与 Eureka Server 统一,在 Server 端用于多个 Eureka Server 的信息同步解决
    @Auto
    private volatile Long lastDirtyTimestamp;
    // 示意 Eureka Server 对改实例执行的操作,包含 ADDED、MODIFIED、DELETED 这三类
    @Auto
    private volatile InstanceInfo.ActionType actionType;
    // 在 AWS 的 autoscaling group 的名称
    @Auto
    private volatile String asgName;
    // 版本
    private String version;
    
    // 无参构造函数
    private InstanceInfo() {
        this.sid = "na";
        this.port = 7001;
        this.securePort = 7002;
        this.countryId = 1;
        this.isSecurePortEnabled = false;
        this.isUnsecurePortEnabled = true;
        this.status = InstanceInfo.InstanceStatus.UP;
        this.overriddenStatus = InstanceInfo.InstanceStatus.UNKNOWN;
        this.isInstanceInfoDirty = false;
        this.isCoordinatingDiscoveryServer = Boolean.FALSE;
        this.version = "unknown";
        this.metadata = new ConcurrentHashMap();
        this.lastUpdatedTimestamp = System.currentTimeMillis();
        this.lastDirtyTimestamp = this.lastUpdatedTimestamp;
    }
    
    // 其余省略...
    
}

租约信息类:LeaseInfo(com.netflix.appinfo.LeaseInfo)

public class LeaseInfo {
    // 默认的续约周期时间,单位秒
    public static final int DEFAULT_LEASE_RENEWAL_INTERVAL = 30;
    // 默认的租约无效时长,单位秒
    public static final int DEFAULT_LEASE_DURATION = 90;
    // Client 端续约的距离周期
    private int renewalIntervalInSecs;
    // Client 端须要设置的租约的无效时长
    private int durationInSecs;
    // Server 端设置该租约的第一次注册工夫
    private long registrationTimestamp;
    // Server 端设置的该租约的最初一次续约工夫
    private long lastRenewalTimestamp;
    // Server 端设置的该租约被剔除的工夫
    private long evictionTimestamp;
    // Server 端设置的该服务实例标记为 UP 的工夫
    private long serviceUpTimestamp;

    // 无参构造函数
    private LeaseInfo() {
        this.renewalIntervalInSecs = 30;
        this.durationInSecs = 90;
    }
}

​ service discovery 的实例信息的形象接口,约定了服务发现的实例利用有哪些通用的信息。

public interface ServiceInstance {default String getInstanceId() {return null;}
    // 实例的 id
    String getServiceId();
    // 实例的 host
    String getHost();
    // 实例端口
    int getPort();
    // 实例是否开启了 https
    boolean isSecure();
    // 实例 uri 地址
    URI getUri();
    // 元数据信息
    Map<String, String> getMetadata();
    // 实例的 scheme
    default String getScheme() {return null;}
}

​ 因为 Spring Cloud Discovery 适配了 Zookeeper、Consul、Netflix Eureka、Nacos 等注册核心,因而其 ServiceInstance 定义更为形象和通用,而且采取的是定义方法的形式。Spring Cloud 对该接口的实现类为 EurekaRegistration(org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration),EurekaRegistration 实现了 ServiceInstance 接口。

InstanceStatus 是 InstanceInfo 内的枚举类,用于标识服务实例的状态。

public static enum InstanceStatus {
        UP,
        DOWN,
        STARTING,
        OUT_OF_SERVICE,
        UNKNOWN;

        private InstanceStatus() {}

        public static InstanceInfo.InstanceStatus toEnum(String s) {if (s != null) {
                try {return valueOf(s.toUpperCase());
                } catch (IllegalArgumentException var2) {InstanceInfo.logger.debug("illegal argument supplied to InstanceStatus.valueOf: {}, defaulting to {}", s, UNKNOWN);
                }
            }

            return UNKNOWN;
        }
    }

​ 从定义能够看得出来,服务实例次要有 UP、DOWN、STARTING、OUT_OFSERVICE、UNKNOWN 这几个状态。其中 OUT_OF_SERVICE 标识进行服务,即进行接管申请,处于这个状态的服务实例将不会被路由到,常常用于降级部署的场景。

服务的外围操作

对于服务发现来说,围绕服务实例次要有一下几个重要的操作:

  • 服务注册(register)
  • 服务下线(cancel)
  • 服务租约(renew)
  • 服务剔除(evict)

围绕这几个性能,Eureka 设计了几个外围操作类:

  • com.netflix.eureka.lease.LeaseManager
  • com.netflix.discovery.shared.LookupService
  • com.netflix.eureka.registry.InstanceRegistry
  • com.netflix.eureka.registry.AbstractInstanceRegistry
  • com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl

Spring CloudEureka 在 netflix Eureka 的根底上,形象或定义了如下外围类:

  • org.springframework.cloud.netflix.eureka.server.InstanceRegistry
  • org.springframework.cloud.client.serviceregistry.ServiceRegistry
  • org.springframework.cloud.netflix.eureka.serviceregistry.EurekaServiceRegistry
  • org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration
  • org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration
  • org.springframework.cloud.netflix.eureka.EurekaClientConfigBean
  • org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean

其中 LeaseManager 以及 LookupService 是 Eureka 对于服务发现相干的操作定义的接口。前者定义了服务写操作相干的办法,后者定义了查问操作相干的办法。

LeaseManager(com.netflix.eureka.lease.LeaseManager)

​ 该接口定义了应用服务实例在服务中心的几个操作方法:register、cansel、renew、evict。

public interface LeaseManager<T> {
    // 用于注册服务实例信息
    void register(T var1, int var2, boolean var3);
    // 用于删除服务实例信息
    boolean cancel(String var1, String var2, boolean var3);
    // 用于与 Eureka Server 进行心跳操作,维持租约
    boolean renew(String var1, String var2, boolean var3);
    // 服务端的一个办法,用于剔除租约过期的服务实例信息
    void evict();}

LookupService(com.netflix.discovery.shared.LookupService)

该接口定义了 Eureka Client 从服务中心获取服务实例的查询方法。

public interface LookupService<T> {Application getApplication(String var1);

    Applications getApplications();

    List<InstanceInfo> getInstancesById(String var1);

    InstanceInfo getNextServerFromEureka(String var1, boolean var2);
}

这个接口次要是给 Client 端应用的,其中定义了获取所有利用信息、依据利用 id 获取所有服务实例,以及依据 visualHostname 应用 round-robin 形式获取下一个服务实例。

Eureka 的设计理念

作为一个服务注册于发现核心,次要解决如下几个问题:

  1. 服务实例如何注册到服务中心

​ 实质上就是在服务启动的时候,须要调用 Eureka Server 的 REST API 的 register 办法,去注册利用实例的信息。对于 Java 利用则是应用 Eureka Client 封装好的 API 去调用;对于 Spring Cloud 利用更加简略,能够应用 spring-cloud-starter-netflix-eureka-client,基于 Spring Boot 的主动配置,主动帮你实现服务信息的注册。

  1. 服务实例如何从服务中心剔除

​ 失常状况下服务实例在敞开利用的时候,应该通过狗子办法或其余生命周期回调办法去调用 Eureka Server 的 REST API 的 de-register 办法,来删除本身服务实例。另外对于服务实例挂掉或者其余异常情况没有及时删除本身信息的问题,Eureka Server 要求 Client 端定时进行续约,也就是发送心跳,来证实服务实例是衰弱的,是能够调用的。如果租约超过肯定工夫没有进行续约操作,Eureka Server 端会被动剔除。这一点 Eureka Server 采纳的就是分布式应用外面经典的心跳模式。

  1. 服务实例信息的一致性问题

​ 因为服务注册以及发现核心可能是单点的,其本身必有个集群。上面次要分为 AP 优于 CP、Peer to Peer 架构、Zone 以及 Region 设计,SELF PRESERVATION 设计四个方面。

AP 优于 CP

分布式系统的 CAP 三个个性:

C(Consistency):数据一致性,即数据在存在多个正本的状况下,可能因为网络、机器故障、软件系统等问题导致数据写入局部正本胜利,局部正本失败,进而造成正本之间数据呈现不统一的,存在抵触。满足一致性则要求对数据进行更新操作之后,多正本的数据保持一致。

A(Availability):可靠性,在任何时候客户端对集群进行读写操作,求情都能失常响应,即在肯定的延时延时内实现。

P(Partition Tolerance):分区容错性,即产生通信故障的时候,整个集群被宰割为多个无奈相互通信的分区是,集群依然可用。

​ 对于分布式系统来说,个别网络条件绝对不可控,呈现网络分区是不可避免的,因而零碎必须具备分区容错性。在这个前提下,分布式系统的设计则在 CP 和 AP 之间进行抉择。不能了解成 CAP 三者之间必须三选二,他们三者之间不是对等和能够相互替换的。在分布式畛域中,P 是一个客观存在的事实,不可绕过,所以 P 与 AC 之间不是对等关系。

​ 对于 ZooKeeper,它就是 CP 的,但又不是严格的强一致性,比方客户端 A 提交了一个写操作,ZooKeeper 在板书节点操作胜利之后返回,此时假如客户端 B 的读操作申请到的是 A 写操作尚未同步到的节点,那么读取到的就不是客户端 A 歇菜做胜利之后的数据。如果在应用的时候须要强一致性,则须要在读取数据的应用执行以下 sync 操作,即 leader 节点先同步下数据,这样能力保障强统一。在极其的状况下产生网络分区的时候,如果 leader 节点不在 non-quorum 分区,那么对这个分区上节点的读写操作申请将会报错,无奈满足 Availability 个性。

​ Eureka 是在部署 AWS 的背景下设计的,其设计者认为,在云端特地是在大规模部署的状况下,失败是不可避免的,有可能是 Eureka 本身部署失败,注册的服务不可用,或者因为呈现网络分区导致服务不可用,因而不能回避这个问题。所以要在呈现了这些问题了,还要能持续提供服务注册以及发现性能,Eureka 才抉择满足 Availability(可靠性)。所以 Eureka 会保留可用及过期的数据,总比丢掉可用的数据好。这样的话,利用实例的注册信息在集群中的所有节点并不是强统一的,这就须要客户端可能反对负载平衡以及失败重试。在 Netflix 的生态外面,有 ribbon 提供这一个性能。

Peer to Peer 架构

在分布式系统中数据存在过个正本之间的复制,复制形式可分为主从复制和对等复制。

  1. 主从复制

    主从复制也就是广为人知的 Master-Slave 模式,即有一个主正本,其余正本是从正本。所有数据的写操作都提交到主正本中,最初由主正本更新到其余从正本。有同步更新、异步更新、同步及异步混合。

  2. 对等复制

    ​ 即 Peer to Peer 模式,因为任何正本都能够接管写操作,不存在写操作的压力瓶颈。然而因为每个正本都能够进行写操作解决,个正本之间的数据同步以及抵触解决是一个比拟辣手的问题。

    ​ Eureka 采纳的就是 Peer to Peer 的复制模式。上面分为 Client 端和 Server 端两个角度。

    (1)Client 端

    Client 端个别通过上面的配置 Eureka Server 的 peer 节点:

    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/

​ 实际上代码里反对 preferSameZoneEureka,即有多个分区的话,优先选择与利用实例所在分区一样的其余服务实例,如果没有找到默认应用 defaultZone。客户端应用 quarantineSet 保护一个不可用的 Eureka Server 列表,进行申请的时候,优先从可用的列表中进行抉择,如果申请失败,则切换到下一 Eureka Server 实例进行空虚,重试次数默认是 3。

​ 另外为了避免每个 Client 都依照下面配置的 Server 列表指定的程序来拜访,而造成 Erueka Server 节点申请散布不均的状况,Client 端有一个定时工作 (默认 5 分钟执行一次) 来刷洗并随机化 Eureka Server 的列表。

(2)Server 端

​ Eureka Server 自身就依赖了 Eureka Client,因为每个 Eureka Server 是作为其余 Eureka Server 的 Client。在单个 Eureka Server 启动的时候,会有一个 syncUp 的操作,通过 Eureka Client 申请其余 Eureka Server 节点中的一个节点获取注册的利用实例信息,而后复制到其余 peer 节点。

​ Eureka Server 在执行复制操作的时候,应用 HEADER_REPLICATION 的 http header 来讲这个申请操作与一般的利用实例的失常操作辨别开来。通过 HEADER_REPLICATION 来标识是复制申请,这样其余 peer 节点接管到申请的时候,就不会对它的 peer 节点进行复制操作,从而防止死循环。

​ Eureka Server 应用 peer ot peer 的复制模式,重点解决的是另外一个问题:数据复制抵触。解决办法:

  • lastDirtyTimestamp 标识
  • heartbeat

​ 针对数据的不统一,个别是通过版本号机制来解决的,最初在不同的正本之间判断申请复制数据的版本号与本地数据的版本号的高下就行了。Eureka 没有间接应用版本号的属性,而是应用 lastDirtyTimestamp 的字段来比照。

如果开启了 SyncWhenTimestampDiffers 配置(默认开启),当 lastDirtyTimestamp 不为空的时候,就会做相应的解决:

  • 如果申请参数的 lastDirtyTimestamp 值大于 Server 本地该实例的 lastDirtyTimstamp 值,则示意 EurekaServer 之间的数据呈现抵触,这个时候就返回 404,要求利用实例从新进行 register 操作。
  • 如果申请参数的 lastDirtyTimestamp 值小于 Server 本地该实例的 lstDirtyTimestamp 值,如果是 peer 节点的复制申请,则示意数据呈现抵触,返回 409 给 peer 节点,要求其同步本人最新的数据信息。

​ peer 节点之间的互相复制不能保障所有操作都胜利,所以 Eureka 还要通过利用实例与 Server 之间的 heartbeat 也就是 renewLease 操作来进行数据的最终修复,如果发现利用实例数据与某个 Server 的数据呈现不统一,则 Server 返回 404,利用实例从新进行 register 操作。

Zone 及 Region 设计

​ Netflix 的服务大部分在 Amazon 上,因而 Eureka 的设计有一部分也是基于 Amazon 的 Zone 及 Region 的基础设施之上。在 AmazonEC2 托管在寰球的各个中央,它用 Region 来代表一个独立的天文区域,比方 Eureka Server 默认设置了个 Region : us-east-I、us – west-I、us-west-2、eu-west-1。

​ 在每个 Region 上面,还分了多个 AvailabilityZone,一个 Region 对应多个 AvailabilityZone。每一个 Region 之间是互相独立以及隔离的,默认状况下资源只在单个 Region 之间的 AvailabilityZone 进行复制,跨 Regin 之间不会进行资源复制。Region 和 AvailabilityZone 之间的关系如下:

SELF PRESERVATION 设计

​ 在分布式系统设计外面,通常须要对利用实例的存活进行健康检查,比拟要害的问题是要解决好网络抖动或短暂不可用时造成的误判。另外 Eureka Server 端与 Client 端之间如果呈现网络分区问题,在极其状况下会使得 Eureka Server 清空局部服务的实例列表,这个将重大影响到 Eureka Server 的 Availability 属性。因而 Erueka Server 引入了 SELF PRESERVATION 机制。
​ Eureka Client 端与 Server 端之间有个租约,Client 要定时发送心跳来维持这个租约,示意本人还存活着。Eureka 通过以后注册的实倒数,去计算每分钟应该从利用实例接管到的心跳数,如果最近一分钟接管到的续约的次数小于等于指定阀值的话,则敞开租约生效剔除,禁止定时工作剔除生效的实例,从而爱护注册信息。

Eureka 参数调优以及监控

上面次要分为 Client 端和 Server 端两大类进行简述,Eureka 的几个外围参数

客户端参数

Client 端的外围参数

参数 默认值 阐明
eureka.client.availability-zones 告知 Client 有哪些 region 以及 availability-zones,反对配置批改运行时失效
eureka.client.filter-only-up-instances true 是否过滤出 InstanceStatus 为 UP 的实例
eureka.client.region us-east-1 指定该利用实例所在的 region,AWS datacenters 实用
eureka.client.register-with-eureka true 是否将该利用实例注册到 Eureka Server
eureka.client.prefer-szme-zone-eureka true 是否优先应用与该实例处于雷同 zone 的 Eureka Server
eureka.client.on-demand-update-status-change true 是够将本地实例状态更新通过 ApplicationInfoManager 实时触发同步到 Eureka Server
eureka.instance.metadata-map 指定利用实例的元数据信息
eureka.instance.prefer-ip-address false 指定优先应用 ip 地址代替 host name 作为实例的 hostName 字段值
eureka.instance.lease-expiration-duration-in-seconds 90 指定 Eureka Client 距离多久须要向 Eureka Server 发送心跳来告知 Eureka Server 该实例还存活

定时工作参数

参数 默认值 阐明
eureka.client.cache-refresh-executor-thread-pool-size 2 刷新缓存的 CacheRefreshThread 的线程池大小
eureka.client.cache-refresh-executor-exponential-back-off-bound 10 (刷新缓存)调度工作执行超时时下次的调度的延迟时间
reka.client.heartbeat-executor-thread-pool-size 2 心跳线程 HeartBeatThread 的线程池大小
eureka.client.heartbeat-executor-exponential-back-off-bound 10 (心跳执行)调度工作超时时下次的调度的延时工夫
eureka.client.registry-fetch-interval-seconds 30 CacheRefreshThread 线程调度频率
eureka.client.eureka-service-url-poll-interval-seconds 5*60 AsyncResolver.updateTask 刷新 Eureka Server 地址的工夫距离
eureka.client.initial-instance-info-replication-interval-seconds 40 InstanceInfoReplicator 将实例信息变更同步到 Eureka Server 的初始延时工夫
eureka.client.instance-info-replication-interval-seconds 30 InstanceInfoReplicator 将实例信息变更同步到 Eureka Server 的工夫距离
ureka.instance.lease-renewal-interval-in-seconds 30 Eureka Client 向 Eureka Server 发送心跳的工夫距离

http 参数

Eureka Client 底层 httpClient 与 Eureka Server 通信,提供的先关参数

参数 默认值 阐明
eureka.client.eureka-server-connect-timeout-seconds 5 连贯超时工夫
eureka.client.eureka-server-read-timeout-seconds 8 读超时工夫
eureka.client.eureka-server-total-connections 200 连接池最大流动连接数
eureka.client.eureka-server-total-connections-per-host 50 每个 host 能应用的最大链接数
eureka.client.eureka-connection-idle-timeout-seconds 30 连接池中链接的闲暇工夫

服务端端参数

次要蕴含这几类:基本参数、response cache 参数、peer 相参数、http 参数

基本参数

参数 默认值 阐明
eureka.server.enable-self-perservation true 是否开启自我保护模式
eureka.server.renewal-percent-threshold 0.85 指定每分钟须要收到续约次数的阈值
eureka.instance.registry.expected-number-of-renews-per-min 1 指定每分钟须要接管到的续约次数值,理论该值在其中被写死为 count*2,另外也会被更新
eureka.server.renewal-threshold-update-interval-ms 15 分钟 指定 updateRenewalThreshold 定时工作的调度频率,来动静更新 expectedNumberOfRenewsPerMin 及 numberOfRenewsPerminThreshold 值
eureka.server.eviction-interval-timer-in-ms 60*1000 指定 EvictionTask 定时工作的调度频率,用于剔除过期的实例

response cache 参数

Eureka Server 为了晋升本身 REST API 接口的性能,提供了两个缓存:一个是基于 ConcurrentMap 的 readOnlyCacheMap,一个是基于 Guava Cache 的 readWriteCacheMap。其相干参数如下:

参数 默认值 阐明
eureka.server.use-read-only-response-cache true 是否应用只读的 response-cache
eureka.server.response-cache-update-interval-ms 30*1000 设置 CacheUpdateTask 的调度工夫距离,用于从 readWriteCacheMap 更新数据到 readOnlyCacheMap。仅仅在 eureka.server.use-read-only-response-cache 为 true 的时候失效
eureka.server.response-cache-auto-expiration-in-seconds 180 设置 readWriteCacheMap 的 expireAfterWrite 参数,指定写入多长时间过过期

peer 相干参数

参数 默认值 阐明
eureka.server.peer-eureka-nodes-update-interval-ms 10 分钟 指定 peersUpdateTask 调度的工夫距离,用于从配置文件刷新 peerEurekaNodes 节点的配置信息(‘eureka.client.serviceUrl 相干 zone 的配置 ’)
eureka.server.peer-eureka-status-refresh-time-interval-ms 30*1000 指定更新 peer node 状态信息的工夫距离

http 参数

Eureka Server 须要与其余 peer 节点进行通信,复制实例信息,其底层应用 httpClient,提供相干的参数

参数 默认值 阐明
eureka.server.peer-node-connect-timeout-ms 200 连贯超时工夫
eureka.server.peer-node-read-timeout-ms 200 读超时工夫
eureka.server.peer-node-total-connections 1000 连接池最大流动连接数
eureka.server.peer-node-total-connections-per-host 500 每个 host 能应用的最大连接数
eureka.server.peer-node-connection-idle-timeout-seconds 30 连接池中连贯的闲暇工夫

参数调优

常见问题

1. 为什么服务下线了,Eureka Server 接口返回的信息还会存在?

2. 为什么服务上线了,Eureka Client 不能及时获取到?

3. 为什么会有一下提醒:

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

解决办法:

1.Eureka Server 并不是强统一的,因而 registry 中会议保留过期的实例信息。起因如下:

  • 利用实例异样挂掉,没能在挂掉之前告知 Eureka Server 要下线掉该服务实例信息。这个就须要依赖 Eureka Server 的 EvictionTask 去剔除。
  • 利用实例下线是有告知 Eureka Server 下线,然而因为 Eureka Server 的 REST API 有 response cache,因而须要期待缓存过期能力更新。
  • 因为 Eureka Server 开启并以入了 SELF PRESERVATION(自我爱护)模式,导致 registry 的信息不会因为过期而被剔除掉,直到退出 SELF PRESERVATION(自我爱护)模式。

针对 Client 下线而没有告诉 Eureka Server 的问题,能够调整 EvictionTask 的调度频率,比方把默认的工夫距离 60s,调整为 5s:

eureka:
  server:
    eviction-interval-timer-in-ms: 5000

针对 response cache 的问题,能够依据状况思考敞开 readOnlyCacheMap:

eureka:
  server:
    use-read-only-response-cache: false

或者调整 readWriteCacheMap 的过期工夫:

eureka:
  server:
    response-cache-auto-expiration-in-seconds: 60

针对 SELF PRESERVATION(自我爱护)的问题,在测试环境能够将 enable-self-preservation 设置为 false:

eureka:
  server:
    enable-self-preservation: false

敞开之后会提醒:

THE SELF PRESER VAT ION MODE IS TURNED OFF.  THIS MAY NOT PRO TECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.

或者:

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

2. 针对新服务上线,Eureka Client 获取不及时的问题,在测试环境,能够适当进步 client 端拉取 Server 注册信息的频率,例如上面将默认的 30s 改为 5s:

eureka:
  client:
    registry-fetch-interval-seconds: 5

3. 在理论生产过程中,常常会有网络抖动等问题造成服务实例与 Eureka Server 的心跳未能如期放弃,然而服务实例自身是衰弱的,这个时候如果依照租约剔除机制剔除的话,会造成误判无果大范畴误判的话,可能导致整个服务注册列表的大部分注册信息被删除,从而没有可用服务。Eureka 为了解决这个问题引入了 SELF PRESERVATION 机制,当最近一分钟接管到的租约次数小于等于指定阈值的话,则敞开租约生效剔除,禁止定时工作生效的实例,从而爱护注册信息。

在生产环境下,能够吧 renewwalPercentThreshold 及 leaseRenewalIntervalInSeconds 参数调小一点,从而进步触发 SELF PRESERVATION 机制的阈值。

eureka:
  instance:
    lease-renewal-interval-in-seconds: 10 #默认是 30
    renewal-percent-threshold: 0.49       #默认是 0.85

监控指标

Eureka 内置了基于 servo 的指标统计,具体在 com.netflix.eureka.util.EurekaMonitors。Spring Boot 2.x 版本改为应用 Micrometer,不再反对 Neflix Servo,转而反对 Neflix Servo 的替代品Neflix Spectator。不过对于 Servo,能够通过DefaultMonitorRegistry.getInstance().getRegisteredMonitors 来获取所有注册了的 Monitor,进而获取其指标值。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.netflix.eureka.util;

import com.netflix.appinfo.AmazonInfo;
import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.appinfo.DataCenterInfo;
import com.netflix.appinfo.AmazonInfo.MetaDataKey;
import com.netflix.appinfo.DataCenterInfo.Name;
import com.netflix.servo.DefaultMonitorRegistry;
import com.netflix.servo.annotations.DataSourceType;
import com.netflix.servo.annotations.Monitor;
import com.netflix.servo.monitor.Monitors;
import java.util.concurrent.atomic.AtomicLong;

public enum EurekaMonitors {
    // 自启动以来收到的总续约次数
    RENEW("renewCounter", "Number of total renews seen since startup"),
    // 自启动以来收到的总勾销租约次数
    CANCEL("cancelCounter", "Number of total cancels seen since startup"),
    // 自启动以来查问 registry 的总次数
    GET_ALL_CACHE_MISS("getAllCacheMissCounter", "Number of total registery queries seen since startup"),
    // 自启动以来 delta 查问 registry 的总次数
    GET_ALL_CACHE_MISS_DELTA("getAllCacheMissDeltaCounter", "Number of total registery queries for delta seen since startup"),
    // 自启动以来应用 remote region 查问 registry 的总次数
    GET_ALL_WITH_REMOTE_REGIONS_CACHE_MISS("getAllWithRemoteRegionCacheMissCounter", "Number of total registry with remote region queries seen since startup"),
  // 自启动以来应用 remote region 及 delta 形式查问 registry 的总次数
  GET_ALL_WITH_REMOTE_REGIONS_CACHE_MISS_DELTA("getAllWithRemoteRegionCacheMissDeltaCounter", "Number of total registry queries for delta with remote region seen since startup"),
    // 自启动以来查问 delta 的总次数
    GET_ALL_DELTA("getAllDeltaCounter", "Number of total deltas since startup"),
    // 自启动以来传递 regions 查问 delta 的总次数
    GET_ALL_DELTA_WITH_REMOTE_REGIONS("getAllDeltaWithRemoteRegionCounter", "Number of total deltas with remote regions since startup"),
    // 自启动以来查问 '/{version}/apps' 的次数
    GET_ALL("getAllCounter", "Number of total registry queries seen since startup"),
    // 自启动以来传递 regions 参数查问 '/{version}/apps' 的次数
    GET_ALL_WITH_REMOTE_REGIONS("getAllWithRemoteRegionCounter", "Number of total registry queries with remote regions, seen since startup"),
    // 自启动以来申请 /{version}/apps/{appId}的总次数
    GET_APPLICATION("getApplicationCounter", "Number of total application queries seen since startup"),
    // 自启动以来 register 的总次数
    REGISTER("registerCounter", "Number of total registers seen since startup"),
    // 自启动以来剔除过期实例的总次数
    EXPIRED("expiredCounter", "Number of total expired leases since startup"),
    // 自启动以来 statusUpdate 的总次数
    STATUS_UPDATE("statusUpdateCounter", "Number of total admin status updates since startup"),
    // 自启动以来 deleteStatusOverride 的总次数
    STATUS_OVERRIDE_DELETE("statusOverrideDeleteCounter", "Number of status override removals"),
    // 自启动以来收到 cancel 申请时对应实例找不到的次数
    CANCEL_NOT_FOUND("cancelNotFoundCounter", "Number of total cancel requests on non-existing instance since startup"),
    // 自启动以来收到 renew 申请时对应实例找不到的次数
    RENEW_NOT_FOUND("renewNotFoundexpiredCounter", "Number of total renew on non-existing instance since startup"),
    REJECTED_REPLICATIONS("numOfRejectedReplications", "Number of replications rejected because of full queue"),
    FAILED_REPLICATIONS("numOfFailedReplications", "Number of failed replications - likely from timeouts"),
    // 因为开启 rate limiter 被抛弃的申请数量
    RATE_LIMITED("numOfRateLimitedRequests", "Number of requests discarded by the rate limiter"),
    // 如果开启 rate limiter 的话,将被抛弃的申请数
    RATE_LIMITED_CANDIDATES("numOfRateLimitedRequestCandidates", "Number of requests that would be discarded if the rate limiter's throttling is activated"),
    // 开启 rate limiter 时申请全量 registry 被抛弃的申请数
    RATE_LIMITED_FULL_FETCH("numOfRateLimitedFullFetchRequests", "Number of full registry fetch requests discarded by the rate limiter"),
    // 如果开启 rate limiter 时申请全量 registry 将被抛弃的申请数
    RATE_LIMITED_FULL_FETCH_CANDIDATES("numOfRateLimitedFullFetchRequestCandidates", "Number of full registry fetch requests that would be discarded if the rate limiter's throttling is activated");

    private final String name;
    private final String myZoneCounterName;
    private final String description;
    @Monitor(
        name = "count",
        type = DataSourceType.COUNTER
    )
    private final AtomicLong counter = new AtomicLong();
    @Monitor(
        name = "count-minus-replication",
        type = DataSourceType.COUNTER
    )
    private final AtomicLong myZoneCounter = new AtomicLong();

    private EurekaMonitors(String name, String description) {
        this.name = name;
        this.description = description;
        DataCenterInfo dcInfo = ApplicationInfoManager.getInstance().getInfo().getDataCenterInfo();
        if (dcInfo.getName() == Name.Amazon) {this.myZoneCounterName = ((AmazonInfo)dcInfo).get(MetaDataKey.availabilityZone) + "." + name;
        } else {this.myZoneCounterName = "dcmaster." + name;}

    }

    public void increment() {this.increment(false);
    }

    public void increment(boolean isReplication) {this.counter.incrementAndGet();
        if (!isReplication) {this.myZoneCounter.incrementAndGet();
        }

    }

    public String getName() {return this.name;}

    public String getZoneSpecificName() {return this.myZoneCounterName;}

    public String getDescription() {return this.description;}

    public long getCount() {return this.counter.get();
    }

    public long getZoneSpecificCount() {return this.myZoneCounter.get();
    }

    public static void registerAllStats() {EurekaMonitors[] var0 = values();
        int var1 = var0.length;

        for(int var2 = 0; var2 < var1; ++var2) {EurekaMonitors c = var0[var2];
            Monitors.registerObject(c.getName(), c);
        }

    }

    public static void shutdown() {EurekaMonitors[] var0 = values();
        int var1 = var0.length;

        for(int var2 = 0; var2 < var1; ++var2) {EurekaMonitors c = var0[var2];
            DefaultMonitorRegistry.getInstance().unregister(Monitors.newObjectMonitor(c.getName(), c));
        }

    }
}

Eureka 实战

Eureka Server 在线扩容

筹备工作:创立 cloud 工程

创立 cloud-config-server 子模块。

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
    </dependencies>

启动类

package com.msr.batter.config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {public static void main(String[] args) {SpringApplication.run(ConfigApplication.class, args);
    }
}

配置文件:resources/bootstrap.yml

spring:
  profiles:
    active: native
  application:
    name: config-server
server:
  port: 8888

这里不是次要解说 Spring Cloud Config 内容。所以对于配置文件应用 native 的 profile,也就是应用文件来存储配置,默认是放在 resources/config 目录下。在 Eureka Server 和 Eureka Client 模块别离引入 spring-cloud-starter-config。别离都创立一个ServerControllerClientController

Eureka Client 的配置文件:resources/config/eureka-client.yml

server:
  port: 8081

spring:
  application:
    name: cloud-eureka-client1

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/ # 一个 Eureka Server

Eureka Server 配置文件:resources/config/eureka-server-peer1.yml

server:
  port: 8761
spring:
  application:
    name: cloud-eureka-server
eureka:
  instance:
    hostname: localhost
    preferIpAddress: true
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/ # 一个 Eureka Server
  server:
    waitTimeInMsWhenSyncEmpty: 0
    enableSelfPreservation: false

cloud-eureka-server 模块

        <!--    增加    -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

ServerController

package com.msr.better.registry.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("query")
public class ServerController {

    @Autowired
    EurekaClientConfigBean eurekaClientConfigBean;

    @GetMapping("eureka-server")
    public Object getEurekaServerUrl() {return eurekaClientConfigBean.getServiceUrl();
    }
}

配置文件:resources/bootstrap.yml

spring:
  application:
    name: cloud-eureka-server
  cloud:
    config:
      uri: http://localhost:8888
management:
  endpoints:
    web:
      exposure:
        include: '*'

配置文件:resources/application.yml

eureka:
  server:
    peer-eureka-nodes-update-interval-ms: 10000 #默认是 10 分钟即 600000,这里为了验证改为 10 秒

cloud-erueka-client 子模块

        <!--    增加    -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>

ClientController

package com.msr.better.client.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author MaiShuRen
 * @date 2020-10-23
 */
@RestController
@RequestMapping("query")
public class ClientController {

    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private EurekaClientConfigBean clientConfigBean;

    /**
     * 服务实例信息
     *
     * @return
     */
    @GetMapping("info")
    public Map<String, Object> info() {Map<String, Object> map = new HashMap<>(16);
        List<String> services = discoveryClient.getServices();
        String description = discoveryClient.description();
        int order = discoveryClient.getOrder();

        map.put("services", services);
        map.put("description", description);
        map.put("order", order);

        return map;
    }

    /**
     * 依据服务名称获取服务实例列表
     *
     * @param name 服务名
     * @return 返回后果
     */
    @GetMapping("instance/{name}")
    public List<ServiceInstance> getIns(@PathVariable String name) {return discoveryClient.getInstances(name);
    }

    /**
     * 获取 Eureka Server 的 url
     *
     * @return
     */
    @GetMapping("eureka-server")
    public Object getServiceUrl() {return clientConfigBean.getServiceUrl();
    }
}

配置文件:resources/bootstrap.yml

spring:
  application:
    name: cloud-eureka-client
  cloud:
    config:
      uri: http://localhost:8888
management:
  endpoints:
    web:
      exposure:
        include: '*'

费别启动cloud-config-servercloud-eureka-server(应用 peer1 的 profile)、cloud-eureka-client

mvn spring-boot:run 在 cloud-config-server 目录下执行,启动 cloud-config-server 服务

mvn spring-boot:run -Dspring-boot.run.profiles=peer1 在 cloud-eureka-server 目录下执行,启动 cloud-eureka-server

mvn spring-boot:run 在 cloud-eureka-client 目录下执行,启动 cloud-eureka-client

扩容两个 Eureka Server

cloud-config-server 下新增配置 cloud-eureka-server-peer2.ymlcloud-eureka-server-peer3.yml

cloud-eureka-server-peer2.yml

server:
  port: 8762

eureka:
  instance:
    hostname: localhost
    preferIpAddress: true
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/,http://localhost:8763/eureka/
  server:
    waitTimeInMsWhenSyncEmpty: 0
    enableSelfPreservation: false

启动:mvn spring-boot:run -Dspring-boot.run.profiles=peer2

cloud-eureka-server-peer3.yml

server:
  port: 8763

eureka:
  instance:
    hostname: localhost
    preferIpAddress: true
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/
  server:
    waitTimeInMsWhenSyncEmpty: 0
    enableSelfPreservation: false

启动:mvn spring-boot:run -Dspring.profiles.active=peer3

批改一下 cloud-eureka-client.ymlcloud-eureka-server-peer1.yml 的配置文件

cloud-eureka-server-peer1.yml 的批改

server:
  port: 8761
eureka:
  instance:
    hostname: localhost
    preferIpAddress: true
  client:
    registerWithEureka: true
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://localhost:8762/eureka/,http://localhost:8763/eureka/
  server:
    waitTimeInMsWhenSyncEmpty: 0
    enableSelfPreservation: false

cloud-eureka-client.yml 的批改

server:
  port: 8081

spring:
  application:
    name: cloud-eureka-client1
eureka:
  client:
    serviceUrl:
      efaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/,http://localhost:8763/eureka/

而后重新启动 cloud-config-server,使批改的配置失效。而后应用 Postman 这类型工具通过POST 申请 去拜访 http://localhost:8081/actuato…cloud-eureka-clientcloud-eureka-server-peer1。或者应用上面的命令

curl -i -X POST localhost:8081/actuator/refresh

curl -i -X POST localhost:8761/actuator/refresh

而后在浏览器或者应用 curl 命令去申请,之前 cloud-config-server 写好的一个接口:http://localhost:8081/query/e…

{“defaultZone”: “http://localhost:8761/eureka/,

正文完
 0