注册中心 Eureka 源码解析 —— 应用实例注册发现(七)之增量获取

服务注册与发现组件 Eureka 客户端实现原理解析 在前面的文章介绍了,如何使用服务注册发现组件: Eureka,并给出使用示例。本文在此基础上,将会讲解 Eureka 客户端实现的内幕,结合源码深入实现的细节,知其所以然。客户端需要重点关注以下几点: 从Eureka Server中拉取注册表信息 全量拉取注册表信息 增量式拉取注册表信息 注册表缓存刷新定时器与续租(心跳)定时器 服务注册与服务按需注册 服务实例的下线 本文摘录于笔者出版的书籍... 阅读详情

点击上方“芋道源码”,选择“置顶公众号”

技术文章第一时间送达!

源码精品专栏

 

本文主要基于 Eureka 1.8.X 版本

  • 1. 概述

  • 2. 应用集合一致性哈希码

  • 2.1 计算公式

  • 2.2 合理性

  • 3. Eureka-Client 发起增量获取

  • 3.1 合并应用集合

  • 4. Eureka-Server 接收全量获取

  • 3.1 接收全量获取请求

  • 3.2 最近租约变更记录队列

  • 3.3 缓存读取

  • 666. 彩蛋


1. 概述

本文主要分享 Eureka-Client 向 Eureka-Server 获取增量注册信息的过程。

前置阅读:《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》

FROM 《深度剖析服务发现组件Netflix Eureka》 

Eureka-Client 获取注册信息,分成全量获取和增量获取。默认配置下,Eureka-Client 启动时,首先执行一次全量获取进行本地缓存注册信息,而后每 30 秒增量获取刷新本地缓存( 非“正常”情况下会是全量获取 )。

本文重点在于增量获取。

推荐 Spring Cloud 书籍:

  • 请支持正版。下载盗版,等于主动编写低级 BUG 。

  • 程序猿DD —— 《Spring Cloud微服务实战》

  • 周立 —— 《Spring Cloud与Docker微服务架构实战》

  • 两书齐买,京东包邮。

推荐 Spring Cloud 视频:

  • Java 微服务实践 - Spring Boot

  • Java 微服务实践 - Spring Cloud

  • Java 微服务实践 - Spring Boot / Spring Cloud

2. 应用集合一致性哈希码

Applications.appsHashCode ,应用集合一致性哈希码。

增量获取注册的应用集合( Applications ) 时,Eureka-Client 会获取到:

  1. Eureka-Server 近期变化( 注册、下线 )的应用集合

  2. Eureka-Server 应用集合一致性哈希码

Eureka-Client 将变化的应用集合和本地缓存的应用集合进行合并后进行计算本地的应用集合一致性哈希码。若两个哈希码相等,意味着增量获取成功;若不相等,意味着增量获取失败,Eureka-Client 重新和 Eureka-Server 全量获取应用集合。

Eureka 比较应用集合一致性哈希码,和日常我们通过哈希码比较两个对象是否相等类似。

2.1 计算公式

appsHashCode = ${status}_${count}_

  • 使用每个应用实例状态( status ) + 数量( count )拼接出一致性哈希码。若数量为 0 ,该应用实例状态不进行拼接。状态以字符串大小排序。

  • 举个例子,8 个 UP ,0 个 DOWN ,则 appsHashCode = UP_8_ 。8 个 UP ,2 个 DOWN ,则 appsHashCode = DOWN_2_UP_8_ 。

  • 实现代码如下:

    // Applications.java
    public String getReconcileHashCode() {
      // 计数集合 key:应用实例状态
      TreeMap<String, AtomicInteger> instanceCountMap = new TreeMap<String, AtomicInteger>();
      populateInstanceCountMap(instanceCountMap);
      // 计算 hashcode
      return getReconcileHashCode(instanceCountMap);
    }
    • 计数那块代码,使用 Integer 即可,无需使用 AtomicInteger 。

    • 调用 #populateInstanceCountMap() 方法,计算每个应用实例状态的数量。实现代码如下:

      // Applications.java
      public void populateInstanceCountMap(Map<String, AtomicInteger> instanceCountMap) {
        for (Application app : this.getRegisteredApplications()) {
            for (InstanceInfo info : app.getInstancesAsIsFromEureka()) {
                // 计数
                AtomicInteger instanceCount = instanceCountMap.computeIfAbsent(info.getStatus().name(),
                        k -> new AtomicInteger(0));
                instanceCount.incrementAndGet();
            }
        }
      }

      public List<Application> getRegisteredApplications() {
        return new ArrayList<Application>(this.applications);
      }

      // Applications.java
      public List<InstanceInfo> getInstancesAsIsFromEureka() {
        synchronized (instances) {
           return new ArrayList<InstanceInfo>(this.instances);
        }
      }
  • 调用 #getReconcileHashCode() 方法,计算 hashcode 。实现代码如下:

    public static String getReconcileHashCode(Map<String, AtomicInteger> instanceCountMap) {
      StringBuilder reconcileHashCode = new StringBuilder(75);
      for (Map.Entry<String, AtomicInteger> mapEntry : instanceCountMap.entrySet()) {
          reconcileHashCode.append(mapEntry.getKey()).append(STATUS_DELIMITER) // status
                  .append(mapEntry.getValue().get()).append(STATUS_DELIMITER); // count
      }
      return reconcileHashCode.toString();
    }

