• Eureka源码2-Eureka预备知识和核心类


    1. 前言

    1.1 Eureka的异地多活

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

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

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

    1.2 Region和Zone

    Eureka中具有RegionAvailability 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-实例信息类

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

    2.1.2 Application

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

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

    2.1.3 Applications

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

    1. public class Applications {
    2. private static class VipIndexSupport {
    3. final AbstractQueue<InstanceInfo> instances = new ConcurrentLinkedQueue<>();
    4. final AtomicLong roundRobinIndex = new AtomicLong(0);
    5. final AtomicReference<List<InstanceInfo>> vipList = new AtomicReference>(Collections.emptyList());
    6. public AtomicLong getRoundRobinIndex() {
    7. return roundRobinIndex;
    8. }
    9. public AtomicReference<List<InstanceInfo>> getVipList() {
    10. return vipList;
    11. }
    12. }
    13. private static final String STATUS_DELIMITER = "_";
    14. private String appsHashCode;
    15. private Long versionDelta;
    16. @XStreamImplicit
    17. private final AbstractQueue<Application> applications;
    18. // key:微服务名称 value:Application实例
    19. private final Map<String, Application> appNameApplicationMap;
    20. private final Map<String, VipIndexSupport> virtualHostNameAppMap;
    21. private final Map<String, VipIndexSupport> secureVirtualHostNameAppMap;
    22. }

    2.2 服务端

    2.2.1 AbstractInstanceRegistry

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

    1. public abstract class AbstractInstanceRegistry implements InstanceRegistry {
    2. // ......
    3. // 服务实例租约信息
    4. private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
    5. = new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();
    6. // 覆盖状态 map
    7. protected final ConcurrentMap<String, InstanceStatus> overriddenInstanceStatusMap = CacheBuilder
    8. .newBuilder().initialCapacity(500)
    9. .expireAfterAccess(1, TimeUnit.HOURS)
    10. .<String, InstanceStatus>build().asMap();
    11. // 最近注册队列,实例注册到服务端时添加
    12. // 先进先出队列,满1000时移除最先添加的
    13. private final CircularQueue<Pair<Long, String>> recentRegisteredQueue;
    14. // 最近下架队列,实例从服务端下架时添加
    15. // 先进先出队列,满1000时移除最先添加的
    16. private final CircularQueue<Pair<Long, String>> recentCanceledQueue;
    17. // 最近变更队列
    18. // 有定时任务维护的队列,每30s执行一次,移除添加进该队列超过3分钟的实例变更信息
    19. private ConcurrentLinkedQueue<RecentlyChangedItem> recentlyChangedQueue = new ConcurrentLinkedQueue<RecentlyChangedItem>();
    20. private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    21. // 读锁(处理客户端注册、下架、状态变更、删除状态时使用)
    22. private final Lock read = readWriteLock.readLock();
    23. // 写锁(处理客户端拉取增量注册表时使用)
    24. private final Lock write = readWriteLock.writeLock();
    25. // 服务端统计最近一分钟预期收到客户端实例心跳续租的请求数
    26. protected volatile int numberOfRenewsPerMinThreshold;
    27. // 服务端统计预期收到心跳续租的客户端实例数
    28. protected volatile int expectedNumberOfClientsSendingRenews;
    29. // 响应缓存
    30. // 服务端处理客户端拉取注册表请求时使用
    31. protected volatile ResponseCache responseCache;
    32. // ......
    33. // 处理注册
    34. public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {...}
    35. // 处理下架
    36. public boolean cancel(String appName, String id, boolean isReplication) {...}
    37. // 具体下架处理
    38. protected boolean internalCancel(String appName, String id, boolean isReplication) {...}
    39. // 处理心跳续租
    40. public boolean renew(String appName, String id, boolean isReplication) {...}
    41. // 处理变更状态
    42. public boolean statusUpdate(String appName, String id,
    43. InstanceStatus newStatus, String lastDirtyTimestamp,
    44. boolean isReplication) {...}
    45. // 处理删除状态
    46. public boolean deleteStatusOverride(String appName, String id,
    47. InstanceStatus newStatus,
    48. String lastDirtyTimestamp,
    49. boolean isReplication) {...}
    50. // 处理实例过期清理
    51. public void evict(long additionalLeaseMs) {...}
    52. // 处理拉取全量注册表(本地全量注册表 + 可能包含全部远程 region 注册表)
    53. public Application getApplication(String appName, boolean includeRemoteRegion) {...}
    54. // 处理拉取全量注册表(本地全量注册表 + 可能包含指定远程 region 全量注册表)
    55. public Applications getApplicationsFromMultipleRegions(String[] remoteRegions) {...}
    56. // 处理拉取增量注册表(本地增量注册表 + 可能包含指定远程 region 增量注册表)
    57. public Applications getApplicationDeltasFromMultipleRegions(String[] remoteRegions) {...}
    58. ......
    59. }
    • Lease-只有服务端维护的实例租约信息类
    1. public class Lease<T> {
    2. // 实例下架时间
    3. private long evictionTimestamp;
    4. // 实例注册时间
    5. private long registrationTimestamp;
    6. // 实例启动时间
    7. private long serviceUpTimestamp;
    8. // 实例租约过期时间
    9. private volatile long lastUpdateTimestamp;
    10. ......
    11. }
    • ResponseCacheImpl:响应缓存实现类
    1. public class ResponseCacheImpl implements ResponseCache {
    2. // ......
    3. // 只读缓存
    4. private final ConcurrentMap<Key, Value> readOnlyCacheMap = new ConcurrentHashMap<Key, Value>();
    5. // 读写缓存
    6. // LoadingCache:Guava 提供的本地缓存,多线程的场景下保证只有一个线程加载相应缓存项
    7. private final LoadingCache<Key, Value> readWriteCacheMap;
    8. // 判断是否使用只读缓存
    9. private final boolean shouldUseReadOnlyResponseCache;
    10. // ......
    11. }

    2.2.2 PeerAwareInstanceRegistryImpl

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

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

    3. Jersey通信框架

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

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

    1. @Produces({"application/xml", "application/json"})
    2. public class InstanceResource {
    3. private static final Logger logger = LoggerFactory
    4. .getLogger(InstanceResource.class);
    5. private final PeerAwareInstanceRegistry registry;
    6. private final EurekaServerConfig serverConfig;
    7. private final String id;
    8. private final ApplicationResource app;
    9. InstanceResource(ApplicationResource app, String id, EurekaServerConfig serverConfig, PeerAwareInstanceRegistry registry) {
    10. this.app = app;
    11. this.id = id;
    12. this.serverConfig = serverConfig;
    13. this.registry = registry;
    14. }
    15. ...
    16. }
  • 相关阅读:
    IPsec协议
    Django使用SMTP发送邮件教程
    openwrt 断网重启检测脚本
    Vue 商场首页头部布局
    深入理解Java NIO:原理、应用与实战详解
    【数据结构与算法】——第五章:树与二叉树(2)
    低代码开发——进最热的赛道,啃最硬的骨头
    数据分析必备的5个工具,你用过几个?
    使用运放产生各种波形
    渗透测试 | 域名信息收集
  • 原文地址:https://blog.csdn.net/LBWNB_Java/article/details/127439229