Eureka源码核心类预备知识

目录
  • 1. 前言
    • 1.1 Eureka的异地多活
    • 1.2 Region和Zone
    • 1.3 Region和AZ需求
  • 2.核心类
    • 2.1 客户端核心类
      • 2.1.1 InstanceInfo-实例信息类
      • 2.1.2 Application
      • 2.1.3 Applications
    • 2.2 服务端
      • 2.2.1 AbstractInstanceRegistry
      • 2.2.2 PeerAwareInstanceRegistryImpl
  • 3. Jersey通信框架

1. 前言

1.1 Eureka的异地多活

异地多活一般是指在不同城市建立独立的数据中心。

活是相对于主备关系中的热备而言的。热备是指备份机房随时全量备份着主机房中的数据,但平时不 支撑业务需求,即不对外提供服务。只有在主机房出现故障时才会切换到备份机房,由备份机房对外提 供服务。也就是说,平时只有主机房是活的。

多活则是指这些机房间属于主从关系,即这些机房平时都支撑业务需求,都对外提供服务,相互备 份。

1.2 Region和Zone

Eureka中具有Region与Availability Zone(简称AZ)概念,都是云计算中的概念。

为了方便不同地理区域中用户的使用,大型云服务提供商一般会根据用户需求量在不同的城市、省份、 国家或洲创建不同的大型云计算机房。这些不同区域机房间一般是不能“内网连通”的。这些区域就称为 一个Region。

这里存在一个问题:同一Region机房是如何实现同域容灾的?为了增强容灾能力,在一个Region中又 设置了不同的Availability Zone。这些AZ间实现了内网连通,且用户可以根据自己所在的具体的位置自动 选择同域中的不同AZ。当用户所要访问的AZ出现问题后,系统会自动切换到其它可用的AZ。

例如,AWS将全球划分为了很多的Region,例如美国东部区、美国西部区、欧洲区、非洲开普敦区、 亚太区等。像Eureka系统架构图中的us-east-1c、us-east-1d、us-east-1e就是us-east-1这个Region中 的c、d、e三个AZ。

再如,阿里云在我国境内的Region有杭州、北京、深圳、青岛、香港等,境外Region有亚太东南1区 (新加坡)、亚太东南2区(悉尼)、亚太东北1区(东京)等

1.3 Region和AZ需求

假设某公司的服务器有Beijing、Shanghai等多个Region。Beijing这个Region中存在两个AZ,分别是 bj-1与bj-2,每个AZ中有三台Eureka Server。

h-1与h-2两台主机提供的都是相同的Service服务,根据地理位置的不同,这两台主机分别注册到了距 离自己最近的不同AZ的Eureka Server。

2.核心类

2.1 客户端核心类

2.1.1 InstanceInfo-实例信息类

// 客户端中,表示自身实例信息
// 服务端中,表示实例存在服务端注册表中的信息
public class InstanceInfo {
    // ......
    // 客户端中,表示自己的真实工作状态
    // 服务端中,表示服务发现时实例想要暴露给其他实例的工作状态,不一定是实例的真实工作状态
    private volatile InstanceStatus status = InstanceStatus.UP;
    // 覆盖状态,服务端可以根据一定规则匹配出 status
    // 外界修改实例在服务端中状态(比如通过 actuator 修改状态)就是修改覆盖状态
    private volatile InstanceStatus overriddenStatus = InstanceStatus.UNKNOWN;
    // 判断实例信息在服务端中是否是脏的
    private volatile boolean isInstanceInfoDirty = false;
    // 租约信息
    private volatile LeaseInfo leaseInfo;
    // 记录实例信息在服务端最近一次修改的时间
    private volatile Long lastUpdatedTimestamp;
    // 记录实例信息在客户端最近一次修改的时间
    private volatile Long lastDirtyTimestamp;
    // ......
}
  • InstanceStatus-实例状态类
