Zookeeper chap03

  • 483 views
Uploaded on

 

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
483
On Slideshare
0
From Embeds
0
Number of Embeds
1

Actions

Shares
Downloads
10
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. ZooKeeper 第3章 Getting Started with the ZooKeeper API 社内勉強会資料 大槌 剛彦 (@ohtsuchi)
  • 2. 第3章の内容 •Setting the ZooKeeper CLASSPATH •Bin/zkEnv.sh 実行 で CLASSPATH 環境変数の設定 •Creating a ZooKeeper Session •Implementing a Watcher •Running the Watcher Example •Getting Mastership •Getting Mastership Asynchronously •Setting Up Metadata •Registering Workers •Queuing Tasks 第2章の Implementation of a Master-Worker Example のjava APIによる基本的な実装例 3つの役割: ・Master ・Worker ・Client •The Admin Client •zkCli の代わりに システムのstateを調べるシンプルなadminクライアントの例 •Takeaway Messages •API はzkCliで使用するコマンドと一致するアプリを書ける • errorをhandleする処理が必要 • 特に ConnectionLossException • 非同期APIはパフォーマンスの利益をもたらし、errorリカバリをsimpleにできる
  • 3. Creating a ZooKeeper Session • ZooKeeper client library – ZooKeeper handleを構築する • ZooKeeper handle = ZooKeeper サーバとのsession を表す – serverとのconnectionが切れた場合は別のserverにmigrateする – sessionが有効な状態を保てるように、継続的にアクティブな接続を維持し ようとする。 – handleをclose → serverにsessionをkillするように伝える
  • 4. Creating a ZooKeeper Session • ZooKeeper handleを作るためのコンストラクタ ZooKeeper( String connectString, int sessionTimeout, Watcher watcher) – connectString • ZooKeeper server のホスト名・ポート – sessionTimeout • ミリ秒で指定 – watcher • eventの発生をclient applicationに通知する • sessionの状態や、ZooKeeper dataの変更をmonitor
  • 5. Implementing a Watcher public interface Watcher { void process(WatchedEvent event); } • Master.javaの実装例 public class Master implements Watcher { … Master(String hostPort) { this.hostPort = hostPort; 1 } void startZK() { zk = new ZooKeeper(hostPort, 15000, this); 2 } public void process(WatchedEvent e) { System.out.println(e); 3 } public static void main(String args[]) … { Master m = new Master(args[0]); m.startZK(); 4 // wait for a bit Thread.sleep(60000);
  • 6. Implementing a Watcher • Master.javaの実行ログ ... - INFO [...] - Client environment:zookeeper.version=3.4.5-1392090, ... 1 ... ... - INFO [...] - Initiating client connection, connectString=127.0.0.1:2181 ... 2 ... - INFO [...] - Opening socket connection to server localhost/127.0.0.1:2181. ... ... - INFO [...] - Socket connection established to localhost/127.0.0.1:2181, initiating session ... - INFO [...] - Session establishment complete on server localhost/127.0.0.1:2181, ... 3 WatchedEvent state:SyncConnected type:None path:null 4 – ①~③はlog4jでの出力 – ④はWatcher#process の出力
  • 7. Running the Watcher Example • Disconnected event – 再接続のために新しくZooKeeper handleを作ってはいけない! – client libraryが対応してくれる • ネットワーク障害 や • サーバ障害 にも対応 • 3台中1台のserverが死んでもサービスは停止しない • ZooKeeper Manages Connections – 自分でconnectionを制御しようとしてはいけない – client library はconnectionをモニタし、障害を通知するだけでなく、再接続も行い、 中断を最小限に抑える – 不必要に新しいsessionを作ろうとすると、loadが増えて、より長い停止をもたらす
  • 8. Running the Watcher Example • two main management interfaces ( 詳細は第10章で) – JMX – four-letter words • stat • dump • Statの出力例(telnetで接続してから実行) stat ZooKeeper version: 3.4.5-1392090, built on 09/30/2012 17:52 GMT Clients: /127.0.0.1:39470[1](queued=0,recved=3,sent=3) /127.0.0.1:39471[0](queued=0,recved=1,sent=0) Latency min/avg/max: 0/5/48 Received: 34 Sent: 33 Connections: 2 Outstanding: 0 Zxid: 0x17 Mode: standalone … – この例では2つのクライアントが接続 (1) Master.java (2)このtelnet接続自身
  • 9. Running the Watcher Example • dumpの出力例(telnetで接続してから実行) dump SessionTracker dump: Session Sets (3): 0 expire at Wed Nov 28 20:34:00 PST 2012: 0 expire at Wed Nov 28 20:34:02 PST 2012: 1 expire at Wed Nov 28 20:34:04 PST 2012: 0x13b4a4d22070006 ephemeral nodes dump: Sessions with Ephemerals (0): – – 1つの active session (Master.javaのsession) expiration time は ZooKeeperのコンストラクタで指定したsession timeout が元 – Master.javaをkill して dumpで繰り返しactive sessionを観察 » → sessionが終了するまではしばらく時間がかかることがわかる » → 指定したsession timeout が過ぎるまで server側はsessionをkillしないため – 処理が終了した時に直ちにsessionが消えるのは良い » Master.java の終了処理に、ZooKeeper#close 呼び出しを追加
  • 10. The Master Role (補足:2章の復習) • 1つのプロセスのみがmaster – /master znodeを作成できたクライアントがactive masterとなる – /master znodeを作成できなかったノードは backup masterとなり /masterをwatch. – active masterのsessionがclose または expired • /master znodeがなくなる(ephemeral znodeなので) • → backup master が/master znodeを作成してactive masterとなる – → create -e /master "master1…" master1 ephemeral create -e /master “master2…“ (失敗) – stat /master true stat /master true watch master2
  • 11. Getting Mastership (znodeのcreate) • "/master" znodeを作成するのに2つ必要 – initial data • 例では、 random server ID を使用 – アクセス制御リスト (ACL) • 信頼できる環境内では、よくopen ACLが使用される • 例では、ZooDefs.Ids.OPEN_ACL_UNSAFE を使用 – 名前が示すとおり、安全ではない void runForMaster() { zk.create("/master", 1 serverId.getBytes(), 2 OPEN_ACL_UNSAFE, 3 CreateMode.EPHEMERAL); 4 } – – – – ① “/master” znodeが既に存在していれば失敗する ② znodeに storeするdata (型=byte[]) ③ open ACL ④ ephemeral znodeで作成
  • 12. Getting Mastership (例外処理) • ZooKeeper#create は2つの例外をthrowする – KeeperException – InterruptedException • 特に以下の例外は対処する必要あり (createが成功している可能性もある) – ConnectionLossException(KeeperExceptionのサブクラス) • ネットワークエラー時など • 以下の事がクライアント側で不明 – request がserver に届いたかどうか – request がserver で処理されたけれど responseを受け取れていないかどうか • → Requestが処理されたかどうか、再requestする必要があるかどうか、要調査 – InterruptedException • client threadでThread.interruptが呼ばれた場合 • アプリのshutdown時など
  • 13. Getting Mastership (例外処理: getDataで確認) • 例外発生時に、システムの状態を確認する必要がある – もし自分のcreateが成功していたら、他のノードはmasterになれていない – どのノードもmasterとして活動しないことになる • どのprocessが“/master” znodeを作成したのかを調べる – ConnectionLossException 対応時 – ZooKeeper#getData を使用 byte[] getData(String path, bool watch, Stat stat) • path – dataを取得するznodeのpath • watch – true: ZooKeeper handle作成時(ZooKeeperのコンストラクタ)に、引数で渡した Watcher objectを介して eventを取得する – ※次の例では、現在の data を知りたいだけなので falseをセット • stat – getData メソッド内で、znode のmetadataがセットされる • 戻り値 – znode のdata
  • 14. Getting Mastership (例外処理: getDataで確認) // “/master”を作成したノードが存在すれば true boolean checkMaster() { while (true) { try { Stat stat = new Stat(); byte data[] = zk.getData("/master", false, stat); 1 isLeader = new String(data).equals(serverId)); 2 return true; } catch (NoNodeException e) { // no master, so try create again return false; } catch (ConnectionLossException e) { } } } – ① /master znodeのdataをget – ② /master znodeのdataは masterになったノードのサーバidが入っている(createの第2引数) • 自分自身のserverIdと一致していれば自分がmaster(isLeader = true)
  • 15. Getting Mastership (例外処理: getDataで確認) void runForMaster() throws InterruptedException { 3 while (true) { try { 4 zk.create("/master", serverId.getBytes(), OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); 5 isLeader = true; break; } catch (NodeExistsException e) { isLeader = false; break; } catch (ConnectionLossException e) { 6 } if (checkMaster()) break; 7 } }
  • 16. Getting Mastership Asynchronously (createメソッド) • 全ての同期呼び出しに対応する非同期呼び出しが存在する • 非同期のZooKeeper#create void create(String path, byte[] data, List<ACL> acl, CreateMode createMode, AsyncCallback.StringCallback cb, 1 Object ctx) 2 – 同期版との相違 • 引数が2つ多い – ① Callback関数を持つオブジェクト – ② Callback関数内にデータを渡したい時に使用するユーザ定義オブジェクト • Exceptionをthrowしない – Errorはコード化されて、Callback関数の第1引数に渡される
  • 17. Getting Mastership Asynchronously (Callback) • StringCallback が定義するメソッド void processResult(int rc, String path, Object ctx, String name) – rc • OK または例外発生を表すコード – path – ctx • ZooKeeper#create で引数に渡されたObject – name • znode の名前 • znode の create が成功した場合、pathとnameは一致する – create時に CreateMode.SEQUENTIAL を指定した場合は一致しない • Callback Processing – – single thread で全てのcallbackを処理するため、1つのcallbackがblockされると、それに続く全てのcallback もblockされてしまう → callback の中で 集中的な操作やblockする操作をするべきではない
  • 18. Getting Mastership Asynchronously (create) static StringCallback masterCreateCallback = new StringCallback() { void processResult(int rc, String path, Object ctx, String name) { switch(Code.get(rc)) { 1 case CONNECTIONLOSS: 2 checkMaster(); return; case OK: 3 isLeader = true; break; default: 4 isLeader = false; } System.out.println("I'm " + (isLeader ? "" : "not ") + "the leader"); } }; void runForMaster() { zk.create("/master", serverId.getBytes(), OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, masterCreateCallback, null); 5 – ① createの結果を 第1引数:rc で取得. Enumに変換 – ② CONNECTIONLOSS時は非同期版checkMasterメソッド(次頁で解説)を呼び出してシス テムの状態をチェック
  • 19. Getting Mastership Asynchronously (getData) • #getData終了 → DataCallback DataCallback masterCheckCallback = new DataCallback() { void processResult(int rc, String path, Object ctx, byte[] data, Stat stat) { switch(Code.get(rc)) { case CONNECTIONLOSS: checkMaster(); return; case NONODE: runForMaster(); return; } } } void checkMaster() { zk.getData("/master", false, masterCheckCallback, null); } • 同期版と非同期版のメソッドの違い – While loop が無い – try-cathで囲まずに、エラー処理はCallback側で
  • 20. Workers, Tasks, and Assignments (補足:2章の復習) • 3つのparent znodeを、一番最初(taskのassignが始まる前:bootstrap処理など)に 作っておく必要 (persistent znode) (contain no data) – /workers – /tasks – /assign • masterは /workers と /tasks を watch ls /workers true ls /tasks true watch 事前に作成 master1
  • 21. Setting Up Metadata public void bootstrap() { createParent("/workers", new byte[0]); createParent(“/assign”, new byte[0]); createParent(“/tasks”, new byte[0]); createParent(“/status”, new byte[0]); } 1 Void createParent(String path, byte[] data) { } zk.create(path, data, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, createParentCallback, data); 2 StringCallback createParentCallback = new StringCallback() { public void processResult(int rc, String path, Object ctx, String name) { …(次頁へ続く) – ①dataは空で作成するので byte[0] – ②第2引数は 作成されるznodeに書き込まれるdata. 第4 第6引数のdataは Callbackに渡すObject
  • 22. Setting Up Metadata StringCallback createParentCallback = new StringCallback() { public void processResult(int rc, String path, Object ctx, String name) { switch (Code.get(rc)) { 3 case CONNECTIONLOSS: createParent(path, (byte[]) ctx ); break; case OK: LOG.info("Parent created"); break; case NODEEXISTS: LOG.warn("Parent already registered: " + path); break; default: LOG.error("Something went wrong: ", KeeperException.create(Code.get(rc), path)); } } }; – ③ CONNECTIONLOSS発生時:simply retry the create • #createPathメソッドを呼び出し – 引数の ctx をそのまま#createPathメソッドの引数にセット
  • 23. The Worker Role (補足:2章の復習) • Worker は taskを実行できる事を masterに伝える – /workers 以下にephemeral znodeを作る • pathに自分のhostnameを使用 • → master は NodeChildrenChanged を観察する • Worker は taskのassignを受け取るためのznodeを作る – /assign 以下にznodeを作り、watchする master1 watch create -e /workers/worker1 "worker1:2224" ephemeral create /assign/worker1 "" ls /assign/worker1 true watch worker1
  • 24. Registering Workers (ephemeral znodeの作成) • /workers 以下にephemeral znodeを作る (workerが死んだ際には/workers から消えるように) – Workerのstateを示すのに dataを利用する public class Worker implements Watcher { … void register() { zk.create("/workers/worker-" + serverId, "Idle".getBytes(), 1 Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, 2 createWorkerCallback, null); } StringCallback createWorkerCallback = new StringCallback() { public void processResult(int rc, String path, Object ctx, String name) { switch (Code.get(rc)) { case CONNECTIONLOSS: register(); break; 3 case OK: LOG.info("Registered successfully: " + serverId); break; case NODEEXISTS: LOG.warn("Already registered: " + serverId); break; default: LOG.error("Something went wrong: " …
  • 25. Registering Workers (非同期にstatusを更新する例) StatCallback statusUpdateCallback = new StatCallback() { public void processResult(int rc, String path, Object ctx, Stat stat) { switch(Code.get(rc)) { case CONNECTIONLOSS: updateStatus((String)ctx); 1 return; } } }; synchronized private void updateStatus(String status) { if (status == this.status) { 2 zk.setData("/workers/" + name, status.getBytes(), -1, statusUpdateCallback, status); 3 } } public void setStatus(String status) { this.status = status; 4 updateStatus(status); 5 }
  • 26. The Client Role (補足:2章の復習) • Client は /tasks 以下にznodeを作って、 taskを登録する – Sequential • taskの順序 • queueを提供 • masterはnew taskをcheckし、available workerにそのtaskをassignする – “/assign/worker名/task名” にznodeを作成 master1 watch create /assign/worker1/task-0000000000 "" create -s /tasks/task- "cmd" client sequential ls /tasks/task-0000000000 true watch watch worker1
  • 27. The Client Role (補足:2章の復習) • • worker は /tasks 以下にstatus znodeを作って、 taskが完了した事を伝える client は status znodeをチェックしてtaskの完了状態を知る master1 watch watch client create /tasks/task-0000000000/status "done" watch worker1
  • 28. Queuing Tasks • /tasks 以下にznodeを作成する – sequential znode を利用する: 2つの利点 • sequence number が taskがキューで処理される順序を指し示す • sequence number がtaskのためのunique pathを作り出す public class Client implements Watcher { … String queueCommand(String command) throws KeeperException { while (true) { try { String name = zk.create("/tasks/task-", 1 command.getBytes(), OPEN_ACL_UNSAFE, CreateMode.SEQUENTIAL); 2 return name; 3 break; } catch (NodeExistsException e) { throw new Exception(name + " already appears to be running"); } catch (ConnectionLossException e) { 4 } } } … }
  • 29. Queuing Tasks (前頁の続き) • ① 作成するznodeの名前のプレフィックス=“task-” • ② CreateMode.SEQUENTIALを指定 – 単調増加するサフィックスが“task-”の後ろに追加される – 各taskに対して、 unique nameになることが保証される – taskの順序も確立される • ③ #create呼び出し時点では“task-”の後ろのsequence numberが分からない – #createの戻り値で取得 • ④ connectionのloss時 – simply retry the create – create multiple znodes for the task • → 「execute-at-least-once policy」 may work fine – → must do more work » Session ID などのunique IDを使って znodeを作る必要 • /tasks以下はephemeral znodeではないので、Client applicationの終了後も、各znodeは 残る
  • 30. The Admin Client • #getData と #getChildren を使用 – • don’t change the state of the system watch parameter は false を指定 – current state を知りたいだけ public class AdminClient implements Watcher { … void listState() throws KeeperException { try { Stat stat = new Stat(); byte masterData[] = zk.getData("/master", false, stat); 1 Date startDate = new Date(stat.getCtime()); 2 … System.out.println("Workers:"); for (String w: zk.getChildren("/workers", false)) { byte data[] = zk.getData("/workers/" + w, false, null); 3 String state = new String(data); System.out.println("¥t" + w + ": " + state); } System.out.println("Tasks:"); for (String t: zk.getChildren("/assign", false)) { System.out.println("¥t" + t); } } …