2.2 合理性

本小节,建议你理解完全文后,再回到此处 
本小节,建议你理解完全文后,再回到此处 
本小节,建议你理解完全文后,再回到此处

笔者刚看完应用集合一致性哈希算法的计算公式,处于一脸懵逼的状态。这么精简的方式真的能够校验出数据的一致性么?不晓得有多少读者跟笔者有一样的疑惑。下面我们来论证该算法的合理性( 一本正经的胡说八道 )。

一致性哈希值通过状态 + 数量来计算,那么是不是可能状态总数是一样多,实际分布在不同的应用?那么我们列举模型如下:


UP
应用Am
应用Bn

如果此时应用A 下线了 c 个原应用实例,应用B 注册了 c 个信应用实例,那么处于 UP 状态的数量仍然是 m + n 个。

  • 正常情况下,Eureka-Client 从 Eureka-Server 获取到完整的增量变化并合并,此时应用情况如下表格所示,两者是一致的,一致性哈希算法合理。


UP (server)UP (client)
应用Am - cm - c
应用Bn + cn + c
  • 异常情况下【1】,变更记录队列全部过期。那 Eureka-Client 从 Eureka-Server 获取到空的增量变化并合并,此时应用情况如下表格所示,两者应用是不相同的, 一致性哈希值却是相等的,一致性哈希算法不合理。


UP (server)UP (client)
应用Am - cm
应用Bn + cn
  • 异常情况下【2】,变更记录队列部分过期,例如应用A 和 应用B 都剩余 w 条变更记录。那 Eureka-Client 从 Eureka-Server 获取到部分的增量变化并合并,两者应用是不相同的,此时应用情况如下表格所示,一致性哈希值却是相等的,一致性哈希算法不合理。


UP (server)UP (client)
应用Am - cm - w
应用Bn + cn + w

What ? 从异常情况【1】【2】可以看到,一致性哈希算法竟然是不合理的,那么我们手动来做一次最精简的实验。实验如下:

  • 模拟场景:异常情况【1】,m = n = c = 1 。简单粗暴。

  • 特别配置

    • eureka.retentionTimeInMSInDeltaQueue = 1 ,变更记录队列每条记录存活时长 1 ms。用以实现 Eureka-Client 请求不到完整的增量变化。

    • eureka.deltaRetentionTimerIntervalInMs = 1 ,变更记录队列每条记录过期定时任务执行频率 1 ms。用以实现 Eureka-Client 请求不到完整的增量变化。

    • eureka.shouldUseReadOnlyResponseCache = false ,禁用响应缓存的只读缓存。用以避免等待缓存刷新。

    • eureka.waitTimeInMsWhenSyncEmpty = 1 ,

  • 实验过程

  1. 00:00 启动 Eureka-Server

  2. 00:30 启动应用A ,向 Eureka-Server 注册

  3. 01:00 启动 Eureka-Client ,向 Eureka-Server 获取注册信息,等待获取到应用A

  4. 01:30 关闭应用A 。立即启动应用B ,向 Eureka-Server 注册

  5. 等待 5 分钟,Eureka-Client 无法获取到应用B

  6. 此时应用情况如下表格所示,两者应用是不相同的,一致性哈希值却是相等的,一致性哈希算法不合理。


UP (server)UP (client)
应用A01
应用B10

????结论????

当然排除掉特别极端的场景,Eureka-Client 从 Eureka-Server 因为网络异常导致一直同步不到增量变化,又恰好应用关闭和开启满足状态统计数量。另外,变更记录队列记录过期时长为 300 秒,增量获取频率为 30 秒,获取的次数有 10 次左右。所以,应用集合一致性哈希码在绝大多数场景是合理的。笔者的YY,解决这个极小场景有如下方式:

  • 第一种,修改计算公式 appsHashCode = MD5(${app_name}_${instance_id}_${status}_${count}_) ,增加对应用名和应用实例编号敏感。

  • 第二种,每 N 分钟进行一次全量获取注册信息。

ps :笔者怀着忐忑的心写完了这个小节,如果有不合理的地方,又或者有不同观点的胖友,欢迎一起探讨。谢谢。

