(一)springcloud 服务注册与发现-spring cloud eureka

概要

You’ll setup a Netflix Eureka service registry and then build a client that both registers itself with the registry and uses it to resolve its own host. A service registry is useful because it enables client-side load-balancing and decouples service providers from consumers without the need for DNS.

您将设置一个Netflix Eureka服务注册表,然后构建一个客户端,该客户端都将自己注册到注册表并使用它来解析自己的主机。 服务注册表很有用,因为它支持客户端负载平衡,并且无需DNS即可将服务提供者与消费者分离

关键字:client-side load-balancing(客户端负载均衡)decouples(解耦)

eureka-server

pom.xml

<!-- springboot版本和cloud版本最好还是按文档来,已经踩过了不少坑 -->
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.5.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>
  <!-- 服务注册和发现的库 -->
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
</dependencies>
<!-- springcloud version Finchley.SR2 -->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>Finchley.SR2</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>

配置:

server:
  port: 1025
eureka:
  instance:
    # 实例host
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  server:
    # 是否开启自我保护(默认true)|| 是否剔除异常服务
    enable-self-preservation: true
    
logging:
    level:
        com.netflix.eureka: OFF
        com.netflix.discovery: OFF

启动类:

  • 启动日志会有一些,生产环境使用多节点配置,警告消失。
  • 默认情况下,注册表将尝试将其自身注册,需要禁用
  • 本地使用它时,将此注册表放在单独的端口上
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

// use Spring Cloud’s @EnableEurekaServer to standup a registry
@EnableEurekaServer
@SpringBootApplication
public class EurekaServiceApplication {

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

eureka-client

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.5.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <java.version>1.8</java.version>
</properties>

<dependencies>
  <!-- 客户端:eureka-client -->
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  </dependency>

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

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>Finchley.SR2</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>

配置(bootstrap.yml):

默认配置:

  • 服务端口为8080
  • 注册地址为http://localhost:8761/eureka
  • 通常在服务配置的最早阶段使用。此属性用于服务引导程序(bootstrap.yml)
spring:
  application:
    name: eureka-client-example

代码:

