Eureka源码核心类预备知识

2022-10-15 12:11:14

目录1.前言1.1Eureka的异地多活1.2Region和Zone1.3Region和AZ需求2.核心类2.1客户端核心类2.1.1InstanceInfo-实例信息类2.1.2App...

目录
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源码核心类预备知识

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有亚太东南1www.cppcns.com区 (新加坡)、亚太东南2区(悉尼)、亚太东北1区(东京)等

1.3 Region和AZ需求

Eureka源码核心类预备知识

假设某公司的服务器有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

服务端具体处理客户端请求http://www.cppcns.com(心跳续租、注册、变更状态等等)的类

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, boophplean 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源码核心类的资料请关注我们其它相关文章!