TODO[0027][反思]:应用集合一致性哈希算法。

3. Eureka-Client 发起增量获取

在 《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》「2.4 发起获取注册信息」 里,调用 DiscoveryClient#getAndUpdateDelta(...) 方法,增量获取注册信息,并刷新本地缓存,实现代码如下:

  1: private void getAndUpdateDelta(Applications applications) throws Throwable {
 2:     long currentUpdateGeneration = fetchRegistryGeneration.get();
 3:
 4:     // 增量获取注册信息
 5:     Applications delta = null;
 6:     EurekaHttpResponse<Applications> httpResponse = eurekaTransport.queryClient.getDelta(remoteRegionsRef.get());
 7:     if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {
 8:         delta = httpResponse.getEntity();
 9:     }
10:
11:     if (delta == null) {
12:         // 增量获取为空,全量获取
13:         logger.warn("The server does not allow the delta revision to be applied because it is not safe. "
14:                 + "Hence got the full registry.");
15:         getAndStoreFullRegistry();
16:     } else if (fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1)) {
17:         logger.debug("Got delta update with apps hashcode {}", delta.getAppsHashCode());
18:         String reconcileHashCode = "";
19:         if (fetchRegistryUpdateLock.tryLock()) {
20:             try {
21:                 // 将变化的应用集合和本地缓存的应用集合进行合并
22:                 updateDelta(delta);
23:                 // 计算本地的应用集合一致性哈希码
24:                 reconcileHashCode = getReconcileHashCode(applications);
25:             } finally {
26:                 fetchRegistryUpdateLock.unlock();
27:             }
28:         } else {
29:             logger.warn("Cannot acquire update lock, aborting getAndUpdateDelta");
30:         }
31:         // There is a diff in number of instances for some reason
32:         if (!reconcileHashCode.equals(delta.getAppsHashCode()) // 一致性哈希值不相等
33:                 || clientConfig.shouldLogDeltaDiff()) { //
34:             reconcileAndLogDifference(delta, reconcileHashCode);  // this makes a remoteCall
35:         }
36:     } else {
37:         logger.warn("Not updating application delta as another thread is updating it already");
38:         logger.debug("Ignoring delta update with apps hashcode {}, as another thread is updating it already", delta.getAppsHashCode());
39:     }
40: }
  • 第 4 至 9 行 :请求增量获取注册信息,实现代码如下:

    // AbstractJerseyEurekaHttpClient.java
    @Override
    public EurekaHttpResponse<Applications> getDelta(String... regions) {
      return getApplicationsInternal("apps/delta", regions);
    }
    • 调用 AbstractJerseyEurekaHttpClient#getApplicationsInternal(...) 方法,GET 请求 Eureka-Server 的 apps/detla 接口,参数为 regions ,返回格式为 JSON ,实现增量获取注册信息。

  • 第 11 至 15 行 :增量获取失败,调用 #getAndStoreFullRegistry() 方法,全量获取注册信息,并设置到本地缓存。该方法在 《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》「2.4.1 全量获取注册信息,并设置到本地缓存」 有详细解析。

  • 第 16 至 35 行 :处理增量获取的结果。

    • 第 33 行 :配置 eureka.printDeltaFullDiff ,是否打印增量和全量差异。默认值 :false 。从目前代码实现上来看,暂时没有生效。注意 :开启该参数会导致每次增量获取后又发起全量获取,不要开启。

    • 第 16 行 :TODO[0025] :并发更新的情况???

    • 第 19 行 :TODO[0025] :并发更新的情况???

    • 第 21 行 :调用 #updateDelta(...) 方法,将变化的应用集合和本地缓存的应用集合进行合并。

    • 第 31 至 35 行 :一致性哈希值不相等,调用 #reconcileAndLogDifference() 方法,全量获取注册信息,并设置到本地缓存,和 #getAndStoreFullRegistry() 基本类似。

3.1 合并应用集合