  • @EnableDiscoveryClient:开启DiscoveryClient
  • 其他服务注册和发现的实现,同样可以使用@EnableDiscoveryClient
  • 服务
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

// @EnableDiscoveryClient activates the Netflix Eureka DiscoveryClient implementation.
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaClientApplication {

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

@RestController
class ServiceInstanceRestController {

    @Autowired
    private DiscoveryClient discoveryClient;

    /**
     * 返回所有的枚举ServiceInstance在注册表中注册的实
     */
    @RequestMapping("/service-instances/{applicationName}")
    public List<ServiceInstance> serviceInstancesByApplicationName(
            @PathVariable String applicationName) {
        return this.discoveryClient.getInstances(applicationName);
    }
}

测试:

访问:http://127.0.0.1:8080/service-instances/eureka-client-example,得到如下响应

[
    {
        "host":"192.168.0.107",
        "port":8080,
        "secure":false,
        "instanceInfo":{
            "instanceId":"192.168.0.107:eureka-client-example",
            "app":"EUREKA-CLIENT-EXAMPLE",
            "appGroupName":null,
            "ipAddr":"192.168.0.107",
            "sid":"na",
            "homePageUrl":"http://192.168.0.107:8080/",
            "statusPageUrl":"http://192.168.0.107:8080/actuator/info",
            "healthCheckUrl":"http://192.168.0.107:8080/actuator/health",
            "secureHealthCheckUrl":null,
            "vipAddress":"eureka-client-example",
            "secureVipAddress":"eureka-client-example",
            "countryId":1,
            "dataCenterInfo":{
                "@class":"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
                "name":"MyOwn"
            },
            "hostName":"192.168.0.107",
            "status":"UP",
            "overriddenStatus":"UNKNOWN",
            "leaseInfo":{
                "renewalIntervalInSecs":30,
                "durationInSecs":90,
                "registrationTimestamp":1555866795740,
                "lastRenewalTimestamp":1555866945689,
                "evictionTimestamp":0,
                "serviceUpTimestamp":1555866795741
            },
            "isCoordinatingDiscoveryServer":false,
            "metadata":{
                "management.port":"8080",
                "jmx.port":"61062"
            },
            "lastUpdatedTimestamp":1555866795741,
            "lastDirtyTimestamp":1555866795618,
            "actionType":"ADDED",
            "asgName":null
        },
        "serviceId":"EUREKA-CLIENT-EXAMPLE",
        "metadata":{
            "management.port":"8080",
            "jmx.port":"61062"
        },
        "uri":"http://192.168.0.107:8080",
        "scheme":null
    }
]

详细配置

**eureka.instance.*: Eureka:实例注册配置项**

org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean

**eureka.server.*: Eureka:Eureka Server配置项**

org.springframework.cloud.netflix.eureka.EurekaClientConfigBean

**eureka.client.*: Eureka:Eureka Client配置的项**

org.springframework.cloud.netflix.eureka.server.EurekaServerConfigBean

注册相关(eureka-server):

  1. 服务提供者在启动会向Eureka Server注册信息(包括服务名(Eureka Service Id)、service id、ip、port、心跳周期等),EurekaServer获取注册请求,将服务实例信息存入到读写缓存中
  2. 服务消费者根据Eureka service id向Eureka Server获取要访问服务的注册信息,第一次请求会一次性拉取对应Eureka service id的全部服务实例信息
  3. Eureka Server收到请求后,会首先在只读缓存查找,如果找到则直接返回,否则再查找读写缓存,如果找到则将再存入到只读缓存中,然后返回查找结果
  4. 服务提供者获取信息的,将服务实例信息缓存到本地,在使用时根据算法从N个服务中中选取一个服务并向这个服务发送请求
spring:
    application:
        # 本服务注册到注册到服务器的名称, 这个名称就是后面调用服务时的服务标识符,即Eureka Service Id
        name: eureka-server
eureka:
    client:
        # 该实例是否向 Eureka  Server注册自己
        register-with-eureka: false
        # 该实例是否向 Eureka 服务器获取所有的注册信息表
        fetch-registry: true
        # 配置 Eureka-Server 地址
        serviceUrl:
            defaultZone: http://127.0.0.1:8761/eureka/  

续约相关(eureka-client):

  1. 服务提供者会定时向Eureka Server发送心跳,默认30s
  2. Eureka Server收到心跳后,会更新对应的服务实例信息,如果服务的状态有变化则将实例的变化加入到”最近租约变更记录队列”中
  3. Eureka Server有个实例过期清理定时器,如果在指定时间内没有收到心跳(默认90s),则认为服务已经下线,会从读写缓存中移除此实例,将并此变化更新“最近租约变更记录队列”。通常建议将存活的最长时间设置为3个心跳
  4. 服务消费者也有个定时任务会定时去更新服务实例信息(默认30s),第一次全量拉取服务实例,会读取所有的实例信息; 之后使用增量拉取服务实例信息,Eureka Server根据”最近租约变更记录队列”,告诉请求方哪些服务实例有变化,只返回变化的实例信息。客户端根据返回的增量信息更新本地缓存。我们也可以禁用增量获取服务实例实例,每次使用全量获取服务实例信息
  5. 服务下线
    1. 主动下线,在服务实例结束前,主动通知Eureka Server,在默认配置下,此时服务消费者最长在30s左右知道此服务已经下线
    2. 被动下线,服务进度崩溃/网络异常,此时服务消费者最长在(3次心跳+一次刷新频率30s)(共约120s左右)内知道此服务已经下线
eureka:
    client:
        # 是否从 Eureka-Server 拉取服务实例注册信息,默认值为true
        fetch-registry: true
        # 从 Eureka-Server 拉取注册服务实例信息频率,默认:30 秒
        registry-fetch-interval-seconds: 30 
    # 是否禁用增量获取服务实例注册信息
    disableDelta: false
    instance:
        # 心跳,租约续约频率,单位:秒
        lease-renewal-interval-in-seconds: 30
        # eureka server多久没有收到心跳,则表示对应的实例过期,单位:秒
        lease-expiration-duration-in-seconds: : 90s
        # Eureka Server端服务实例租约过期定时任务执行频率
        eviction-interval-timer-in-ms: 60s 

缓存配置(eureka-server):

  1. 读写缓存:每次有服务实例状态变化(如注册服务、下线等)只会更新读写缓存
  2. 只读缓存永不过期,但是可能存在过期数据。此时为了保证只读数据的准确性,会有个定时器定时同步两个缓存,然后将状态变化的服务实例添加到”最近租约变更记录队列”。执行频率默认30s

“最近租约变更记录队列”里的数据也有有效期,默认为180s。

  1. 当有注册、状态变更、下线时,会将创建最近租约变更记录加入此队列中
  2. 用于注册信息增量获取
  3. 后台任务定时顺序扫描队列,当数据最后更新时间超过一定时长后进行移除
eureka:
    server:
        # 是否开启只读请求响应缓存。响应缓存 ( ResponseCache ) 机制目前使用两层缓存策略。
        # 优先读取只读缓存,读取不到后读取固定过期的读写缓存。
        use-read-only-response-cache: true
        # 只读缓存更新频率,单位:毫秒。只读缓存定时更新任务只更新读取过请求 ,
        # 因此虽然永不过期,也会存在读取不到的情况。
        response-cache-update-interval-ms: 30s
        # 读写缓存写入后过期时间,单位:秒。
        response-cache-auto-expiration-in-seconds: 180s
        # 移除队列里过期的租约变更记录的定时任务执行频率,单位:毫秒。默认值 :30 * 1000 毫秒。
        delta-retention-timer-interval-in-ms: 30s
        # 租约变更记录过期时长,单位:毫秒。默认值 : 3 * 60 * 1000 毫秒。
        retention-time-in-m-s-in-delta-queue: 180s

自我保护机制(eureka-server):

  1. Renews threshold: Eureka Server 期望每分钟最少收到服务实例心跳的总数
    1. Renews threshold = 当前注册的服务实例数 x 2 * 自我保护系数( eureka.renewalPercentThreshold )
    2. 之所以 x 2,是因为Eureka 硬编码认为每个实例每 30 秒 1 个心跳,即1分钟2个心跳
  2. Renews (last min): Eureka Server 最近 1 分钟收到服务实例心跳的总数
eureka:
    server:
        # 是否开启自我保护模式。
        enable-self-preservation: false
        # 开启自我保护模式比例,超过该比例后开启自我保护模式。
        # 是否开启自我保护的差别,在于是否执行清理过期租约逻辑。
        # 如果想关闭分批逐步过期,设置 renewalPercentThreshold = 0 。
        renewal-percent-threshold: 0.85
        # 自我保护模式比例更新定时任务执行频率。
        renewal-threshold-update-interval-ms: 900s

集群:

多个Eureka Client进程向注册中心注册的Eureka Service Id相同,则被认为是一个集群

多个Eureka Server相互注册,组成一个集群

为了保证集群里所有Eureka Server节点的状态同步,所有以下操作都会同步到集群的所有服务上:服务注册(Registers)、服务更新(Renewals)、服务取消(Cancels),服务超时(Expirations)和服务状态变更(Status Changes)

  1. syncUp:在Eureka Server重启或新的Eureka Server节点加进来的,会执行初始化,从而能够正常提供服务。当Eureka server启动时,他会从其它节点获取所有的注册信息。如果获取同步失败,它在一定时间(此值由决定)内拒绝服务
  2. replicateToPeers: 复印所有的eureka操作到集群中其他节点,请求再次转发到其它的Eureka Server,调用同样的接口,传入同样的参数,除了会在header中标记isReplication=true,从而避免重复的replicate
  3. register: 注册登录的实例,并且复印此实例的信息到所有的eureka server的节点。如果其它Eureka server调用此节点,只在本节点更新实例信息,避免通知其他节点执行更新
  4. renew:心跳

已有的Eureka Server:在运行过程中,Eureka Server之间会定时同步实例的注册信息。这样即使新的Application Service只向集群中一台注册服务,则经过一段时间会集群中所有的Eureka Server都会有这个实例的信息。那么Eureka Server节点之间如何相互发现,各个节点之间定时(时间由eureka.server.peer-eureka-nodes-update-interval-ms决定)更新节点信息,进行相互发现。

Service Consumer:Service Consumer刚启动时,它会从配置文件读取Eureka Server的地址信息。当集群中新增一个Eureka Server中时,那么Service Provider如何发现这个Eureka Server?Service Consumer会定时(此值由eureka.client.eureka-service-url-poll-interval-seconds决定)调用Eureka Server集群接口,获取所有的Eureka Server信息的并更新本地配置。

eureka:
    server:
      # Eureka-Server 启动时,从远程 Eureka-Server 读取不到注册信息时,多长时间不允许 Eureka-Client 访问
        wait-time-in-ms-when-sync-empty: 5
        # Eureka-Server 集群节点更新频率,单位:毫秒
        eureka.server.peer-eureka-nodes-update-interval-ms:
        # 初始化实例信息到Eureka服务端的间隔时间,单位为秒
        initial-instance-info-replication-interval-seconds: 40
        # 更新实例信息的变化到Eureka服务端的间隔时间,单位为秒
        instance-info-replication-interval-seconds: 30

猜你喜欢

转载自www.cnblogs.com/zuier/p/10748847.html
今日推荐