マイクロサービスアーキテクチャでは、サービス管理の中核部分であり、ガバナンスの最も基本的なコンポーネントは、登録されたサービス・センターです。春の雲のサポート飼育係、領事とEurakaは、正式にユーレカの登録センターとして推奨します。
ユーレカ純粋なJava実装、一つだけのサーバが利用可能な場合でも、可用性レジストリを満たすために偉大な基本的なサービスの登録と発見を達成するために、レジストリの追加は、それは、レジストリの可用性を確保することができます。
ユーレカは、設計原理のAP、すなわち可用性とフォールトトレランスパーティションです。それは登録センターの利用可能性を保証するが、データの強い整合性を放棄し、各ノード上のデータが矛盾(支持結果整合性)であってもよいです。
1.ユーレカ全体のアーキテクチャ
1.1ユーレカ展開アーキテクチャ
図は、より多くの部屋アーキテクチャ図に配備ユーレカレジストリで、ここでの高可用性の利点を体現
アセンブリ関数から見て
- 北京、天津、青島の部屋で展開イエロークラスタ・レジストリ、。
- 北京と青島部屋に配備レッドサービスプロバイダ、。
- ライトグリーンコンシューマーサービスは北京と天津の部屋に配備されました。
エンジンルームの分布から、
- レジストリ、サービスプロバイダとサービスコンシューマを展開する北京部屋
- 天津は、レジストリやルームサービスコンシューマを展開しました
- 青島の部屋は、登録センターやサービス・プロバイダーを展開しています
1.2呼び出し関係部品
1.2.1サービスプロバイダ
- スタート後、登録センターを開始する要求を登録し、登録サービス。
- 動作中は、定期的にそれは「私が生きている」ことを証明するために、登録センターを更新するために、ハートビートを送信します。
- ストップサービスプロバイダ、現在のサービスの登録情報を消去し、レジストリをキャンセルする要求を開始します。
1.2.2消費者サービス
- プルサービスレジストリ登録情報から開始した後、
- 動作中は、定期的にサービス登録情報を更新しました。
- サービスの消費者は、リモート呼び出しを開始します。
A)消費者サービス(北京)、ルームサービス(北京)でのサービスプロバイダ登録情報から選択したリモート呼び出しを開始、ちょうど部屋のサービスプロバイダにハングアップするサービスプロバイダ、他の部屋(青島)を選択します。
B)消費者サービス(天津)、北京や青島のサービスプロバイダを選択しますロード・バランシング・アルゴリズムに従って、同じ部屋内には、サービスプロバイダが存在しないため、リモート呼び出しを開始します。
1.2.3登録センター
- 、別のノードの登録情報からプルサービスを開始した後、
- 動作中、タイミングタスクが(タスクやネットワーク障害の不規則な停止を含む)のサービスを更新する時間を排除することなく、追い出しを実行しています。
- 動作時には、レジスタ、リクエストを更新し、キャンセルを受け、レジストリは、他のノードに同期されます。
データ記憶構造1.3
ユーレカは、図形データ格納構造です。データ記憶層とバッファ層:二層にユーレカデータ記憶装置。ユーレカクライアントサービス情報取得開始バッファ層を引っ張るとき、取得していない場合、データはキャッシュから取り出さ第1のデータ記憶層のキャッシュにロードされます。なお、データ記憶層のデータ構造は、サービス情報であり、加工処理されたキャッシュに格納され、ユーレカクライアントのデータ構造に直接送信されてもよいです。
1.3.1データ記憶層
レジストリデータ記憶層は、メモリに記憶された二層のConcurrentHashMap、データです。
- 第一層は、値がConcurrentHashMapの第二の層キーspring.application.nameです。
- ConcurrentHashMapの第二の層は、値リース対象であるのInstanceIdサービスキーです。
- リースオブジェクトは、サービス情報やサービスのガバナンスに関連するプロパティが含まれています。
我々は見つけることができます。このストレージ構成によれば、ユーレカServerは、すべてのサービス名、サービス名と対応するインスタンス情報第一層店です。第二層は、ストア固有のIDの一例です
データ記憶層データ構造を次のように:
1.3.2二次キャッシュ層
ユーレカは、外部サービス情報の送信を保持する二次キャッシュを実現しています。
レベルのキャッシュ(readOnlyCacheMap):
ConcurrentHashMap <キー、値> readOnlyCacheMap、有効期限なしでは、外国人のサービス情報の出力データ構造を保存します。readWriteCacheMap、30秒のデフォルトの時間からデータ依存のタイミング同期。
二次キャッシュ(readWriteCacheMap):
Loading<Key, Value> readWriteCacheMap,本质上是Google的guava缓存,包含失效机制,保存服务信息的对外输出数据结构。当获取缓存时判断缓存中是否没有数据,如果不存在此数据,则通过 CacheLoader 的 load 方法去加载,加载成功之后将数据放入缓存,同时返回数据。readWriteCacheMap 缓存过期时间,默认为 180 秒,当服务下线、过期、注册、状态变更,都会来清除此缓存中的数据。
Eureka Client 获取全量或者增量的数据时,会先从一级缓存中获取;如果一级缓存中不存在,再从二级缓存中获取;如果二级缓存也不存在,这时候先将存储层的数据同步到缓存中,再从缓存中获取。
通过 Eureka Server 的二层缓存机制,可以非常有效地提升 Eureka Server 的响应时间,通过数据存储层和缓存层的数据切割,根据使用场景来提供不同的数据支持。
1.3.3 缓存更新机制
既然是缓存,那必然要有更新机制,来保证数据的一致性。下面是缓存的更新机制。更新机制包含删除和加载两个部分,黑色箭头表示删除缓存的动作,绿色表示加载或触发加载的动作。:
删除二级缓存:
-
Eureka Client发送register、renew和cancel请求并更新registry注册表之后,删除二级缓存;
-
Eureka Server自身的Evict Task剔除服务后,删除二级缓存;
-
二级缓存本身设置了guava的失效机制,隔一段时间后自己自动失效;
加载二级缓存:
-
Eureka Client发送getRegistry请求后,如果二级缓存中没有,就触发guava的load,即从registry中获取原始服务信息后进行处理加工,再加载到二级缓存中。
-
Eureka Server更新一级缓存的时候,如果二级缓存没有数据,也会触发guava的load。
更新一级缓存:
- Eureka Server内置了一个TimerTask,定时将二级缓存中的数据同步到一级缓存(这个动作包括了删除和加载)。
2. 服务注册机制
服务提供者、服务消费者、以及服务注册中心自己,启动后都会向注册中心注册服务。下图是介绍如何完成服务注册的:
注册中心服务接收到register请求后:
-
保存服务信息,将服务信息保存到registry中;
-
更新队列,将此事件添加到更新队列中,供Eureka Client增量同步服务信息使用。
-
清空二级缓存,即readWriteCacheMap,用于保证数据的一致性。
-
更新阈值,供剔除服务使用。
-
同步服务信息,将此事件同步至其他的Eureka Server节点。
3. 服务续约机制
服务注册后,要定时(默认30S,可自己配置)向注册中心发送续约请求,告诉注册中心“我还活着”。
注册中心收到续约请求后:
- 更新服务对象的最近续约时间,即Lease对象的lastUpdateTimestamp;
- 同步服务信息,将此事件同步至其他的Eureka Server节点。
注:剔除服务之前会先判断服务是否已经过期,判断服务是否过期的条件之一是续约时间和当前时间的差值是不是大于阈值。
4. 服务注销机制
服务正常停止之前会向注册中心发送注销请求,告诉注册中心“我要下线了”。
注册中心服务接收到cancel请求后:
- 删除服务信息,将服务信息从registry中删除;
- 更新队列,将此事件添加到更新队列中,供Eureka Client增量同步服务信息使用。
- 清空二级缓存,即readWriteCacheMap,用于保证数据的一致性。
- 更新阈值,供剔除服务使用。
- 同步服务信息,将此事件同步至其他的Eureka Server节点。
注:服务正常停止才会发送Cancel,如果是非正常停止,则不会发送,此服务由Eureka Server主动剔除。
5. 服务剔除机制
Eureka Server提供了服务剔除的机制,用于剔除没有正常下线的服务。
服务的剔除包括三个步骤,首先判断是否满足服务剔除的条件,然后找出过期的服务,最后执行剔除。
5.1 判断是否满足服务剔除的条件
有两种情况可以满足服务剔除的条件:
- 关闭了自我保护
- 如果开启了自我保护,需要进一步判断是Eureka Server出了问题,还是Eureka Client出了问题,如果是Eureka Client出了问题则进行剔除。
这里比较核心的条件是自我保护机制,Eureka自我保护机制是为了防止误杀服务而提供的一个机制。Eureka的自我保护机制“谦虚”的认为如果大量服务都续约失败,则认为是自己出问题了(如自己断网了),也就不剔除了;反之,则是Eureka Client的问题,需要进行剔除。而自我保护阈值是区分Eureka Client还是Eureka Server出问题的临界值:如果超出阈值就表示大量服务可用,少量服务不可用,则判定是Eureka Client出了问题。如果未超出阈值就表示大量服务不可用,则判定是Eureka Server出了问题。
条件1中如果关闭了自我保护,则统统认为是Eureka Client的问题,把没按时续约的服务都剔除掉(这里有剔除的最大值限制)。
5.1.1 自我保护阀值
自我保护阈值是区分Eureka Client还是Eureka Server出问题的临界值:如果超出阈值就表示大量服务可用,少量服务不可用,则判定是Eureka Client出了问题。如果未超出阈值就表示大量服务不可用,则判定是Eureka Server出了问题。
阈值的计算公式如下:
- 自我保护阈值 = 服务总数 * 每分钟续约数 * 自我保护阈值因子。
- 每分钟续约数 =(60S/客户端续约间隔)
- 最后自我保护阈值的计算公式为:自我保护阈值 = 服务总数 * (60S/客户端续约间隔) * 自我保护阈值因子。
举例:
如果有100个服务,续约间隔是30S,自我保护阈值0.85。
自我保护阈值=100 * 60 / 30 * 0.85 = 170。
如果上一分钟的续约数=180>170,则说明大量服务可用,是服务问题,进入剔除流程;
如果上一分钟的续约数=150<170,则说明大量服务不可用,是注册中心自己的问题,进入自我保护模式,不进入剔除流程。
5.2 找出过期的服务
遍历所有的服务,判断上次续约时间距离当前时间大于阈值就标记为过期。并将这些过期的服务保存到集合中。
5.3 剔除服务
在剔除服务之前先计算剔除的数量,然后遍历过期服务,通过洗牌算法确保每次都公平的选择出要剔除的任务,最后进行剔除。
执行剔除服务后:
- 删除服务信息,从registry中删除服务。
- 更新队列,将当前剔除事件保存到更新队列中。
- 清空二级缓存,保证数据的一致性。
6. 服务获取机制
Eureka Client获取服务有两种方式,全量同步和增量同步。获取流程是根据Eureka Server的多层数据结构进行的:
无论是全量同步还是增量同步,都是先从缓存中获取,如果缓存中没有,则先加载到缓存中,再从缓存中获取。(registry只保存数据结构,缓存中保存ready的服务信息。)
6.1 从一级缓存中获取
- 先判断是否开启了一级缓存
- 如果开启了则从一级缓存中获取,如果存在则返回,如果没有,则从二级缓存中获取
- 如果未开启,则跳过一级缓存,从二级缓存中获取
6.2 从二级缓存中获取
- 如果二级缓存中存在,则直接返回;
- 如果二级缓存中不存在,则先将数据加载到二级缓存中,再从二级缓存中获取。注意加载时需要判断是增量同步还是全量同步,增量同步从recentlyChangedQueue中load,全量同步从registry中load。
7. 服务同步机制
服务同步机制是用来同步Eureka Server节点之间服务信息的。它包括Eureka Server启动时的同步,和运行过程中的同步。
7.1 启动时同步
Eureka Server启动后,遍历eurekaClient.getApplications获取服务信息,并将服务信息注册到自己的registry中。
注:这里是两层循环,第一层循环是为了保证已经拉取到服务信息,第二层循环是遍历拉取到的服务信息。
7.2 运行过程中同步
当Eureka Server节点有register、renew、cancel请求进来时,会将这个请求封装成TaskHolder放到acceptorQueue队列中,然后经过一系列的处理,放到batchWorkQueue中。
TaskExecutor.BatchWorkerRunnable是个线程池,不断的从batchWorkQueue队列中poll出TaskHolder,然后向其他Eureka Server节点发送同步请求。
这里省略了两个部分:
- 一个是在acceptorQueue向batchWorkQueue转化时,省略了中间的processingOrder和pendingTasks过程。
- 另一个是当同步失败时,会将失败的TaskHolder保存到reprocessQueue中,重试处理。