调用 #updateDelta(...) 方法,将变化的应用集合和本地缓存的应用集合进行合并。实现代码如下:

  1: private void updateDelta(Applications delta) {
 2:     int deltaCount = 0;
 3:     for (Application app : delta.getRegisteredApplications()) { // 循环增量(变化)应用集合
 4:         for (InstanceInfo instance : app.getInstances()) {
 5:             Applications applications = getApplications();
 6:             // TODO[0009]:RemoteRegionRegistry
 7:             String instanceRegion = instanceRegionChecker.getInstanceRegion(instance);
 8:             if (!instanceRegionChecker.isLocalRegion(instanceRegion)) {
 9:                 Applications remoteApps = remoteRegionVsApps.get(instanceRegion);
10:                 if (null == remoteApps) {
11:                     remoteApps = new Applications();
12:                     remoteRegionVsApps.put(instanceRegion, remoteApps);
13:                 }
14:                 applications = remoteApps;
15:             }
16:
17:             ++deltaCount;
18:             if (ActionType.ADDED.equals(instance.getActionType())) { // 添加
19:                 Application existingApp = applications.getRegisteredApplications(instance.getAppName());
20:                 if (existingApp == null) {
21:                     applications.addApplication(app);
22:                 }
23:                 logger.debug("Added instance {} to the existing apps in region {}", instance.getId(), instanceRegion);
24:                 applications.getRegisteredApplications(instance.getAppName()).addInstance(instance);
25:             } else if (ActionType.MODIFIED.equals(instance.getActionType())) { // 修改
26:                 Application existingApp = applications.getRegisteredApplications(instance.getAppName());
27:                 if (existingApp == null) {
28:                     applications.addApplication(app);
29:                 }
30:                 logger.debug("Modified instance {} to the existing apps ", instance.getId());
31:
32:                 applications.getRegisteredApplications(instance.getAppName()).addInstance(instance);
33:             } else if (ActionType.DELETED.equals(instance.getActionType())) { // 删除
34:                 Application existingApp = applications.getRegisteredApplications(instance.getAppName());
35:                 if (existingApp == null) {
36:                     applications.addApplication(app);
37:                 }
38:                 logger.debug("Deleted instance {} to the existing apps ", instance.getId());
39:                 applications.getRegisteredApplications(instance.getAppName()).removeInstance(instance);
40:             }
41:         }
42:     }
43:     logger.debug("The total number of instances fetched by the delta processor : {}", deltaCount);
44:
45:     getApplications().setVersion(delta.getVersion());
46:     // 过滤、打乱应用集合
47:     getApplications().shuffleInstances(clientConfig.shouldFilterOnlyUpInstances());
48:
49:     // TODO[0009]:RemoteRegionRegistry
50:     for (Applications applications : remoteRegionVsApps.values()) {
51:         applications.setVersion(delta.getVersion());
52:         applications.shuffleInstances(clientConfig.shouldFilterOnlyUpInstances());
53:     }
54: }
  • 第 6 至 15 行 :TODO[0009]:RemoteRegionRegistry

  • 第 18 至 24 行 :添加( ADDED )应用实例时,调用 Application#addInstance(...) 方法,实现代码如下:

    // Application.java
    public void addInstance(InstanceInfo i) {
      // 添加到 应用实例映射
      instancesMap.put(i.getId(), i);
      synchronized (instances) {
          // 移除原有实例
          instances.remove(i);
          // 添加新实例
          instances.add(i);
          // 设置 isDirty ,目前只用于 `#toString()` 方法打印,无业务逻辑
          isDirty = true;
      }
    }

    // InstanceInfo.java
    @Override
    public int hashCode() { // 只使用 ID 计算 hashcode
      String id = getId();
      return (id == null) ? 31 : (id.hashCode() + 31);
    }

    @Override
    public boolean equals(Object obj) { // 只对比 ID
      if (this == obj) {
          return true;
      }
      if (obj == null) {
          return false;
      }
      if (getClass() != obj.getClass()) {
          return false;
      }
      InstanceInfo other = (InstanceInfo) obj;
      String id = getId();
      if (id == null) {
          if (other.getId() != null) {
              return false;
          }
      } else if (!id.equals(other.getId())) {
          return false;
      }
      return true;
    }
  • 第 25 至 32 行 :修改( MODIFIED )应用实例时,同样调用 Application#addInstance(...) 方法。

  • 第 33 至 40 行 :删除( DELETED )应用实例时,调用 Application#removeInstance(...) 方法,实现代码如下:

    public void removeInstance(InstanceInfo i) {
       removeInstance(i, true);
    }

    private void removeInstance(InstanceInfo i, boolean markAsDirty) {
       // 移除 应用实例映射
       instancesMap.remove(i.getId());
       synchronized (instances) {
           // 移除 应用实例
           instances.remove(i);
           if (markAsDirty) {
               // 设置 isDirty ,目前只用于 `#toString()` 方法打印,无业务逻辑
               isDirty = true;
           }
       }
    }
  • 第 47 行 :调用 Applications#shuffleInstances(...) 方法,根据配置 eureka.shouldFilterOnlyUpInstances = true ( 默认值 :true ) 过滤只保留状态为开启( UP )的应用实例,并随机打乱应用实例顺序。打乱后,实现调用应用服务的随机性。代码比较易懂,点击链接查看方法实现。

  • 第 49 至 53 行 :TODO[0009]:RemoteRegionRegistry

4. Eureka-Server 接收全量获取

3.1 接收全量获取请求

com.netflix.eureka.resources.ApplicationsResource,处理所有应用的请求操作的 Resource ( Controller )。

