記事ディレクトリ
序文
分散システムは、複数のノードが連携して高可用性、高パフォーマンス、および高スケーラビリティのサービスを提供するシステムです。ただし、分散システムは、ネットワーク遅延、ノード障害、データの一貫性など、多くの課題にも直面しています。これらの問題を解決するには、分散システムにはノード間の通信とコラボレーションを管理するための調整サービスが必要です。
ZooKeeper は、高性能の分散アプリケーション調整サービスです。これは、命名、構成管理、同期、グループ サービスなどの一般的に使用されるサービスを提供します。シンプルなインターフェイスでは、最初から作成する必要はなく、それを直接使用して、コンセンサス、グループ管理、リーダー選出、存在合意などの機能を実装できます。もちろん、その上に独自の特定のニーズを構築することもできます。
ZooKeeper は Java で書かれたオープン ソース ツール ライブラリであり、ZooKeeper クラスター内のデータにアクセスして操作するための Java API をネイティブに提供します。ZooKeeper を Java アプリケーションまたはフレームワークの依存関係ライブラリとして使用して、分散関数を実装したり、他の分散コンポーネントを統合したりできます。
ZooKeeper の名前の由来: 「Paxos から ZooKeeper へ」第 4 章 第 1 節
ZooKeeper は Yahoo Research の研究グループから生まれました。当時、研究者らは、Yahoo 内の多くの大規模システムは基本的に分散調整のために同様のシステムに依存する必要があるが、これらのシステムには分散された単一点の問題が頻繁に存在することを発見しました。したがって、Yahoo の開発者は、開発者がビジネス ロジックの処理に集中できるように、単一の問題点のない一般的な分散調整フレームワークの開発を試みました。
実は、「ZooKeeper」プロジェクトの名前には興味深い逸話があります。プロジェクトの初期段階では、多くの内部プロジェクト (有名な Pig プロジェクトなど) が動物にちなんで名付けられていることを考慮して、Yahoo のエンジニアはこのプロジェクトに動物の名前を付けることを希望していました。当時の同研究所の主任科学者、ラグー・ラマクリシュナン氏は冗談めかしてこう言った、「このままでは、私たちの場所は動物園になってしまう!」 コンポーネントを組み合わせると、Yahoo の分散システム全体が大きな動物園のように見え、ZooKeeper はそれに慣れているだけです。分散環境を調整するため、ZooKeeper という名前が生まれました。
この記事では、Java オープン ソース ツール ライブラリとしての ZooKeeper のアーキテクチャと動作原理、ZooKeeper が提供する Java API、および一部の分散システムまたはアプリケーションにおける ZooKeeper の使用例を紹介します。
公式ウェブサイト: https: //ZooKeeper.apache.org/
github:https://github.com/apache/ZooKeeper
pom の依存関係:
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.7.0</version>
</dependency>
1. 構造と動作原理
1.1 クラスター構造
Zookeeper は複数のサーバーで構成されるアンサンブルで、各サーバーには同じデータが保存され、ZAB (ZooKeeper Atomic Broadcast Protocol) プロトコルを通じてデータの一貫性が保証されます。クライアント (クライアント) は、任意のサーバーに接続し、要求 (リクエスト) を送信することでデータの読み取りまたは変更を行うことができます。サーバーはリクエストの種類に応じた処理を行い、応答(レスポンス)を返します。リクエストにデータの変更が含まれる場合、サーバーはリクエストをリーダーに転送し、リーダーはリクエストを他のフォロワーにブロードキャストし、リクエストを実行して応答を返す前に他のフォロワーが同意するのを待ちます。
Zookeeper クラスターは複数のサーバー ノードで構成され、各ノードは調整サービスを提供できます。データの一貫性を確保するために、Zookeeper クラスター内のノードは、リーダー、フォロワー、オブザーバーの 3 つの役割に分割されます。
- リーダー ノードはクラスター内のマスター ノードであり、クライアントの書き込み要求を処理し、フォロワー ノードのステータスを調整する責任を負います。リーダー ノードは投票によって選出され、クラスター内に存在できるリーダー ノードは 1 つだけです。
- フォロワー ノードはクラスター内のスレーブ ノードであり、クライアントの読み取り要求を処理し、リーダー ノードのデータを同期する責任を負います。フォロワー ノードは、リーダー ノードの投票に参加できます。
- オブザーバー ノードはクラスター内のオブザーバー ノードであり、クライアントの読み取り要求の処理とリーダー ノードのデータの同期を担当します。オブザーバー ノードはリーダー ノードの投票には参加せず、クラスターの可用性と一貫性には影響を与えません。
Zookeeper クラスターは通常、障害が発生した場合でも適切に機能できるように、奇数のノード (少なくとも 3 つ) を必要とします。たとえば、5 ノードのクラスタでは、2 つのノードがダウンしても、サービスを提供し続けることができるノードがまだ 3 つあります。
ZooKeeper には、リーダー ノードを選出してデータの一貫性を確保するために過半数が必要なため、奇数のノードが必要です。ノード数が偶数の場合、半分のノードに障害が発生すると過半数を形成できず、サービスの提供を継続できなくなります。さらに、偶数のノードを追加しても可用性は向上しませんが、通信オーバーヘッドが増加します。したがって、ZooKeeper は通常 3 つまたは 5 つのノードを使用してクラスターを構築します。
1.2 データモデル
ZooKeeper によって保存されるデータはツリー構造に編成されたノード (ノード) であり、各ノードは一意のパス (path) 識別を持ち、いくつかのメタデータ (metadata) とオプションの値 (value) を含みます。各ノードは子ノードを持つこともでき、階層関係を形成します。クライアントはパスを通じてノードにアクセスしたり、ノードを操作したりできます。
Zookeeper のデータ モデルは (ファイル システムに似た) 階層名前空間であり、各ノードは ZNode と呼ばれます。各 ZNode は一部のデータを保存でき、子ノードを持つ場合があります。ZNode へのパスはスラッシュで区切られ、絶対パスである必要があります (相対パスは使用できません)。ZNode の名前には Unicode 文字を含めることができます。
例: /app/config/db/url は有効な ZNode パスです。
)
各 ZNode には状態構造 (stat) があり、作成時刻、変更時刻、バージョン番号、子ノードの数などの ZNode のメタデータ情報を記録するために使用されます。ZooKeeper クライアントは、この情報を使用して ZNode の変更を判断し、対応する操作を行うことができます。
ZooKeeper は、永続的と一時的な ZNode の 2 種類の ZNode をサポートします。
- 永続的な ZNode は、作成後、削除または名前変更されるまで ZooKeeper 内に存在することを意味します。
- 一時 ZNode は、作成後の一定期間 ZooKeeper 内にのみ存在することになっており、作成されたセッションが終了すると自動的に削除されます。
1.3 セッション管理
ZooKeeper とクライアントの間でセッションが維持され、クライアントはセッションを有効に保つために定期的にハートビート パケット (ping) をサーバーに送信する必要があります。指定された時間を超えてクライアントがサーバーとの接続を失った場合、セッションは無効になり、クライアントによって作成または保持された一時ノードも削除されます。一時ノードは、それを作成または保持したクライアントの有効期間内にのみ存在し、クライアントが切断されると自動的に消える特別なタイプのノードです。一時ノードは子ノードを持つことができません。
ZooKeeper セッションは、クライアントとサーバー間の接続状態を指し、クライアントの要求と応答の一貫性を確保するために使用されます。各 ZooKeeper セッションには、一意の 64 ビット識別子 (セッション ID) とタイムアウト (セッション タイムアウト) があります。
- セッション ID は、クライアントが初めて接続するときにサーバーによってクライアントに割り当てられ、クライアントを識別するために使用されます。クライアントが切断して別のサーバーに再接続すると、前のセッションを継続するために独自のセッション ID を新しいサーバーに送信します。
- セッション タイムアウトは、ZooKeeper インスタンスの作成時にクライアントによって指定され、クライアントがハートビート パケットを送信しなかった時間が無効であるとみなされるまでの時間をサーバーに伝えるために使用されます。サーバーがセッション タイムアウト時間内にクライアントからメッセージを受信しない場合、サーバーはクライアントが切断されたと見なし、関連する一時 ZNode イベントとリスナー イベントを削除します。
ZooKeeper クライアントは、自身のセッション状態を維持するために、サーバーにハートビート パケット (ping) を定期的に送信する必要があります。ネットワーク障害またはその他の理由により、クライアントが現在のサーバーと通信できない場合、クライアントは他の利用可能なサーバーに接続して、独自のセッション状態を復元しようとします。
ZooKeeper クライアントは Watcher を登録することで ZNode の変更イベントを監視し、イベント発生時に通知を受け取ることができます。Watcher は 1 回のみトリガーできるため、クライアントが ZNode の変更を継続的に監視したい場合は、Watcher を再登録する必要があります。Watcher の登録とトリガーは ZooKeeper セッションで行われ、ZooKeeper セッションが失敗した場合、Watcher の登録とトリガーは無効になります。
1.4 一貫性の保証
ZooKeeper は、複数のサーバー ノード間でデータを同期し、クライアントが最新のデータにアクセスできるようにする必要がある分散調整サービスです。この目標を達成するために、ZooKeeper は次の一貫性保証を提供します。
- アトミック性: すべての更新操作 (ZNode の作成、削除、変更) はアトミックであり、成功または失敗します。
- 順序: 各更新操作にはグローバルに一意のトランザクション番号 (zxid) があり、クラスター全体での操作の順序を示すために使用されます。クライアントはzxidを通じてZNodeの変更を判断できます。
- 単一システム イメージ: クライアントがどのサーバー ノードに接続しても、同じビューのデータが表示されます。
- 信頼性: 更新操作が正常に完了すると、その操作はすべてのサーバー ノードに保持され、取り消されません。
- 適時性: クライアントは限られた時間内に最新のデータにアクセスできます。
ZooKeeper の一貫性保証は、リーダー選出と ZAB プロトコルに基づいています。リーダーの選出とは、クラスター内のサーバー ノードが投票メカニズムを通じてリーダー ノードを選択することを意味します。このリーダー ノードは、すべての書き込みリクエストを処理し、他のノードのステータスを調整する役割を果たします。ZAB プロトコルは、リーダー ノードとフォロワー ノードの間でデータを送信および同期するために使用される ZooKeeper アトミック ブロードキャスト プロトコルを指します。
ZooKeeper の一貫性保証は強一貫性 (Strong Consistency) ではなく、逐次一貫性 (Sequential Consistency) です。これは、異なるクライアントには異なるバージョンのデータが表示される可能性がありますが、変更は同じ順序で表示されることを意味します。これにより、読み取り効率と耐障害性が向上しますが、スプリット ブレインなどの問題が発生する可能性もあります。したがって、ZooKeeper を使用する場合は、これらの問題を回避するように注意し、タイムアウトや再試行回数などのパラメーターを適切に設定する必要があります。
スプリット ブレイン現象とは、ネットワークの分割により ZooKeeper クラスター内のノードが 2 つ以上のサブセットに分割され、各サブセットがリーダー ノードを生成し、その結果データの不整合が生じる可能性があるという事実を指します。
ZooKeeper は、クォーラム アルゴリズムを使用してスプリット ブレイン現象を防止します。クォーラム アルゴリズムの基本的な考え方は、リーダーの選出と状態変更のブロードキャストを実行するために、クラスター内のほとんどのノード (半分以上) が通信して正常に動作できることを要求することです。したがって、同点を避けるために、ZooKeeper クラスター内のノードの数は奇数である必要があります。特定のサブセット内のノードの数がクォーラム条件を満たさない場合、リーダー ノードを生成することも、クライアント リクエストを処理することもできません。ソース コードを参照してください。org.apache.zookeeper.server.quorum.flexible.QuorumMaj.java
private int half;
public QuorumMaj(Map<Long, QuorumServer> allMembers) {
this.allMembers = allMembers;
for (QuorumServer qs : allMembers.values()) {
if (qs.type == LearnerType.PARTICIPANT) {
votingMembers.put(Long.valueOf(qs.id), qs);
} else {
observingMembers.put(Long.valueOf(qs.id), qs);
}
}
half = votingMembers.size() / 2;
}
public boolean containsQuorum(Set<Long> ackSet) {
return (ackSet.size() > half);
}
ZooKeeper は次の 3 つの順序を保証します。
- グローバル順序: すべてのサーバーは同じリクエストを同じ順序で実行します。
- 因果的順序付け: イベント A が別のイベント B の前に発生した場合、B を監視するクライアントは A も監視する必要があります。
- クライアントの順序: 同じクライアントによって送信されたリクエストの場合、どのサーバーもクライアントがリクエストを送信した順序でリクエストを実行します。
二、Java API
ZooKeeper によって提供される一般的な Java API (ノードの作成、読み取り、更新、削除、ノードの変更の監視、非同期操作など)。
2.1 基本ノードの CRUD
ZooKeeper のデータはツリー構造で保存され、各ノードは ZNode と呼ばれます。ZNode は一部のメタデータとユーザー データを保存でき、複数の子ノードを持つことができます。ZNode には、バージョン番号、タイムスタンプ、アクセス制御リストなどのいくつかの属性もあります。
ZooKeeper は、ZNode を作成、読み取り、更新、削除するためのいくつかの基本操作 (CRUD) を提供します。これらの操作はアトミックであり、成功するか失敗するかのいずれかです。
- 作成: 新しい ZNode を作成し、そのパス、データ、およびアクセス制御リストを指定します。一時ノードを作成するか順次ノードを作成するかを選択できます。
- 読み取り: 既存の ZNode のデータと属性を読み取ります。この ZNode の変更イベントを監視するために Watcher を登録するかどうかを選択できます。
- 更新: 既存の ZNode のデータまたは ACL を更新します。予想されるバージョン番号を指定する必要があります。現在のバージョン番号と一致しない場合、更新は失敗します。
- 削除: 既存の ZNode とそのすべての子ノードを削除します。予想されるバージョン番号を指定する必要があります。それが現在のバージョン番号と一致しない場合、削除は失敗します。
ZooKeeper クライアントは、Java、Python、C++ などのさまざまな言語とフレームワークを使用して、これらの基本操作を呼び出すことができます。以下は、Java クライアントを使用した基本ノード CRUD のサンプル コードです。
Watcher watcher = event -> System.out.println("Watch event: " + event);
// 创建一个 ZooKeeper 实例并连接到服务器,指定 Watcher 和超时时间
ZooKeeper zk = new ZooKeeper("localhost:2181", 3000, watcher);
// 检查父节点是否存在,如果不存在就先创建父节点
Stat stat = zk.exists("/test", false);
if (stat == null) {
String parent = zk.create("/test", "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("创建父节点成功:" + parent);
}
// 创建一个新的 ZNode /test/node123,并设置其数据为 hello,
String path = zk.create("/test/node123", "hello".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("Created node: " + path);
// 读取 /test/node2 的数据和属性,并注册 Watcher 监听其变化事件
stat = new Stat();
byte[] data = zk.getData("/test/node123", watcher, stat);
System.out.println("Get node data: " + new String(data));
System.out.println("Get node stat: " + stat);
// 更新 /test/node2 的数据为 world,并指定期望的版本号为 -1 (表示忽略版本检查)
stat = zk.setData("/test/node123", "world".getBytes(), -1);
System.out.println("Set node stat: " + stat);
// 删除 /test/node2 及其所有子节点,并指定期望的版本号为 -1 (表示忽略版本检查)
zk.delete("/test/node123", -1);
System.out.println("Delete node success");
// 关闭 ZooKeeper 连接
zk.close();
2.2 ノードの変更を監視する
ZooKeeper は、クライアントが ZNode 変更イベントを監視するために Watcher と呼ばれるメカニズムを提供します。ZNode のデータまたは子ノードが変更されると、ZooKeeper は Watcher に登録されているクライアントに通知を送信します。
ウォッチャーは、ZNode データや子ノードのリストを読み取るとき、または ZNode の存在を確認するときに登録できます。Watcher は 1 回のみトリガーできるため、クライアントが ZNode の変更を継続的に監視したい場合は、Watcher を再登録する必要があります。
ウォッチャーにはいくつかの種類があります。
- NodeCreated: 存在しない ZNode が作成されるとトリガーされます。
- NodeDeleted: 既存の ZNode が削除されるとトリガーされます。
- NodeDataChanged: 既存の ZNode のデータが変更されるとトリガーされます。
- NodeChildrenChanged: 既存の ZNode の子ノード リストが変更されるとトリガーされます。
Watcher には次の機能もあります。
- クライアントは、同じ ZNode または異なる ZNode を監視するように複数の Watcher を設定できます。
- ZooKeeper は、クライアントが最初に Watcher イベントを確認するまで、Watcher を設定したノードへの変更がクライアントに表示されないことを保証します。
- ZooKeeper は、ネットワークの遅延やその他の要因によってイベントが失われる可能性があるため、すべてのクライアントがすべての Watcher イベントを受信することを保証しません。
- ZooKeeper は、Watcher イベントの順序が実際の変更の順序と一致していることを保証しません。これは、順序が狂う原因となる同時更新やその他の要因が存在する可能性があるためです。
以下は、Java API を使用してノードの変更を監視するサンプル コードです。
// 创建一个 ZooKeeper 实例并连接到服务器,指定超时时间
ZooKeeper zk = new ZooKeeper("localhost:2181", 3000, null);
// 创建一个 Watcher 实例,用于监听 /test/node3 的变化事件
Watcher watcher = new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println("Watch event: " + event);
// 如果是 NodeCreated 或 NodeDataChanged 类型,则重新读取数据并注册新的 Watcher
if (event.getType() == Event.EventType.NodeCreated || event.getType() == Event.EventType.NodeDataChanged) {
try {
byte[] data = zk.getData("/test/node3", this, null);
System.out.println("New data: " + new String(data));
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
// 检查父节点是否存在,如果不存在就先创建父节点
Stat stat = zk.exists("/test", false);
if (stat == null) {
String parent = zk.create("/test", "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("创建父节点成功:" + parent);
}
// 检查 /test/node3 是否存在,并注册 Watcher 监听其创建和修改事件
stat = zk.exists("/test/node3", watcher);
System.out.println("Check node stat: " + stat);
// 创建 /test/node3,并设置其数据为 hello
String path = zk.create("/test/node3", "hello".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("Created node: " + path);
// 更新 /test/node3 的数据为 world
stat = zk.setData("/test/node3", "world".getBytes(), -1);
System.out.println("Set node stat: " + stat);
// 关闭 ZooKeeper 连接
zk.close();
2.3 非同期操作
ZooKeeper Java API は、メインスレッドをブロックせずに一部の操作を実行し、コールバック関数を通じて操作結果を取得するための非同期メソッドをいくつか提供します。非同期メソッドは通常、async で終了し、コールバック関数として StringCallback または DataCallback パラメーターを渡す必要があります。例えば:
// 创建一个 ZooKeeper 实例并连接到服务器,指定超时时间
ZooKeeper zk = new ZooKeeper("localhost:2181", 3000, null);
// 检查父节点是否存在,如果不存在就先创建父节点
Stat stat = zk.exists("/app", false);
if (stat == null) {
String parent = zk.create("/app", "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("创建父节点成功:" + parent);
}
// 异步创建 /app/config 节点,并设置数据为 "hello"
zk.create("/app/config", "hello".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, (rc, path, ctx, name) -> {
// 处理创建结果
System.out.println("Create result: " + rc);
System.out.println("Create path: " + path);
System.out.println("Create name: " + name);
}, null);
// 异步读取 /app/config 节点的数据和状态
zk.getData("/app/config", false, (rc, path, ctx, data, state) -> {
// 处理读取结果
System.out.println("Get result: " + rc);
System.out.println("Get path: " + path);
System.out.println("Get data: " + new String(data));
System.out.println("Get stat: " + state);
}, null);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
2.4 セッション管理
ZooKeeper のセッション管理は、主に SessionTracker を通じて行われます。SessionTracker は、管理にバケット化戦略 (同じブロック内の類似したセッションを管理する) を使用するため、ZooKeeper は異なるブロック内のセッションを分離し、同じブロックの統合処理を行うことができます。
ZooKeeper のデフォルトの外部サービス ポートは 2181 です。クライアントが起動すると、最初にサーバーとの TCP 接続が確立されます。最初の接続確立から、クライアント セッションのライフ サイクルも始まります。この接続を通じて、クライアントはハートビート検出を通じてサーバーとの有効なセッションを維持でき、ZooKeeper サーバーにリクエストを送信して応答を受信したり、サーバーから最新のデータ変更通知を取得したりすることもできます。
ZooKeeper セッションには、一時的なセッションと永続的なセッションの 2 種類があります。存続期間の短いセッションは、クライアントが切断されるかタイムアウトになると自動的に終了しますが、永続的なセッションでは、クライアントが明示的にセッションを閉じるか削除する必要があります。有効期間の短いセッションは分散ロックやマスター選出などの機能を実装するために使用でき、永続セッションはデータのパブリッシュ/サブスクライブやネーミング サービスなどの機能を実装するために使用できます。
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
// 定义一个类实现Watcher接口,用于监听会话状态和节点变化
public class EphemeralSessionExample implements Watcher {
// 定义一个ZooKeeper对象,用于连接和操作ZooKeeper服务器
private ZooKeeper zk;
// 定义一个构造方法,传入ZooKeeper服务器的地址和端口,并创建连接
public EphemeralSessionExample(String hostPort) throws Exception {
zk = new ZooKeeper(hostPort, 3000, this); // 设置会话超时时间为3秒
}
// 定义一个方法,用于创建或更新一个短暂节点,并设置数据和权限
public void createOrUpdate(String path, byte[] data) throws Exception {
Stat stat = zk.exists(path, false); // 检查znode是否存在,不设置监听器
if (stat == null) {
// 如果不存在,则创建znode
zk.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); // 创建短暂的znode,并设置数据和权限
} else {
// 如果存在,则更新znode的数据
zk.setData(path, data, -1); // 更新znode的数据,并忽略版本号
}
}
// 定义一个方法,用于获取并打印当前会话的ID和状态
public void printSessionInfo() {
long sessionId = zk.getSessionId(); // 获取会话ID
String sessionState = zk.getState().toString(); // 获取会话状态
System.out.println("The session ID is: " + sessionId); // 打印会话ID
System.out.println("The session state is: " + sessionState); // 打印会话状态
}
// 实现Watcher接口中的process方法,用于处理事件通知
@Override
public void process(WatchedEvent event) {
System.out.println("Received a watch event: " + event); // 打印事件信息
if (event.getType() == Event.EventType.None && event.getState() == Event.KeeperState.SyncConnected) {
System.out.println("The client is connected to the server"); // 如果是连接成功的事件,打印连接信息
} else if (event.getType() == Event.EventType.None && event.getState() == Event.KeeperState.Expired) {
System.out.println("The session is expired"); // 如果是会话过期的事件,打印过期信息
try {
zk.close(); // 关闭ZooKeeper对象
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 定义一个主方法,用于测试短暂会话的创建和更新
public static void main(String[] args) throws Exception {
EphemeralSessionExample ese = new EphemeralSessionExample("localhost:2181"); // 创建一个EphemeralSessionExample对象,并连接到本地的ZooKeeper服务器
ese.printSessionInfo(); // 打印当前会话的信息
ese.createOrUpdate("/test", "Hello".getBytes()); // 创建或更新一个短暂节点,并设置数据为"Hello"
Thread.sleep(5000); // 睡眠5秒,模拟客户端断开连接
ese.printSessionInfo(); // 打印当前会话的信息,此时应该是过期状态
}
}
3. 適用シナリオと事例分析
3.1 メリットとデメリット
アドバンテージ
- シンプルな分散調整プロセス: ZooKeeper は、データとメタデータを保存できるツリー構造の znode ノードに基づくシンプルなデータ モデルを提供し、一時ノード、順次ノード、その他のタイプをサポートします。ZooKeeper は、作成、削除、読み取り、書き込みなどのいくつかの基本的な操作と、リスナー、サブスクライバーなどのいくつかの高度な機能も提供します。
- 高度に同期された作業モード: ZooKeeper は、すべてのノード間のデータの一貫性を保証し、ZAB プロトコルを介してリーダーの選択とトランザクション ログのレプリケーションを実装します。ZooKeeper は、サーバー プロセス間の相互排他と連携もサポートします。
- 多様な分散シナリオ アプリケーション: ZooKeeper を使用して、構成管理、ネーミング サービス、クラスター管理、ロード バランシング、ロック サービス、キュー サービスなどのさまざまな分散シナリオで機能とサービスを実装できます。
- 成熟したオープンソース コミュニティとエコシステム: ZooKeeper は、幅広いユーザー ベースと活発な開発者コミュニティを備えた Apache のトップ プロジェクトの 1 つです。ZooKeeper には、分散 ZooKeeper モードの多くの実装を提供する Curator (Java ライブラリ) など、ZooKeeper に基づいて開発された多くのフレームワークとライブラリもあります。
欠点がある
- 非効率的な書き込みパフォーマンス: ZooKeeper はすべてのノード間で強い一貫性を確保する必要があるため、各書き込み操作はリーダー ノードによって確認され、すべてのフォロワー ノードにコピーされる必要があります。これにより、書き込み操作に待ち時間とオーバーヘッドが追加され、スループットが低下します。
- ストレージ容量の制限: ZooKeeper データはメモリに保存されるため、メモリのサイズによって制限されます。データ量が多すぎる場合やノード数が多すぎる場合は、メモリ不足や過剰な GC プレッシャーが発生する可能性があります。
- 動的拡張機能の欠如: ZooKeeper はすべてのノード間の状態情報を維持し、リーダー選出やトランザクション ログ レプリケーションなどのメカニズムの正常な動作を保証する必要があるため、ノードの数を任意に増減することはできません。クラスターのサイズを拡張または縮小する必要がある場合は、手動操作が必要となり、クラスターの安定性に影響を与える可能性があります。
3.2 適用可能なシナリオ分析
- データパブリッシュ/サブスクライブ:構成情報の一元管理と動的な更新を実現する構成センターとして使用できます。パブリッシャーは ZooKeeper ノードにデータを公開し、サブスクライバーはリスナーを通じてデータの変更を取得することで、データの同期と共有を実現します。
- 負荷分散: サービスの検出と負荷分散を実現するためのサービス レジストリとして使用できます。サービスプロバイダーは自身のアドレス情報を ZooKeeper ノードに登録し、サービスコンシューマーは ZooKeeper を通じて利用可能なサービスのリストを取得し、特定の戦略に従って適切なサービスプロバイダーを選択します。
- ネーミングサービス:分散システムにおける固有識別子の生成・管理を実現するネーミングサービスとして利用できます。ZooKeeper のシーケンシャル ノード機能を使用すると、各ノードがグローバルに一意で順序付けられた名前を持つことが保証されます。
- 分散調整/通知: 分散調整/通知メカニズムを実装して、分散システム内のコンポーネント間のコラボレーションと状態同期を実現できます。ZooKeeperのテンポラリノード機能を利用して障害の検出や復旧を実現したり、ZooKeeperのリスナー機能を利用してイベントドリブンやメッセージ通知を実現したりできます。
- クラスター管理: クラスター内の各ノードを監視および管理するためのクラスター管理ツールとして使用できます。ZooKeeper のメタデータ ストレージ機能を使用すると、クラスター内の各ノードの状態情報を保存でき、ZooKeeper の ZAB プロトコル機能を使用すると、クラスター内の各ノード間のデータの一貫性を確保できます。
- マスター選出: マスター選出メカニズムは、分散システムにおけるマスター/スレーブ モードでのマスター ノードの選択と切り替えを実現します。ZooKeeper の一時シーケンシャル ノード機能を使用すると、複数の候補のうち 1 つだけがマスター ノードになれることが保証され、マスター ノードに障害が発生した場合には再選出が実行されます。
- 分散ロック: 分散システム内の共有リソースへの相互排他的アクセスを実現する分散ロック サービスとして使用できます。ZooKeeper の一時シーケンシャル ノード機能を使用すると、複数のリクエスタのうち 1 つだけがロックを取得できることが保証され、ロックを保持しているリクエスタが解放または切断されるとロックが解放されます。
- 分散キュー: 分散システムでタスクやメッセージの先入れ先出し (FIFO) または優先処理を実装するための分散キュー サービスとして使用できます。ZooKeeper のシーケンシャル ノード機能を使用すると、キュー内の要素が挿入順または優先順に配置されていることを確認し、同時挿入または削除操作をサポートできます。
3.3 実際のケース
- データのパブリッシュ/サブスクライブ: Hadoop、HBase、Kafkaなどの分散システムはすべて、構成センターとして ZooKeeper を使用し、構成情報の一元管理と動的な更新を実現します。たとえば、HBase の HMaster ノードは、クラスターのメタデータ情報 (RegionServer リスト、リージョン分布など) を ZooKeeper ノードに保存し、クライアントおよび他の RegionServer ノードは、ZooKeeper を通じてこの情報をタイムリーに取得できます。更新します。
- 負荷分散: Dubbo や Spring Cloudなどのマイクロサービス フレームワークはすべて、サービス レジストリとして ZooKeeper を使用して、サービスの検出と負荷分散を実現します。たとえば、Dubbo のサービスプロバイダーは ZooKeeper ノードにアドレス情報を登録し、サービス消費者は ZooKeeper を通じて利用可能なサービスのリストを取得し、特定の戦略 (ランダム、ポーリング、最もアクティブでないなど) に従って適切なサービスを選択します。プロバイダー。
- ネーミングサービス: SolrCloud は、ZooKeeper をネーミングサービスとして使用し、分散システム内での一意の識別子の生成と管理を実現する分散検索プラットフォームです。たとえば、SolrCloud の各コレクションには一意の名前があり、各シャードには一意の番号があり、各レプリカには一意の ID があります。これらの名前、番号、ID はすべて、ZooKeeper のシーケンシャル ノード機能を通じて生成され、保存されます。
- 分散調整/通知: Storm は、ZooKeeper を分散調整/通知メカニズムとして使用し、分散システム内のコンポーネント間のコラボレーションと状態同期を実現する分散リアルタイム コンピューティング フレームワークです。たとえば、Storm の Nimbus ノードはクラスター全体の管理と複数のスーパーバイザー ノードへのタスクの割り当てを担当し、スーパーバイザー ノードはワーカー プロセスの開始と停止を担当し、ワーカー プロセスは特定のコンピューティング タスクの実行を担当します。これらのノードは、ZooKeeper を通じて通信し、調整する必要があります。
- クラスター管理: Redis は、マスター/スレーブ レプリケーションとセンチネル モードをサポートし、高可用性を実現する高性能メモリ データベースです。Redis は、クラスター内の各ノードを監視および管理するクラスター管理ツールとして ZooKeeper を使用します。たとえば、センチネル モードでは、センチネル ノードは自身を ZooKeeper の一時ノードに登録し、定期的にハートビートを送信します。マスター ノードに障害が発生すると、センチネル ノードは ZooKeeper を通じてリーダー選出を実行し、他のセンチネル ノードにスレーブ ノードを切り替えるように通知します。マスターノードとして。