public enum InstanceStatus {
    UP, // 启动状态,表示实例对外正常提供服务
    DOWN, // 下线状态,实例健康检查失败时修改为该状态
    STARTING, // 启动中状态,表示实例正在初始化启动中
    OUT_OF_SERVICE, // 停止服务状态,表示实例不对外提供服务
    UNKNOWN; // 未知状态
    // ......
}
  • LeaseInfo 租约信息类
public class LeaseInfo {
    public static final int DEFAULT_LEASE_RENEWAL_INTERVAL = 30;
    public static final int DEFAULT_LEASE_DURATION = 90;
    // 客户端维护的心跳间隔时间
    private int renewalIntervalInSecs = DEFAULT_LEASE_RENEWAL_INTERVAL;
    // 客户端维护的租约持续时间
    private int durationInSecs = DEFAULT_LEASE_DURATION;
    // 服务端维护的实例注册时间
    private long registrationTimestamp;
    // 服务端维护的实例最近一次更新时间
    private long lastRenewalTimestamp;
    // 服务端维护的实例过期清理时间
    private long evictionTimestamp;
    // 服务端维护的实例启动时间
    private long serviceUpTimestamp;
    // ......
}

2.1.2 Application

一个Application实例保存着一个特定微服务的所有提供者实例

public class Application {
    private static Random shuffleRandom = new Random();
    private String name;
    @XStreamOmitField
    private volatile boolean isDirty = false;
    /**
     * 保存着当前name所指定的微服务名称的所有InstanceInfo 实例
     */
    @XStreamImplicit
    private final Set<InstanceInfo> instances;
    private final AtomicReference<List<InstanceInfo>> shuffledInstances;
    // key:instanceId  value:InstanceInfo实例
    private final Map<String, InstanceInfo> instancesMap;

2.1.3 Applications

该类封装了来自于Eureka Server的所有注册信息,我们可成为 "客户端注册表"

public class Applications {
    private static class VipIndexSupport {
        final AbstractQueue<InstanceInfo> instances = new ConcurrentLinkedQueue<>();
        final AtomicLong roundRobinIndex = new AtomicLong(0);
        final AtomicReference<List<InstanceInfo>> vipList = new AtomicReference<List<InstanceInfo>>(Collections.emptyList());
        public AtomicLong getRoundRobinIndex() {
            return roundRobinIndex;
        }
        public AtomicReference<List<InstanceInfo>> getVipList() {
            return vipList;
        }
    }
    private static final String STATUS_DELIMITER = "_";
    private String appsHashCode;
    private Long versionDelta;
    @XStreamImplicit
    private final AbstractQueue<Application> applications;
    // key:微服务名称 value:Application实例
    private final Map<String, Application> appNameApplicationMap;
    private final Map<String, VipIndexSupport> virtualHostNameAppMap;
    private final Map<String, VipIndexSupport> secureVirtualHostNameAppMap;
}

2.2 服务端

2.2.1 AbstractInstanceRegistry

服务端具体处理客户端请求(心跳续租、注册、变更状态等等)的类

public abstract class AbstractInstanceRegistry implements InstanceRegistry {
    // ......
    // 服务实例租约信息
    private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
            = new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();
    // 覆盖状态 map
    protected final ConcurrentMap<String, InstanceStatus> overriddenInstanceStatusMap = CacheBuilder
            .newBuilder().initialCapacity(500)
            .expireAfterAccess(1, TimeUnit.HOURS)
            .<String, InstanceStatus>build().asMap();
    // 最近注册队列,实例注册到服务端时添加
    // 先进先出队列,满1000时移除最先添加的
    private final CircularQueue<Pair<Long, String>> recentRegisteredQueue;
    // 最近下架队列,实例从服务端下架时添加
    // 先进先出队列,满1000时移除最先添加的
    private final CircularQueue<Pair<Long, String>> recentCanceledQueue;
    // 最近变更队列
    // 有定时任务维护的队列,每30s执行一次,移除添加进该队列超过3分钟的实例变更信息
    private ConcurrentLinkedQueue<RecentlyChangedItem> recentlyChangedQueue = new ConcurrentLinkedQueue<RecentlyChangedItem>();
    private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    // 读锁(处理客户端注册、下架、状态变更、删除状态时使用)
    private final Lock read = readWriteLock.readLock();
    // 写锁(处理客户端拉取增量注册表时使用)
    private final Lock write = readWriteLock.writeLock();
    // 服务端统计最近一分钟预期收到客户端实例心跳续租的请求数
    protected volatile int numberOfRenewsPerMinThreshold;
    // 服务端统计预期收到心跳续租的客户端实例数
    protected volatile int expectedNumberOfClientsSendingRenews;
    // 响应缓存
    // 服务端处理客户端拉取注册表请求时使用
    protected volatile ResponseCache responseCache;
    // ......
    // 处理注册
    public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {...}
    // 处理下架
    public boolean cancel(String appName, String id, boolean isReplication) {...}
    // 具体下架处理
    protected boolean internalCancel(String appName, String id, boolean isReplication) {...}
    // 处理心跳续租
    public boolean renew(String appName, String id, boolean isReplication) {...}
    // 处理变更状态
    public boolean statusUpdate(String appName, String id,
                                InstanceStatus newStatus, String lastDirtyTimestamp,
                                boolean isReplication) {...}
    // 处理删除状态
    public boolean deleteStatusOverride(String appName, String id,
                                        InstanceStatus newStatus,
                                        String lastDirtyTimestamp,
                                        boolean isReplication) {...}
    // 处理实例过期清理
    public void evict(long additionalLeaseMs) {...}
    // 处理拉取全量注册表(本地全量注册表 + 可能包含全部远程 region 注册表)
    public Application getApplication(String appName, boolean includeRemoteRegion) {...}
    // 处理拉取全量注册表(本地全量注册表 + 可能包含指定远程 region 全量注册表)
    public Applications getApplicationsFromMultipleRegions(String[] remoteRegions) {...}
    // 处理拉取增量注册表(本地增量注册表 + 可能包含指定远程 region 增量注册表)
    public Applications getApplicationDeltasFromMultipleRegions(String[] remoteRegions) {...}
    ......
}
  • Lease-只有服务端维护的实例租约信息类
public class Lease&lt;T&gt; {
    // 实例下架时间
    private long evictionTimestamp;
    // 实例注册时间
    private long registrationTimestamp;
    // 实例启动时间
    private long serviceUpTimestamp;
    // 实例租约过期时间
    private volatile long lastUpdateTimestamp;
    ......
}
  • ResponseCacheImpl:响应缓存实现类
public class ResponseCacheImpl implements ResponseCache {
    // ......
    // 只读缓存
    private final ConcurrentMap<Key, Value> readOnlyCacheMap = new ConcurrentHashMap<Key, Value>();
    // 读写缓存
    // LoadingCache:Guava 提供的本地缓存,多线程的场景下保证只有一个线程加载相应缓存项
    private final LoadingCache<Key, Value> readWriteCacheMap;
    // 判断是否使用只读缓存
    private final boolean shouldUseReadOnlyResponseCache;
    // ......
}

2.2.2 PeerAwareInstanceRegistryImpl

处理集群节点间相关操作的实现类

public class PeerAwareInstanceRegistryImpl extends AbstractInstanceRegistry implements PeerAwareInstanceRegistry {
    // 当前服务端节点的启动时间
    private long startupTime = 0;
    // 判断服务端启动时同步集群节点注册表的实例数是否为空
    private boolean peerInstancesTransferEmptyOnStartup = true;
    // 最近一分钟同步复制给集群节点的次数
    private final MeasuredRate numberOfReplicationsLastMin;
    // 服务端的相邻集群节点,配置文件中配置
    protected volatile PeerEurekaNodes peerEurekaNodes;
}

3. Jersey通信框架

Spring Cloud中Eureka Client与Eureka Server的通信,及Eureka Server间的通信,均采用的是Jersey框架。

Jersey框架是一个开源的RESTful框架,实现了JAX-RS规范。该框架的作用与SpringMVC是相同的,其 也是用户提交URI后,在处理器中进行路由匹配,路由到指定的后台业务。这个路由功能同样也是通过 处理器完成的,只不过这里的处理器不叫Controller,而叫Resource。

@Produces({"application/xml", "application/json"})
public class InstanceResource {
    private static final Logger logger = LoggerFactory
            .getLogger(InstanceResource.class);
    private final PeerAwareInstanceRegistry registry;
    private final EurekaServerConfig serverConfig;
    private final String id;
    private final ApplicationResource app;
    InstanceResource(ApplicationResource app, String id, EurekaServerConfig serverConfig, PeerAwareInstanceRegistry registry) {
        this.app = app;
        this.id = id;
        this.serverConfig = serverConfig;
        this.registry = registry;
    }
    ...
}

参考文章

eureka-0.10.11源码(注释)

springcloud-source-study学习github地址

以上就是Eureka源码核心类预备知识的详细内容,更多关于Eureka源码核心类的资料请关注我们其它相关文章!

(0)

相关推荐