接收增量获取请求,映射 ApplicationsResource#getContainers() 方法。

  • 和 《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》「3.1 接收全量获取请求」 类似,就不重复啰嗦啦。

  • 点击 链接 查看该方法的带中文注释代码。

3.2 最近租约变更记录队列

AbstractInstanceRegistry.recentlyChangedQueue,最近租约变更记录队列。实现代码如下:

// AbstractInstanceRegistry.java
/**
* 最近租约变更记录队列
*/
private ConcurrentLinkedQueue<RecentlyChangedItem> recentlyChangedQueue = new ConcurrentLinkedQueue<RecentlyChangedItem>();

/**
* 最近租约变更记录
*/
private static final class RecentlyChangedItem {
  /**
   * 最后更新时间戳
   */
  private long lastUpdateTime;
  /**
   * 租约
   */
  private Lease<InstanceInfo> leaseInfo;

  public RecentlyChangedItem(Lease<InstanceInfo> lease) {
      this.leaseInfo = lease;
      lastUpdateTime = System.currentTimeMillis();
  }

  public long getLastUpdateTime() {
      return this.lastUpdateTime;
  }

  public Lease<InstanceInfo> getLeaseInfo() {
      return this.leaseInfo;
  }
}
  • 当应用实例注册、下线、状态变更时,创建最近租约变更记录( RecentlyChangedItem ) 到队列。

  • 后台任务定时顺序扫描队列,当 lastUpdateTime 超过一定时长后进行移除。实现代码如下:

    // AbstractInstanceRegistry.java
    this.deltaRetentionTimer.schedule(getDeltaRetentionTask(),
                   serverConfig.getDeltaRetentionTimerIntervalInMs(),
                   serverConfig.getDeltaRetentionTimerIntervalInMs());

    private TimerTask getDeltaRetentionTask() {
      return new TimerTask() {
       @Override
      public void run() {
          Iterator&lt;RecentlyChangedItem&gt; it = recentlyChangedQueue.iterator();
          while (it.hasNext()) {
              if (it.next().getLastUpdateTime() &lt; System.currentTimeMillis() - serverConfig.getRetentionTimeInMSInDeltaQueue()) {
                  it.remove();
              } else {
                  break;
              }
          }
      }
      };
    }
    • 配置 eureka.deltaRetentionTimerIntervalInMs, 移除队列里过期的租约变更记录的定时任务执行频率,单位:毫秒。默认值 :30 * 1000 毫秒。

    • 配置 eureka.retentionTimeInMSInDeltaQueue,租约变更记录过期时长,单位:毫秒。默认值 : 3 * 60 * 1000 毫秒。

3.3 缓存读取

在 《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》「3.3 缓存读取」 里,在 #generatePayload() 方法里,调用 AbstractInstanceRegistry#getApplicationDeltas(...) 方法,获取近期变化的应用集合,实现代码如下:

// AbstractInstanceRegistry.java
 1: public Applications getApplicationDeltas() {
 2:     // 添加 增量获取次数 到 监控
 3:     GET_ALL_CACHE_MISS_DELTA.increment();
 4:     // 初始化 变化的应用集合
 5:     Applications apps = new Applications();
 6:     apps.setVersion(responseCache.getVersionDelta().get());
 7:     Map<String, Application> applicationInstancesMap = new HashMap<String, Application>();
 8:     try {
 9:         // 获取写锁
10:         write.lock();
11:         // 获取 最近租约变更记录队列
12:         Iterator<RecentlyChangedItem> iter = this.recentlyChangedQueue.iterator();
13:         logger.debug("The number of elements in the delta queue is :" + this.recentlyChangedQueue.size());
14:         // 拼装 变化的应用集合
15:         while (iter.hasNext()) {
16:             Lease<InstanceInfo> lease = iter.next().getLeaseInfo();
17:             InstanceInfo instanceInfo = lease.getHolder();
18:             Object[] args = {instanceInfo.getId(), instanceInfo.getStatus().name(), instanceInfo.getActionType().name()};
19:             logger.debug("The instance id %s is found with status %s and actiontype %s", args);
20:             Application app = applicationInstancesMap.get(instanceInfo.getAppName());
21:             if (app == null) {
22:                 app = new Application(instanceInfo.getAppName());
23:                 applicationInstancesMap.put(instanceInfo.getAppName(), app);
24:                 apps.addApplication(app);
25:             }
26:             app.addInstance(decorateInstanceInfo(lease));
27:         }
28:
29:         // TODO[0009]:RemoteRegionRegistry
30:         boolean disableTransparentFallback = serverConfig.disableTransparentFallbackToOtherRegion();
31:         if (!disableTransparentFallback) {
32:             Applications allAppsInLocalRegion = getApplications(false);
33:
34:             for (RemoteRegionRegistry remoteRegistry : this.regionNameVSRemoteRegistry.values()) {
35:                 Applications applications = remoteRegistry.getApplicationDeltas();
36:                 for (Application application : applications.getRegisteredApplications()) {
37:                     Application appInLocalRegistry =
38:                             allAppsInLocalRegion.getRegisteredApplications(application.getName());
39:                     if (appInLocalRegistry == null) {
40:                         apps.addApplication(application);
41:                     }
42:                 }
43:             }
44:         }
45:
46:         // 获取全量应用集合,通过它计算一致性哈希值
47:         Applications allApps = getApplications(!disableTransparentFallback);
48:         apps.setAppsHashCode(allApps.getReconcileHashCode());
49:         return apps;
50:     } finally {
51:         write.unlock();
52:     }
53: }
  • 第 2 至 3 行 :添加增量获取次数到监控。配合 Netflix Servo 实现监控信息采集。

  • 第 4 行 :初始化变化( 增量 )的应用集合( apps )。

  • 第 9 行 :获取写锁。在 《Eureka源码解析 —— 应用实例注册发现 (九)之岁月是把萌萌的读写锁》 详细解析。

  • 第 11 至 13 行 :获取最近租约变更记录队列( 最近租约变更记录队列 )。

  • 第 14 至 27 行 :拼装变化的应用集合( apps )。

  • 第 29 至 44 行 :TODO[0009]:RemoteRegionRegistry

  • 第 46 至 48 行 :调用 #getApplications(...) 方法,获取全量应用集合( allApps ),在 《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》「3.3.1 获得注册的应用集合」 有详细解析。后通过 allApps 计算一致性哈希值。通过这个全量应用集合的哈希值,Eureka-Client 获取到增量应用集合并合并后,就可以比对啦。

  • 第 51 行 :释放写锁。

