写BUG的派大星

Patrick Star

  • 首页
  • 归档

  • 搜索
设计模式 Gis Kafka Druid 微信小程序 Java 开源项目源码 物体识别 机器学习 Mybatis 微服务 Feign OpenVPN CSS Streamsets CDH SpringCloud SpringBoot maven 分布式 Shell Tree Linux js WebSocket 多线程 集群 Hadoop 大数据 JDK ElasticSearch MySQL 数据库 Redis Http Nginx

Eureka

发表于 2020-05-18 | 分类于 Java | 0 | 阅读次数 1328

Eureka简介

Eureka采用了CS的设计架构,Server端作为服务注册功能的服务器,它是服务注册中心,而系统中运行的其他微服务则作为Eureka的客户端连接到Server,并且维持心跳,这样维护人员就可以直接通过监控系统来查看微服务是否正常运行。

在服务注册与发现中,有一个注册中心,当微服务客户端启动的时候,会把当前自己服务器的通讯地址等信息以别名的方式发送到注册中心上,其他微服务客户端可以通过别名获取到通讯地址,以此实现RPC。

  • RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务

核心设计思想

框架的核心设计思想在于注册中心,使用注册中心管理每个微服务与微服务间的依赖关系(服务治理概念)。

基本规则

  • 服务启动时会生成服务的基本信息对象InstanceInfo,然后在启动时会register到服务治理中心。
  • 注册完成后会从服务治理中心拉取所有的服务信息,缓存在本地。
  • 之后服务会被30s(可配置)发送一个心跳信息,续约服务。
  • 如果服务治理中心在90s内没有收到一个服务的续约,就会认为服务已经挂了,会把服务注册信息删掉。
  • 服务停止前,服务会主动发送一个停止请求,服务治理中心会删除这个服务的信息。
  • 如果Eureka Server收到的心跳包不足正常值的85%(可配置)就会进入自我保护模式,在这种模式下,Eureka Server不会删除任何服务信息。
  • 两个比较重要的配置
    • 服务过期时间配置:eureka.instance.lease-expiration-duration-in-seconds
    • 服务刷新时间配置:eureka.instance.lease-renewal-interval-in-seconds

缓存机制

在Eureka注册中心有一个Map来保存所有的服务以及机器信息,为了保证性能以及线程安全,采用了ConcurrentHashMap来实现。下面是具体的代码

private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
            = new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();

说明

  1. 服务注册时,会把服务的信息写到这个registry变量中。
  2. 在拉取服务列表信息中,并不会读取registry变量,而是在ResponseCache中拉取。更好地支持高并发。
  3. ResponseCache分为两部分,ReadWriteMap和ReadOnlyMap。
  • ResponseCache
    • ReadWriteMap可以看作是registry的缓存,在服务注册时,除了将信息写到registry中,还会让ReadWriteMap主动过期,达到更新缓存的目的。
    • ReadOnlyMap可以看作是ReadWriteMap的缓存,当需要获取服务列表的时候,会直接取ReadOnlyMap,只有数据不存在的情况才会从ReadWriteMap中更新。
    • 由于在服务注册时会让ReadWriteMap主动过期,因此可以将ReadWriteMap和registry看作是实时同步的,但是由于ReadOnlyMap少了主动过期的操作,所以它和ReadWriteMap之间可能会存在延迟更新。
    • eureka.server.responseCacheUpdateInvervalMs这个配置可以设置从ReadWriteMap同步到ReadOnlyMap的时间。
    • eureka.server.eviction-interval-timer-in-ms这个配置可以设置Eureka Server内部定时扫描的时间间隔,用以扫描出registry中的过期实例并删除。

服务注册

客户端

服务在启动完成后,会启动一个线程,去执行注册信息,具体代码在InstanceInfoReplicator中,这个类实现了Runnable:

public void run() {
        try {
            discoveryClient.refreshInstanceInfo();

            Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
            if (dirtyTimestamp != null) {
                discoveryClient.register();
                instanceInfo.unsetIsDirty(dirtyTimestamp);
            }
        } catch (Throwable t) {
            logger.warn("There was a problem with the instance info replicator", t);
        } finally {
            Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
            scheduledPeriodicRef.set(next);
        }
    }

会先去检测服务信息是否发生了变更,如果发生了,会调用 discoveryClient.register();方法重新注册,第一次的时候也会认为是发生了变更,从而发起第一次注册。

在finally块中指定了多少时间间隔后再次执行这个方法。

服务端

首先处理上面提到的registry和ReadWriteMap,然后将这次注册信息添加到一个"最近变更序列"中,这个队列的存在是为了方便服务增量更新服务列表的信息。

在这些工作做完之后调用其他的Eureka节点的register方法,将注册信息发送给他们。

服务续约

服务启动后,会创建一个后台的任务,定时进行续约(维持心跳),代码如下

scheduler.schedule(
                    new TimedSupervisorTask(
                            "heartbeat",
                            scheduler,
                            heartbeatExecutor,
                            renewalIntervalInSecs,
                            TimeUnit.SECONDS,
                            expBackOffBound,
                            new HeartbeatThread()
                    ),
                    renewalIntervalInSecs, TimeUnit.SECONDS);
  • scheduler.schedule方法只会在延时一定时间后执行一次提交的任务,所以这里会延时执行,同时,虽然schedule只会执行一次,但是TimedSupervisorTask里封装了循环调用的信息,所以其实是定时调用了。

具体的心跳线程(HeartbeatThread)的实现也很简单,想服务注册中心发送一个Http请求,如果返回404,就认为这个服务没有注册或者注册了但是已经失效。此时会调用register()方法进行注册。

服务取消

在服务停止之前,客户端会向服务注册中心发送一个服务取消的请求,用于注销掉该微服务。操作如下:

  1. 从register中删除对应的服务信息。
  2. 使ReadWriteMap主动过期(缓存失效)。
  3. 将服务取消的信息加入到最近变更队列中。
  • 本文作者: Patrick
  • 本文链接: https://www.write1bug.cn/archives/eureka
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# 设计模式 # Gis # Kafka # Druid # 微信小程序 # Java # 开源项目源码 # 物体识别 # 机器学习 # Mybatis # 微服务 # Feign # OpenVPN # CSS # Streamsets # CDH # SpringCloud # SpringBoot # maven # 分布式 # Shell # Tree # Linux # js # WebSocket # 多线程 # 集群 # Hadoop # 大数据 # JDK # ElasticSearch # MySQL # 数据库 # Redis # Http # Nginx
SpringBoot中@Value无法赋值给静态变量
idea中maven项目出现"Cannot resolve jdk.tools:jdk.tools:1.7"
  • 文章目录
  • 站点概览
Patrick

Patrick

不是在改BUG,就是在写BUG。

52 日志
9 分类
36 标签
RSS
E-mail
Creative Commons
© 2018 — 2023 Patrick
人生如逆旅|我亦是行人
鲁ICP备18043140号-1