  • FeignClient如何脱离eureka自定义URL

    目录 FeignClient脱离eureka自定义URL 需求 方法一: FeignClient注解中添加URL属性 方法二: eureka禁用ribbon,并手动配置服务列表 feign不使用eureka main server端代码 Client端代码 FeignClient脱离eureka自定义URL 需求 Spring Cloud环境中的FeignClient有时候需要调用特定主机的接口,但是默认是从eureka上的注册列表自动获取到的负载均衡过后的URL,这时怎么办呢? 方法一: Fe

  • spring-cloud入门之eureka-client(服务注册)

    前言 上一节我们搭建起了服务注册中心,为各个服务提供者和消费者提供一个桥梁,这一节我们搭建一个服务提供者,注册到注册中心 开源地址:https://github.com/bigbeef 新建eureka-client模块 代码结构如下: 代码编写 cppba-spring-cloud-eureka-client > pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns=&q

  • Eureka源码阅读之环境搭建及工程结构

    目录 1. 源码阅读环境搭建 1.1 源码下载: 2. 工程结构速览 3. 调试须知 1. 源码阅读环境搭建 ide:IntelliJ IDEA 2020.1 包管理:gradle eureka版本:1.10.11 Spring Cloud : 2020.0.2 Spring Boot :2.4.4 1.1 源码下载: 下载完源码之后,需要更改一下几个地方: build.gradle增加阿里云镜像仓库,将如下插件版本改一下,否则导入idea会报错: maven { url 'https://ma

  • Eureka源码阅读Client启动入口注册续约及定时任务

    目录 引言 1.环境 2. Spring Cloud整合Eureka Client 启动入口 2.1 封装配置文件的类 2.1.1 EurekaClientConfigBean 2.1.2 EurekaInstanceConfigBean 2.2 EurekaClient 2.2.1 ApplicationInfoManager 2.2.2 EurekaClient 2.3 小结 3. DiscoveryClient类的解析 3.1 DiscoveryClient 作用 3.2 Discover

  • SpringCloud添加客户端Eureka Client过程解析

    1.添加依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> 2.添加客户端配置 /src/main/java/resource/application.yml server: port: 8770 eur

  • springcloud client指定注册到eureka的ip与端口号方式

    client指定注册到eureka的ip与端口号 #设置使用IP eureka.instance.prefer-ip-address=true #设置外网IP号 eureka.instance.ip-address=49.10.22.106 #设置外网端口号 eureka.instance.non-secure-port=5565 eureka注册时使用ip而不是hostname问题 eureka的client注册到server时默认是使用hostname而不是ip,这就导致client在多台机

  • Eureka源码核心类预备知识

    目录 1. 前言 1.1 Eureka的异地多活 1.2 Region和Zone 1.3 Region和AZ需求 2.核心类 2.1 客户端核心类 2.1.1 InstanceInfo-实例信息类 2.1.2 Application 2.1.3 Applications 2.2 服务端 2.2.1 AbstractInstanceRegistry 2.2.2 PeerAwareInstanceRegistryImpl 3. Jersey通信框架 1. 前言 1.1 Eureka的异地多活 异地多

  • Eureka源码阅读解析Server服务端启动流程实例

    目录 环境 1.spring cloud整合eureka server demo 1.1 新建spring boot项目 pom.xml文件添加 配置文件 1.2 启动类 1.3 启动 2. spring cloud自动装配eureka server源码解析 2.1 @EnableEurekaServer注解 2.2 EurekaServerAutoConfiguration 2.2.1 查找starter 自动装配类的技巧 2.2.2 EurekaServerAutoConfiguration

  • Eureka源码解析服务离线状态变更

    目录 环境 1. 服务离线的方式 1.1 基于Actuator监控器实现 1.2 直接向Eureka Server提交请求 1.3 特殊状态CANCEL_OVERRIDE 2. 服务下架源码 2.1 cancelScheduledTasks() 2.2 unregister() 3. 服务下线源码分析(状态变更) 3.1 变更状态 3.2 获取状态 环境 eureka版本:1.10.11 Spring Cloud : 2020.0.2 Spring Boot :2.4.4测试代码:github.

  • Spring源码解析之BeanPostProcessor知识总结

    一.简介 BeanPostProcessor是Spring IOC容器给我们提供的一个扩展接口. 实例化Bean做前置处理.后置处理 二.接口定义 @Component public class BeanPost implements BeanPostProcessor { /** * 在每个bean创建之后的初始化方法之前调用 * @param bean 当前实例化的bean * @param beanName bean的名称 * @return 返回实例化的bean或者可以对对象进行再封装返

  • Idea导入eureka源码实现过程解析

    通过GitHub获取Eureka源码 进入git bash命令行,自己找个目录,用来存放eureka源码,然后在目录里面,执行git clone  https://github.com/Netflix/eureka.git,就可以了,这个是需要点时间的,稍微有点慢,你等一会儿好了. git clone https://github.com/Netflix/eureka.git 获取eureka项目依赖 然后在eureka目录中,直接双击gradlew.bat就可以,这个是人家给你提供的命令,直接

  • JDK源码Enum类原理及代码实例解析

    正文 一 概述 枚举类型是 JDK 5 之后引进的一种非常重要的引用类型,可以用来定义一系列枚举常量,使用 enum 来表示枚举可以更好地保证程序的类型安全和可读性 实际上在使用关键字enum创建枚举类型并编译后,编译器会为我们生成一个相关的类,这个类继承了Java API中的java.lang.Enum类, 也就是说通过关键字enum创建枚举类型在编译后事实上也是一个类类型而且该类继承自java.lang.Enum类 使用举例 public class EnumTest { enum MyCo

  • vue parseHTML函数源码解析 AST预备知识

    目录 正文 createASTElement函数 解析指令所用正则 parse 函数中的变量 正文 接上章节:parseHTML 函数源码解析AST 基本形成 在正式扎进Vue parse源码之前,我们先了解下他周边的工具函数, 这能帮我们快速的去理解阅读. 还记得我们在上章节讲的element元素节点的描述对象吗? var element = { type: 1, tag: tag, parent: null, attrsList: attrs, children: [] } 在源码中定义了一

  • Java CountDownLatch的源码硬核解析

    目录 前言 介绍和使用 例子 概述 实现思路 源码解析 类结构图 await() 实现原理 countDown()实现原理 前言 对于并发执行,Java中的CountDownLatch是一个重要的类,简单理解, CountDownLatch中count down是倒数的意思,latch则是“门闩”的含义.在数量倒数到0的时候,打开“门闩”, 一起走,否则都等待在“门闩”的地方. 为了更好的理解CountDownLatch这个类,本文通过例子和源码带领大家深入解析这个类的原理. 介绍和使用 例子

随机推荐