666. 彩蛋

在汉堡王写完这篇热情的博客。为什么用“热情”这个字眼呢?大夏天的,竟然不开空调的!对的,没有开空调,简直是个小火炉。恩,不过静心写完这篇文章,让我还是挺嗨皮的。

胖友,分享我的公众号( 芋道源码 ) 给你的胖友可好?



知识星球

目前在知识星球(https://t.zsxq.com/2VbiaEu)更新了如下 Dubbo 源码解析如下:

01. 调试环境搭建
02. 项目结构一览
03. API 配置(一)之应用
04. API 配置(二)之服务提供者
05. API 配置(三)之服务消费者
06. 属性配置
07. XML 配置
08. 核心流程一览

09. 拓展机制 SPI

10. 线程池

...
一共 60 篇++

三、Eureka源码解析:客户端服务列表的获取与更新 1、Eureka client从注册中心更新服务列表,然后自身会做缓存; 2、作为服务消费者,就是从这些缓存信息中获取的服务提供者的信息; 3、增量更新的服务以30秒为周期循环调用; 4、增量更新数据在服务端保存时间为3分钟,因此Eureka client取得的数据虽然被称为"增量更新",仍然可能和30秒前取的数据一样,所以Eureka client要自己来处理重复信息; 5、由3、4两点... 阅读详情

相关推荐

FortiOS路径遍历漏洞CVE-2022-41328实战复现:从环境搭建到漏洞利用全记录

本文深入解析FortiOS路径遍历漏洞CVE-2022-41328,从漏洞背景、影响范围到技术原理进行全面剖析。提供详细的漏洞复现环境搭建步骤和实战利用方法,并给出官方修复方案与临时缓解措施,帮助企业有效防御此类防火墙漏洞风险。

weixin_30485379的博客 525

Eureka 源码解析 —— 应用实例注册发现(七)之增量获取

摘要: 原创出处 http://www.iocoder.cn/Eureka/instance-registry-fetch-delta/ 「芋道源码」欢迎转载,保留摘要,谢谢!本文主要基于 Eureka 1.8.X 版本 1. 概述 2. 应用集合一致性哈希码 2.1 计算公式 2.2 合理性3. Eureka-Client 发起增量获取 3.1 合并应用集合 4. Eureka-Server

天黑了 1094

VS2022 LNK1168无法打开进行写入 解决办法

C语言编程---VS---错误和警告---链接器工具错误---LNK1168--无法打开进行写入

weixin_67166415的博客 7162

聊聊eureka server的RemoteRegionRegistry

序 本文主要研究下eureka server的RemoteRegionRegistry PeerAwareInstanceRegistryImpl.getSortedApplications eureka-core-1.8.8-sources.jar!/com/netflix/eureka/registry/PeerAwareInstanceRegistryImpl.java /** ...

weixin_34004576的博客 441

【第24例】华为 IPD 体系 | RMT 需求管理团队

RMT需求管理团队

产品线负责人 1769

地平线旭日X3开发板--使用Opencv显示CSI摄像头例程

但是从libsrcampy获取的摄像头图像格式是NV12,无法直接在opencv上显示或者处理,调试过程发现旭日派无法直接用opencv的VideoCapture()调用CSI摄像头,需要使用opencv来做开发的小伙伴可以做个参考,效果如下图所示。需要使用libsrcampy库来获取CSI摄像头图像,为了能够使用opencv的API来处理图像,需要增加NV12转化成bgr像素格式的函数。opencv显示图像.png。

bingdund的博客 542

【Eureka】【12】EurekaClient启动后,增量获取注册信息

CacheRefreshThread @Singleton public class DiscoveryClient implements EurekaClient { private final ScheduledExecutorService scheduler; @Inject DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, DiscoveryClient

huiyanshizhen21的博客 437

从零开始学习CANoe(六)—— CAPL 测试节点

创建CAPL Test Module的两种方式

蚂蚁小兵 1万+

spring cloud五大组件之Eureka注册中心

spring cloud五大组件值Eureka注册中心 一、原理 ---------------------------还缺个覆盖状态 ​ Eureka注册中心用于服务的注册和发现,即是将服务信息注册到存储微服务信息的服务器,进行微服务调用时再取得服务相关信息进行调用。根据功能进行分类:服务注册、服务续约、服务下线。为保证服务的高可用,可进行集群部署,增加服务的同步功能。保证高可用性防止因网络问题造成的服务集体下线增加服务的自我保护功能。我们根据这些功能来看下具体的实现。 二、注册和发现的实现 ​ 1)、服

qq_35015148的博客 627

1.1、matlab:基于模板匹配的车牌识别

matlab:基于模板匹配的车牌识别

XU157303764的博客 2917

基于matlab的阈值分割的车牌定位识别系统

车牌自动识别模块是现代社会智能交通系统(ITS)的重要组成部分,是图像处理和模式识别技术研究的热点,具有非常广泛的应用。车牌识别主要包括以下三个主要步骤:车牌区域定位、车牌字符分割、车牌字符识别。本案例通过对采集的车牌图像进行灰度变换、边缘检测、腐蚀及平滑等过程进行车牌图像预处理,并由此得到一种基于车牌颜色纹理特征的车牌定位方法,最终实现了车牌区域定位。车牌字符分割是为了方便后续对车牌字符进行匹配,从而对车牌进行识别。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 973

聊聊eureka的delta配置

序 本文主要研究一下eureka的delta配置 client端配置 { "sourceType": "org.springframework.cloud.netflix.eureka.EurekaClientConfigBean", "defaultValue": false, "name":...

weixin_33918114的博客 481

基于STM32+ESP8266+手机APP的心率血氧温湿度监测报警系统设计(项目资料ID:18)

基于 STM32 微控制器、ESP8266 无线模块和 QT 手机 APP 的多参数监测报警系统。该系统可实时采集心率、血氧饱和度、环境温湿度等参数,通过 OLED 显示屏本地显示,并通过 WiFi 模块上传至手机 APP 实现远程监测与数据可视化。系统具备报警阈值自定义功能,当参数超出设定范围时,通过蜂鸣器和 LED 进行声光报警。系统以 STM32F103 为控制核心,采用 MAX30102 传感器采集心率和血氧,DHT20 采集温湿度,通过 ESP8266 实现无线通信,配合 QT 开发的手机 APP

以培养嵌入式工程师为己任 1万+

12、Eureka 源码解析 之 Eureka Client 增量拉取注册表

在之前的博客当中 6、Eureka 源码解析 之 Eureka Server 多级缓存 已经详细说明了在 Eureka Client 启动的时候会去 Eureka Server 里面全量的拉取一下注册到里面的服务信息列表。并且 Eureka Server 里面是使用了多级缓存来保存全量的注册信息列表。 Eureka Client 在启动时获取 Eureka Server 中的注册信息,但是作为分布式环境服务随时时可进行上线、下线操作。所以作为注册中心它需要有服务自动上下线发现的功能。

carl.zhao的专栏 1430

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Eureka 源码解析 —— Eureka源码解析 —— 应用实例注册发现 (九)之读写锁

1. 概述 本文主要分享Eureka 注册中心的那把读写锁,让我瘙痒难耐,却不得其解。在某次意外的抠脚的一刻( 笔者不抽烟,如果抽烟的话,此处应该就不是抠脚了 ),突然顿悟,爽,这好比... 比喻有点猥琐,笔者就省略 100 字。 不瞎比比,上代码: public abstract class AbstractInstanceRegistry implements InstanceRegistry { private final ReentrantReadWriteLock readWr.

weixin_42073629的博客 470

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

注册中心 Eureka 源码解析 —— 应用实例注册发现(六)之全量获取

点击上方“芋道源码”,选择“置顶公众号”技术文章第一时间送达!源码精品专栏 中文详细注释的开源项目Java 并发源码合集RocketMQ 源码合集Sharding-JDB...

芋道源码 560

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Eureka实现原理深度解析:从源码到实战

特性实现方式目的AP设计最终一致 + 自我保护保证高可用心跳检测客户端主动续约减少服务端压力缓存机制客户端本地缓存减少网络请求增量拉取只拉取变更部分减少数据传输自我保护阈值触发 + 特殊处理应对网络分区Eureka通过多层Map存储、心跳续约、定时剔除、增量拉取和自我保护等机制,构建了一个AP风格的轻量级服务注册中心。其设计思想影响了后来所有的注册中心组件。(本文为微服务架构系列文章,欢迎关注更多分布式系统深度内容)

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 1162

Eureka增量获取机制源码揭秘

Eureka增量获取机制通过只同步变化的服务实例信息提升效率,降低了网络和服务端压力。其核心流程包括:客户端定期请求增量数据,服务端从最近变更队列获取变化信息,客户端合并增量并通过一致性哈希校验保证数据一致性。该机制包含故障降级策略(增量失败转全量)和多级缓存支持,在保证数据最终一致性的同时显著提升了微服务架构下的注册表同步性能。

油墨香^-^的博客 1170

Spring Cloud Netflix 注册中心 Eureka 入门

1. 概述 本文我们来学习Spring Cloud Netflix提供的spring-cloud-netflix-eureka-server和spring-cloud-netflix-eureka-server组件,基于 Spring Cloud 的编程模型,接入Eureka作为注册中心,实现服务的注册与发现。 旁白君:下面我们先来科普 Eureka 和注册中心的概念,保持耐心,嘿嘿~ 2. Eureka 简介 Eureka 是 Netflix 开源的注册中心组件,分成 Eure...

weixin_42073629的博客 800

注册中心 Eureka源码解析 —— 应用实例注册发现 (九)之岁月是把萌萌的读写锁...

点击上方“芋道源码”,选择“置顶公众号”技术文章第一时间送达!源码精品专栏 精尽 Dubbo 原理与源码专栏( 已经完成 69+ 篇,预计总共 75+ 篇 )中文详细注释...

芋道源码 585

芋道 Spring Cloud Netflix 注册中心 Eureka 入门

摘要: 原创出处 http://www.iocoder.cn/Spring-Cloud/Netflix-Eureka/ 「芋道源码」欢迎转载,保留摘要,谢谢! 1. 概述 2. Eureka 简介 3. 注册中心原理 4. 快速入门 5. 高可用 Eureka 集群 6. 安全认证 666. 彩蛋 本文在提供完整代码示例,可见https://github.com/YunaiV/SpringBoot-Labs的labx-22目录。 原创不易,给点个Star嘿,一起冲鸭! 1....

很多时候犯错都是在不知情的情况下发生的 584

SpringCloud系列之Eureka服务注册及发现

EurekaEureka简介什么是EurekaEureka实例编写Eureka Server编写Eureka Client构建高可用的Eureka Server集群 Eureka简介 什么是Eureka 和Consul,zookeeper类似,Eureka是一个用于服务注册及发现的组件,最开始主要应用于亚马逊公司旗下的云计算服务平台AWS。Eureka分为Eureka Server和Eureka Client。其中server是Eureka的服务注册中心,Client是客户端。 Eureka实例 编写Eur

陆林烽的博客 1527

深度解析:Eureka 首次同步注册表的八步详解

对于 Server 端来说,为了更好的提供查询注册表的服务,使用了多级缓存来缓存注册表信息。对于 Client 端来说,首次获取注册表时就会全量抓取注册表,存在自己本地。

m0_61571842的博客 1372

微服务之服务注册中心(EUREKA)

为什么要用eureka 在微服务架构中,原本一个项目被拆分成若干个小项目独立运行,他们有各自的端口,运行在不同的jvm上.假设 A 功能需要用到B 和 C 模块的数据,原本在一个项目中的时候直接通过内存调用就可以获得,现在被拆分为多个服务.就得通过网络传输数据. eureka 的作用就是让被拆分的服务能相互调用,作为一个服务的注册和发现 eureka 概念 1. Eureka Server 提供一个场所让想要被人调用的服务可以在这里注册自己的信息 2. Eureka Provider 服务的提供方,将自己的

weixin_48420669的博客 1316
上一篇: 分布式消息队列 RocketMQ 源码分析 —— Message 发送与接收
下一篇: 【RPC 专栏】深入理解 RPC 之协议篇
公众号-芋道源码
博客等级 码龄10年 1636粉丝 377原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值