{"id":1181,"date":"2024-01-29T11:15:32","date_gmt":"2024-01-29T02:15:32","guid":{"rendered":"https:\/\/skanto.co.kr\/?p=1181"},"modified":"2024-01-29T16:48:57","modified_gmt":"2024-01-29T07:48:57","slug":"zookeeper-%ea%b8%81%ec%a0%81%ec%9d%b4%ea%b8%b0","status":"publish","type":"post","link":"https:\/\/skanto.co.kr\/?p=1181","title":{"rendered":"ZooKeeper \uae01\uc801\uc774\uae30"},"content":{"rendered":"\n<p>ZooKeeper\ub294 MSA\uc5d0\uc11c Scalable\ud55c \uad6c\uc870\ub97c \uc720\uc9c0\uac00\ud788 \uc704\ud574 \ub9ce\uc740 \uc624\ud508\uc18c\uc2a4 \uc194\ub8e8\uc158\uc774 \ub0b4\ubd80\uc801\uc73c\ub85c \ud65c\uc6a9\ud558\ub294 Coordination \uc11c\ube44\uc2a4\ub77c \ud560 \uc218 \uc788\ub2e4. \ub300\ud45c\uc801\uc73c\ub85c Kafka\uc5d0\uc11c \ub0b4\ubd80\uc801\uc73c\ub85c \ud658\uacbd\uc744 \uad00\ub9ac\ud558\uace0 \ud074\ub7ec\uc2a4\ud130\ub97c \uc720\uc9c0\ud558\uae30 \uc704\ud574 \uc0ac\uc6a9\ud558\ub294 \uc815\ub3c4\ub85c \uc54c\uace0 \uc788\ub2e4.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/zookeeper.apache.org\/doc\/current\/images\/zookeeper_small.gif\" alt=\"\"\/><\/figure>\n\n\n\n<p>ZooKeeper\uc758 \ud0c4\uc0dd\uc744 \ubcf4\uba74 \uc57c\ud6c4(Yahoo)\uac00 \uc544\uc9c1\uc740 \uadf8 \uba85\uc131\uc774 \uc0b4\uc544 \uc788\uc744 \ub54c \ud074\ub77c\uc6b0\ub4dc \ud658\uacbd\uc73c\ub85c \uc804\ud658\uacfc \ub9de\ubb3c\ub824 \uc790\uccb4\uc73c\ub85c \ud544\uc694\uc5d0 \uc758\ud574 \ub9cc\ub4e0 \ud6c4 \uc624\ud508\uc18c\uc2a4\ub85c \uacf5\uac1c\ud55c \uc11c\ube44\uc2a4\uc774\ub2e4(2011). \ud074\ub77c\uc6b0\ub4dc\/MSA\ud658\uacbd\uc5d0\uc11c\ub294 \uc548\uc815\uc801\uc778 \uc11c\ube44\uc2a4 \uc6b4\uc601\uc744 \uc704\ud574 Scalable\ud55c \uad6c\uc870\uac00 \ud544\uc218\uc801\uc774\ub2e4. \uc774\ub54c \uc11c\ube44\uc2a4\uc758 \ud2b8\ub798\ud53d \uc0c1\ud669\uc5d0 \ub530\ub77c \ub178\ub4dc \uac1c\uc218\uac00 \ub298\uc5b4\ub098\uac70\ub098 \uc904\uc5b4\ub4e4\uc5b4\uc57c \ud558\uba70 \ubc14\uc774\ub108\ub9ac \uc5c5\ub370\uc774\ud2b8 \ubc30\ud3ec\uc2dc\uc5d0\ub3c4 \ubaa8\ub4e0 \ub178\ub4dc\ub4e4\uc5d0 \ub3d9\uc77c\ud55c \ud658\uacbd\uc774 \uad6c\uc131\ub418\ub3c4\ub85d \ud574\uc57c \ud55c\ub2e4. \uc608\ub97c \ub4e4\uba74 \ud658\uacbd\uc124\uc815 \ud30c\uc77c\uc5d0 \ubcc0\uacbd\uc774 \ubc1c\uc0dd\ud560 \uacbd\uc6b0 \ub3d9\uc77c\ud55c \ud658\uacbd\uc124\uc815\uc744 \ubaa8\ub4e0 \ub178\ub4dc\ub4e4\uc5d0 \ubc30\ud3ec\ud558\ub294 \uac83\uc740 Time Consuming\ud55c \uc791\uc5c5\uc774 \ub420 \uac83\uc774\uba70 \uc774 \uacfc\uc815\uc5d0\uc11c \uc758\ub3c4\uce58 \uc54a\uc740 Human Error\uac00 \ubc1c\uc0dd\ud560 \uc218\ub3c4 \uc788\ub2e4. ZooKeeper\ub85c \ud560 \uc218 \uc788\ub294 \uc791\uc5c5\uc774 \ub9ce\uc774 \uc788\uaca0\uc9c0\ub9cc \uc801\uc5b4\ub3c4 \uc774\ub7f0 \uc791\uc5c5\uc5d0 ZooKeeper\ub97c \uc774\uc6a9\ud558\uc5ec \uc790\ub3d9\ud654\ub97c \ud55c\ub2e4\uba74 \uc5c5\ubb34 \uc0dd\uc0b0\uc131\uc774 \ub192\uc544\uc9c8 \uac83\uc774\ub2e4.<\/p>\n\n\n\n<p>\uc774 \uae00\uc740 \uc774\ub7f0 \uc2dc\ub098\ub9ac\uc624 \uc0c1\ud669\uc5d0\uc11c ZooKeeper\ub85c \uad6c\ud604\ud560 \uc218 \uc788\ub294 \uc544\uc8fc \uae30\ubcf8\uc801\uc778 \ucf54\ub4dc\ub97c \ub9cc\ub4e4\uc5b4 \ubcf4\uace0\uc790 \ud55c\ub2e4. ZooKeeper\uac00 \uc9c0\uc6d0\ud558\ub294 \uae30\ub2a5\uc774 \uc5b4\ub5a4 \uac83\uc774 \uc788\uc73c\uba70 \uc5b4\ub5a4 \ubc29\uc2dd\uc73c\ub85c \ub3d9\uc791\ud558\ub294\uc9c0 \uae30\ubcf8\uc801\uc73c\ub85c \uc54c\uace0 \uc788\uc73c\uba74 \ub9ce\uc740 \ub3c4\uc6c0\uc774 \ub41c\ub2e4. \uc774 \uacbd\uc6b0 <a href=\"https:\/\/zookeeper.apache.org\/doc\/current\/zookeeperProgrammers.html\">ZooKeeper \uac1c\ubc1c\uc790 \uac00\uc774\ub4dc<\/a>\ub97c \uba3c\uc800 \ucc38\uace0\ud558\uae30 \ubc14\ub780\ub2e4.<\/p>\n\n\n\n<p>ZooKeeper \ud648\ud398\uc774\uc9c0\uc5d0 <a href=\"https:\/\/zookeeper.apache.org\/doc\/current\/javaExample.html\">ZooKeeper Java Example<\/a>\uc774\ub77c\ub294 \uac83\uc774 \uc788\uae34 \ud558\uc9c0\ub9cc ZooKeeper \uac1c\ub150\uc774 \uc0dd\uc18c\ud55c \uac1c\ubc1c\uc790\uac00 \ubcf4\uae30\uc5d0\ub294 \uc880 \ub09c\ud574\ud560 \uac83\uc774\ub77c \uc0dd\uac01\ub41c\ub2e4. \uc65c\ub0d0\ud558\uba74 Watcher\ub77c\ub294 \uac1c\ub150\uacfc StatCallback\uc774 \uc5b4\ub5bb\uac8c \ub3d9\uc791\ud558\ub294\uc9c0 \uc5b4\ub290\uc815\ub3c4 \uc774\ub860\uc801\uc778 \uc774\ud574\uac00 \uc788\uc5b4\uc57c \ud558\uace0 \uc81c\uacf5\ub418\ub294 \uc608\uc81c\uac00 \uac1c\ub150\uc801\uc73c\ub85c \uc774\ud574\ud558\uae30 \uc27d\ub3c4\ub85d \uac1c\ubc1c\ub41c \ucf54\ub4dc\uac00 \uc544\ub2c8\uae30 \ub54c\ubb38\uc774\ub2e4.<\/p>\n\n\n\n<p>\uc880 \ub354 \uba85\ub8cc\ud55c \uc774\ud574\ub97c \uc704\ud574 ZooKeeper\uc5d0 \ub4f1\ub85d\ub418\uc5b4 \uc788\ub294 \ud658\uacbd\uc124\uc815 \uc815\ubcf4\uac00 \ubcc0\uacbd\ub418\uc5c8\uc744 \uacbd\uc6b0 \ud074\ub77c\uc774\uc5b8\ud2b8\uac00 \uc774 \uc774\ubca4\ud2b8\ub97c \ubc1b\uc558\uc744 \ub54c \ub85c\uceec\uc5d0 \uc788\ub294 \ud658\uacbd\uc124\uc815\uc744 \uc5c5\ub370\uc774\ud2b8 \ud558\ub294 \uc2dc\ub098\ub9ac\uc624\ub85c ZooKeeper \ud074\ub77c\uc774\uc5b8\ud2b8 \ucf54\ub4dc\ub97c \ub9cc\ub4e4\uc5b4 \ubcf4\ub3c4\ub85d \ud55c\ub2e4.<\/p>\n\n\n\n<p>ZooKeeper\uc5d0\uc11c \ubc30\ud3ec\ub418\ub294 \ud074\ub77c\uc774\uc5b8\ud2b8 API\ub97c \ud65c\uc6a9\ud558\ub824\uba74 \uae30\ubcf8\uc801\uc73c\ub85c 3\uac1c\uc758 \ud30c\ub77c\ubbf8\ud130 \uc124\uc815\uc774 \ud544\uc694\ud558\ub2e4.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>ZooKeeper \uc11c\ubc84\uc815\ubcf4<\/strong>: \uc5f0\uacb0\ud560 ZooKeeper\uc11c\ubc84 \uc815\ubcf4\ub97c \ub098\ud0c0\ub0b4\uba70 \ub4f1\ub85d\ud560 \uc11c\ubc84\uac00 \uc5ec\ub7ec\uac1c\uc77c \uacbd\uc6b0 \ucf64\ub9c8(,)\ub85c \uad6c\ubd84\ud558\uc5ec \uae30\uc220\ud55c\ub2e4. \uc608) 127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002<\/li>\n\n\n\n<li><strong>\uc138\uc158 \ud0c0\uc784\uc544\uc6c3<\/strong>: \uc138\uc158 \ud0c0\uc784\uc544\uc6c3 \uc2dc\uac04(milliseconds)<\/li>\n\n\n\n<li><strong>Watcher<\/strong>: ZooKeeper \uc11c\ubc84\uc758 \uc0c1\ud0dc\uac00 \ubcc0\uacbd\ub420 \uacbd\uc6b0 \uc774\ubca4\ud2b8\ub97c \ubc1b\uae30 \uc704\ud574 \ub4f1\ub85d\ud558\ub294 \uc778\ud130\ud398\uc774\uc2a4<\/li>\n<\/ul>\n\n\n\n<p>\uc5ec\uae30\uc11c \uc911\uc694\ud55c \uac83\uc740 Watcher\uc774\ub2e4. Watcher\uc758 \uc790\uc138\ud55c \uc124\uba85\uc740 <a href=\"https:\/\/zookeeper.apache.org\/doc\/current\/zookeeperProgrammers.html#ch_zkWatches\">\uc5ec\uae30\uc11c \ud655\uc778<\/a>\ud558\uae30 \ubc14\ub780\ub2e4. \uc54c\uc544 \ub450\uc5b4\uc57c \ud560 \uac83\uc740 Watcher \uc774\ubca4\ud2b8\ub294 One-time Trigger\uc774\ubbc0\ub85c \ud55c\ubc88 \ubc1c\uc0dd\ub41c \uc774\ubca4\ud2b8\ub294 \ub2e4\uc2dc \ubc1c\uc0dd\ub418\uc9c0 \uc54a\uc73c\ubbc0\ub85c \ub2e4\uc2dc \ub4f1\ub85d\ud558\ub294 \uacfc\uc815\uc744 \uac70\uccd0\uc57c \ud55c\ub2e4\ub294 \uac83\uc774\ub2e4.<\/p>\n\n\n\n<p>\uadf8\ub9ac\uace0 \uc758\ubbf8 \uc804\ub2ec\uc744 \uc704\ud574 \uc608\uc81c\ub294 ZooKeeper API\uc640 \uc774\ubca4\ud2b8\ub97c \ucc98\ub9ac\ud558\ub294 \ub85c\uc9c1 \uc0ac\uc774\uc5d0 \ucd5c\ub300\ud55c Coupling\uc744 \uc5c6\uc560\ub294 \ud615\ud0dc\ub85c \ub9cc\ub4e4\ub3c4\ub85d \ud55c\ub2e4. \uc774\ub97c \uc704\ud574 Proxy \uc778\ud130\ud398\uc774\uc2a4\uc640 LazyListener \uc778\ud130\ud398\uc774\uc2a4\ub97c \ub454\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/*\n * Copyright (c) 2024 kt corp. All rights reserved.\n *\/\npackage app.zookeeper;\n\nimport org.apache.zookeeper.ZooKeeper;\n\n\/**\n * Interface for getting ZooKeeper instance in a loosely coupled manner.\n *\/\npublic interface Proxy {\n    \n    \/**\n     * \n     * @return ZooKeeper instance.\n     *\/\n    ZooKeeper zookeeper();\n}<\/code><\/pre>\n\n\n\n<p>Proxy \uc778\ud130\ud398\uc774\uc2a4\ub294 ZooKeeper \ud074\ub77c\uc774\uc5b8\ud2b8 \ub85c\uc9c1 \ucc98\ub9ac\uacfc\uc815\uc5d0\uc11c ZooKeeper API\uc640 Coupling\uc744 \uc904\uc774\uba74\uc11c \uc778\uc2a4\ud134\uc2a4\ub97c \uc0dd\uc131\ud558\uae30 \uc704\ud55c \uc6a9\ub3c4\ub85c \uc0ac\uc6a9\ud55c\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/*\n * Copyright (c) 2024 kt corp. All rights reserved.\n *\/\npackage app.zookeeper;\n\nimport java.util.List;\n\n\/**\n * This interface is used for loading EventListeners in a loosely coupled manner.\n *\/\npublic interface LazyListener {\n    \n    \/**\n     * @return EventListeners to register in the ZooKeeper client. Through these EventListeners you can be notified\n     * with the specific ZooKeeper event.\n     *\/\n    List&lt;EventListener> listeners();\n}<\/code><\/pre>\n\n\n\n<p>LazyListener \uc778\ud398\uc774\uc2a4\ub294 Watcher\uc5d0\uc11c \uc774\ubca4\ud2b8 \ucc98\ub9ac\uacfc\uc815\uc5d0\uc11c \ud544\uc694\ub85c \ud558\ub294 EventListener\ub97c Loosely Coupled\ub41c \ubc29\uc2dd\uc73c\ub85c \ub4f1\ub85d\ud558\uae30 \uc704\ud574 \uc0ac\uc6a9\ub41c\ub2e4. \ub098\uc911\uc5d0 \uc124\uba85\ud558\uaca0\uc9c0\ub9cc EventListener\ub294 ZooKeeper\uc11c\ubc84\uc5d0\uc11c \ubcc0\uacbd\uc774 \ubc1c\uc0dd\ud560 \uacbd\uc6b0 \uad00\ub828 \uc774\ubca4\ud2b8\ub97c Callback\ubc1b\uc544 \ucc98\ub9ac\ud558\uae30 \uc704\ud55c \uc6a9\ub3c4\ub85c \uc0ac\uc6a9\ub41c\ub2e4. <\/p>\n\n\n\n<p>ZooKeeper\uc5d0\uc11c \ubcc0\uacbd\uc774 \ubc1c\uc0dd\ud558\uba74 \ud074\ub77c\uc774\uc5b8\ud2b8\ub294 Watcher\ub77c\ub294 \uc778\ud37c\ud398\uc774\uc2a4\ub97c \ud1b5\ud574\uc11c \ud574\ub2f9 \uc774\ubca4\ud2b8\ub97c \ubc1b\uc544 \ucc98\ub9ac\ud560 \uc218 \uc788\ub2e4. \uc774\ubca4\ud2b8\uac00 \ubc1c\uc0dd\ud558\uba74 \uc774 Watcher\uc5d0\uc11c \ucc98\ub9ac\uc57c\uc57c \ud558\ub294 \uc77c\ub828\uc758 \uacfc\uc815\ub4e4\uc774 \uc788\uc9c0\ub9cc \uacf5\ud1b5\uc801\uc73c\ub85c \ucc98\ub9ac\ud558\ub294 \uacfc\uc815\ub4e4\uc740 Framework \ud615\ud0dc\ub85c \ubd84\ub9ac\uc2dc\ud0a4\uace0 \uc2e4\uc81c \uc5c5\ubb34\ub85c\uc9c1\uc73c\ub85c \ucc98\ub9ac\uac00 \ud544\uc694\ud55c \uc791\uc5c5\ub4e4\uc740 EventListener\uc5d0\uc11c \ucc98\ub9ac\ud558\ub3c4\ub85d \uc124\uacc4\ud574 \ubcf8\ub2e4.<\/p>\n\n\n\n<p>\uacf5\ud1b5\uc801\uc73c\ub85c \ucc98\ub9ac\ud560 \uacfc\uc815\ub4e4\uc744 Framework\ud654 \ud55c \uac83\uc774 \ubb38\uc9c0\uae30\uc758 \uc5ed\ud560\uacfc \ube44\uc2b7\ud560\uac83 \uac19\uc544 Guardian\uc774\ub77c\ub294 \ud074\ub798\uc2a4 \uc774\ub984\uc73c\ub85c \uad6c\ud604\ud588\ub2e4. \uc55e\uc5d0\uc11c \uc124\uba85\ud55c \ub450 \uac1c\uc758 \uc778\ud130\ud398\uc774\uc2a4(Proxy, LazyListeners)\ub294 Guardian\uc5d0\uc11c ZooKeeper API\uc640 EventListener \uc0ac\uc774\uc5d0\uc11c Loosely\ud558\uac8c \uc0c1\ud638\uc791\uc6a9\ud558\ub3c4\ub85d \uad6c\uc131\ud558\uc600\uc73c\uba70 \uadf8\ub9bc\uc73c\ub85c \ud45c\ud604\ud558\uba74 \uc544\ub798\uc640 \uac19\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;Event Listener] &lt;--> &#91;Guardian] &lt;--> &#91;ZooKeeper API] &lt;--> &#91;ZooKeeper Server]<\/code><\/pre>\n\n\n\n<p>Guadian\uc740 \uc55e\uc11c \uc124\uba85\ud55c \uac83\ucc98\ub7fc ZooKeeper \uc758 \uc774\ubca4\ud2b8\ub97c \ubc1b\uc744 \uc218 \uc788\ub3c4\ub85d Wacher\uc778\ud130\ud398\uc774\uc2a4\ub97c \uad6c\ud604\ud558\uba70 \ubc1c\uc0dd\ub41c \uc774\ubca4\ud2b8 \uc720\ud615\uc5d0 \ub530\ub77c \ub4f1\ub85d\ub41c EventListener\uac00 \uc801\uc808\ud558\uac8c \ucc98\ub9ac\ud560 \uc218 \uc788\ub3c4\ub85d EventListener \ub4f1\ub85d\uc791\uc5c5\uacfc Event Dispatch\uc791\uc5c5\uc744 \uc8fc\ub85c \uc218\ud589\ud55c\ub2e4. \uc544\ub798\ub294 EventListener \uc778\ud130\ud398\uc774\uc2a4\uc774\uba70 on \uba54\uc18c\ub4dc\uc5d0\uc11c \uc774\ubca4\ud2b8\ub97c Callback\ubc1b\uc73c\uba74 \uc0c1\ud669\uc5d0 \ub9de\uac8c \uc801\uc801\ud560 \uc791\uc5c5\uc744 \ucc98\ub9ac\ud558\uba74 \ub41c\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/**\n * Example for testing the operation of ZooKeeper\n * @author skanto.kim@kt.com\n *\/\npackage app.zookeeper;\n\nimport org.apache.zookeeper.WatchedEvent;\nimport org.apache.zookeeper.data.Stat;\n\n\/**\n * You can implement this interface if you want to get notified when the node on the ZooKeeper is changed. \n *\/\npublic interface EventListener {\n    \n    \/**\n     * This method will be notified if the {@link WatchedEvent} is fired on the node. You can also receive the data\n     * as a second parameter that belongs to the path\n     * \n     * @param event\n     * @param data\n     * @param path\n     * @param ctx\n     * @param stat\n     *\/\n    void on(WatchedEvent event, byte&#91;] data, String path, Object ctx, Stat stat) throws Exception;\n}<\/code><\/pre>\n\n\n\n<p>\uc544\ub798\ub294 Guardian.java \ucf54\ub4dc\uc774\uba70 addListener \uba54\uc18c\ub4dc\uc640 curry \uba54\uc18c\ub4dc\ub97c \ub208\uc5ec\uaca8 \ubcfc\ub9cc \ud558\ub2e4. \uc55e\uc5d0\uc11c\uc124\uba85\ud55c \uac83\ucc98\ub7fc ZooKeeper Event\ub294 One-time \uc774\ubca4\ud2b8 \uc774\ubbc0\ub85c Event\ucc98\ub9ac \ud6c4 \uacc4\uc18d\ud574\uc11c \uc774\ubca4\ud2b8\ub97c \ubc1b\uc73c\ub824\uba74 exists \uba54\uc18c\ub4dc\ub97c \uc774\uc6a9\ud558\uc5ec  Watch\ub97c \uc124\uc815\ud574\uc57c \ud55c\ub2e4. \uc774 \uacfc\uc815\uc740 monitor \uba54\uc18c\ub4dc\ub97c \ucc38\uace0\ud558\uae30 \ubc14\ub780\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package app.zookeeper;\n\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.apache.zookeeper.AsyncCallback.StatCallback;\nimport org.apache.zookeeper.KeeperException.Code;\nimport org.apache.zookeeper.WatchedEvent;\nimport org.apache.zookeeper.Watcher;\nimport org.apache.zookeeper.Watcher.Event.EventType;\nimport org.apache.zookeeper.data.Stat;\n\n\/**\n * \n *\/\npublic class Guardian implements Watcher {\n\n    private final Map&lt;EventType, Map&lt;String, EventListener>> registry = new HashMap&lt;EventType, Map&lt;String, EventListener>>();\n    private final Proxy proxy;\n    private final LazyListener lazy;\n    \n    \/**\n     * @param proxy\n     * @param lazy\n     *\/\n    public Guardian(Proxy proxy, LazyListener lazy) {\n        this.proxy = proxy;\n        this.lazy = lazy;\n    }\n    \n    @SuppressWarnings(\"incomplete-switch\")\n    @Override\n    public void process(WatchedEvent event) {\n        if (event.getType() == Event.EventType.None) {\n            switch (event.getState()) {\n            case SyncConnected:\n                if (event.getPath() != null) break;\n                lazy.listeners().forEach((listener) -> {\n                    addListener(listener);\n                    monitor(annotationOf(listener).znode(), curry(listener, event));\n                });\n                break;\n            case Expired:\n                System.out.println(\"session expired!\");\n                notifyAll();\n                break;\n            }\n        } else {\n            monitor(event.getPath(), curry(listenersOf(event.getType()).get(event.getPath()), event));\n        }\n    }\n    \n    \/**\n     * @param path\n     * @param callback\n     *\/\n    private void monitor(String path, StatCallback callback) {\n        proxy.zookeeper().exists(path, true, callback, null);\n    }\n    \n    \/**\n     * @param type\n     * @param listener\n     *\/\n    @SuppressWarnings(\"serial\")\n    private void addListener(EventListener listener) {\n        ZooKeeperWatch annotation = annotationOf(listener);\n        Map&lt;String, EventListener> listeners = listenersOf(annotation.event());\n        if (listeners == null) {\n            registry.put(annotation.event(), new HashMap&lt;String, EventListener>() {{\n                put(annotation.znode(), listener); \n            }});\n        } else {\n            listeners.put(annotation.znode(), listener); \/\/ replace the existing one\n        }\n    }\n    \n    \/**\n     * @param listener\n     * @return\n     *\/\n    public ZooKeeperWatch annotationOf(EventListener listener) {\n        try {\n            Method m = listener.getClass().getMethod(\"on\", EventListener.class.getMethods()&#91;0].getParameterTypes());\n            if (!m.isAnnotationPresent(ZooKeeperWatch.class)) {\n                throw new RuntimeException(listener.getClass().getSimpleName() + \".\" + m.getName() \n                + \" is not annotated with @\" + ZooKeeperWatch.class.getSimpleName());\n            }\n            return m.getAnnotation(ZooKeeperWatch.class);\n        } catch (Exception e) {\n            throw new IllegalArgumentException(\"Can not find the annotation from the EventListener\", e);\n        }\n    }\n\n    \/**\n     * @param type\n     * @return\n     *\/\n    public Map&lt;String, EventListener> listenersOf(EventType type) {\n        return registry.get(type);\n    }\n    \n    \/**\n     * @param listener\n     * @return\n     *\/\n    private StatCallback curry(final EventListener listener, final WatchedEvent event) {\n        return new StatCallback() {\n            @Override\n            public void processResult(int rc, String path, Object ctx, Stat stat) {\n                switch(Code.get(rc)) {\n                case OK:\n                    break;\n                case NONODE:\n                    break;\n                case SESSIONEXPIRED:\n                case NOAUTH:\n                    System.out.println(\"Session is expired or no auth\");\n                    return;\n                default:\n                    System.out.println(\"default\");\n                    proxy.zookeeper().exists(path, true, this, null);\n                    return;\n                }\n                if (Code.get(rc) == Code.OK &amp;&amp; annotationOf(listener).event() == event.getType()) {\n                    try {\n                        listener.on(event, proxy.zookeeper().getData(path, false, null), path, ctx, stat);\n                    } catch (Exception e) {\n                        e.printStackTrace();\n                    }\n                }                \n            }\n        };\n    }\n}<\/code><\/pre>\n\n\n\n<p>\ud5a5\ud6c4 ZooKeeper\ub97c \uc774\uc6a9\ud55c \uc11c\ube44\uc2a4 \uac1c\ubc1c \uc2dc \uc774 \ucf54\ub4dc\ub97c \ucc38\uc870\ud560 \uacbd\uc6b0\uac00 \uc788\uc744 \uac83\uc73c\ub85c \ubcf4\uc5ec EventListener\ub4f1\ub85d\uc2dc Listening\ud560 Event Type\uacfc \uc9c0\ucf1c \ubcfc ZooKeeper\ub178\ub4dc\ub97c \ud3b8\ub9ac\ud558\uac8c \uc124\uc815\ud560 \uc218 \uc788\ub3c4\ub85d java Annotation\uc744 \ud65c\uc6a9\ud588\ub2e4. Annotation\uc744 \ub9cc\ub4dc\ub294 \uac83\uc778 \uc774 \uae00\uc758 \ubc94\uc704\ub97c \ubc97\uc5b4\ub098\ub294 \uac83\uc774\ub2c8 <a href=\"https:\/\/www.baeldung.com\/java-custom-annotation\">\uad00\ub828 \uc790\ub8cc<\/a>\ub97c \ucc38\uace0\ud558\uae30 \ubc14\ub780\ub2e4.<\/p>\n\n\n\n<p>\uadf8\ub9ac\uace0 monitor \uba54\uc18c\ub4dc\uc5d0\uc11c exists \ud30c\ub77c\ubbf8\ud130\ub85c StatCallback\uc744 \ub4f1\ub85d\ud558\uba70 \uc774\ub54c StatCallback \uac1d\uccb4 \uc0dd\uc131\uc2dc JavaScript\uc5d0\uc11c \ub9ce\uc774 \ud65c\uc6a9\ud558\ub294 <a href=\"https:\/\/builtin.com\/software-engineering-perspectives\/currying-javascript\">currying \ubc29\uc2dd<\/a>\uc744 \ud65c\uc6a9\ud588\uc73c\ub2c8 \uc774 \ubd80\ubd84\ub3c4 \uac19\uc774 \ucc38\uace0\ud558\uae30 \ubc14\ub780\ub2e4.<\/p>\n\n\n\n<p>ZooKeeper Node\uc5d0 \ubcc0\uacbd\uc774 \ubc1c\uc0dd\ud560 \uacbd\uc6b0 EventListener\uc5d0\uc11c \uc774\ubca4\ud2b8\ub97c \ubc1b\uc544 \uad00\ub828\uc791\uc5c5\uc744 \ucc98\ub9ac\ud558\ub294\ub370 \uc5ec\uae30\uc11c\ub294 \uc124\uc815\uc815\ubcf4\uac00 \ubcc0\uacbd\ub41c \uac83\uc744 \uac00\uc815\ud558\uc5ec \ubcc0\uacbd\ub41c \uc124\uc815\uc815\ubcf4\ub97c \ud658\uacbd\ud30c\uc77c\ub85c \uc800\uc7a5\ud558\ub294 \uacfc\uc815\uc744 \uc608\uc81c\ub85c \ub9cc\ub4e4\uc5c8\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/**\n * Example for testing the operation of ZooKeeper\n * @author skanto.kim@kt.com\n *\/\npackage app.zookeeper;\n\nimport java.io.FileOutputStream;\nimport java.io.OutputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\n\nimport org.apache.zookeeper.WatchedEvent;\nimport org.apache.zookeeper.Watcher.Event.EventType;\nimport org.apache.zookeeper.data.Stat;\n\npublic class AporiaChangeListener implements EventListener {\n    \n    \/**\n     * It holds previous configuration data used for verifying the changes compared to this change\n     *\/\n    private byte prev&#91;];\n\n    @ZooKeeperWatch(event = EventType.NodeDataChanged, znode = \"\/configs\/aporia\")\n    public void on(WatchedEvent event, byte&#91;] data, String path, Object ctx, Stat stat) throws Exception {\n        final String filename = \"\/home\/skanto\/Documents\/00.Development\/zookeeper\/aporia.conf\";\n        if ((data == null &amp;&amp; data != prev) || (data != null &amp;&amp; !Arrays.equals(prev, data))) {\n            try (OutputStream fos = new FileOutputStream(filename)) {\n                fos.write(data);\n                prev = data;\n            }\n            System.out.printf(\"%s - %s: %s\\n\", event.getType(), path, new String(data, StandardCharsets.UTF_8));\n        } else {\n            System.out.printf(\"%s - %s: %s\\n\", event.getType(), path, \"Data is not changed!\");\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p>\uc774\uc640 \uc720\uc0ac\ud558\uac8c \ud658\uacbd\uc124\uc815 \uc815\ubcf4\uac00 \uc5ec\ub7ec \uac1c\uc77c \uacbd\uc6b0\ub97c \uac00\uc815\ud558\uc5ec \ud558\ub098 \ub354 \ub9cc\ub4e4\uc5b4 \ubcf8\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/**\n * Example for testing the operation of ZooKeeper\n * @author skanto.kim@kt.com\n *\/\npackage app.zookeeper;\n\nimport java.io.FileOutputStream;\nimport java.io.OutputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\n\nimport org.apache.zookeeper.WatchedEvent;\nimport org.apache.zookeeper.Watcher.Event.EventType;\nimport org.apache.zookeeper.data.Stat;\n\n\/**\n * \n *\/\npublic class TelosChangeListener implements EventListener {\n    \n    \/**\n     * It holds previous configuration data used for verifying the changes compared to this change\n     *\/\n    private byte prev&#91;];\n\n    @ZooKeeperWatch(event = EventType.NodeDataChanged, znode = \"\/configs\/telos\")\n    public void on(WatchedEvent event, byte&#91;] data, String path, Object ctx, Stat stat) throws Exception {\n        final String filename = \"\/home\/skanto\/Documents\/00.Development\/zookeeper\/telos.conf\";\n        if ((data == null &amp;&amp; data != prev) || (data != null &amp;&amp; !Arrays.equals(prev, data))) {\n            try (OutputStream fos = new FileOutputStream(filename)) {\n                fos.write(data);\n                prev = data;\n            }\n            System.out.printf(\"%s - %s: %s\\n\", event.getType(), path, new String(data, StandardCharsets.UTF_8));\n        } else {\n            System.out.printf(\"%s - %s: %s\\n\", event.getType(), path, \"Data is not changed!\");\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p>\uc704 \ucf54\ub4dc\uc5d0\uc11c \ub208\uc5ec\uaca8 \ubcfc \ub9cc\ud55c \ub0b4\uc6a9\uc740 Aonnotation\uc744 \ud65c\uc6a9\ud558\uc5ec EventType\uacfc ZooKeeper \ub178\ub4dc\ub97c \uc124\uc815\ud588\ub2e4\ub294 \uac83\uc774\ub2e4. \ub610\ud55c Change \uc774\ubca4\ud2b8\uac00 \ubc1c\uc0dd\ud558\ub354\ub77c\ub3c4 \uc774\uc804\uc758 \ub370\uc774\ud130\uc640 \ube44\uad50\ud558\uc5ec \ubcc0\uacbd\ub41c \ubd80\ubd84\uc774 \uc788\ub294\uc9c0 \ud655\uc778\ud558\uace0 \ubcc0\uacbd\ub41c \ubd80\ubd84\uc774 \uc788\ub2e4\uba74 \ud30c\uc77c\ub85c \uc800\uc7a5\ud558\ub3c4\ub85d \uc608\uc81c\ub97c \ub9cc\ub4e4\uc5c8\ub2e4.<\/p>\n\n\n\n<p>\uc790, \uc774\uc81c \uc774\ub807\uac8c \ub9cc\ub4e0 Guardian\uacfc 2\uac1c\uc758 Listener\ub97c \uc870\ud569\ud558\uc5ec ZooKeeperClient \uc608\uc81c\ub97c \ub9cc\ub4e4\ub3c4\ub85d \ud55c\ub2e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/**\n * Example for testing the operation of ZooKeeper\n * @author skanto.kim@kt.com\n *\/\npackage app.zookeeper;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.zookeeper.Watcher;\nimport org.apache.zookeeper.ZooKeeper;\n\n\/**\n * ZooKeeper Example\n *\/\npublic class ZooKeeperClient {\n    \n    \/**\n     * \n     *\/\n    private ZooKeeper zookeeper;\n    \n    \/**\n     * @param server\n     * @param listeners\n     * @throws IOException\n     *\/\n    public void observe(String server, List&lt;EventListener> listeners) throws IOException {\n        Watcher watcher = new Guardian(() -> zookeeper, () -> listeners);\n        this.zookeeper = new ZooKeeper(server, 3000, watcher);\n    }\n    \n    \/**\n     * @param definitions\n     * @return\n     * @throws Exception\n     *\/\n    @SuppressWarnings(\"serial\")\n    private static List&lt;EventListener> loadListeners(String&#91;] listeners) throws Exception {\n        return new ArrayList&lt;EventListener>() {{\n            for (String str: listeners) add(obj(str, EventListener.class));\n        }};\n    }\n    \n    @SuppressWarnings({ \"unchecked\" })\n    private static &lt;T> T obj(final String str, Class&lt;T> iface) throws Exception {\n        Class&lt;T> clazz = (Class&lt;T>) Class.forName(str).asSubclass(iface);\n        return  clazz.getConstructor(new Class&#91;]{ }).newInstance();\n    }\n    \n    \/**\n     * @param args\n     * @throws IOException\n     *\/\n    public static void main(String&#91;] args) throws Exception {\n        final String&#91;] listeners = { \"app.zookeeper.AporiaChangeListener\", \"app.zookeeper.TelosChangeListener\" };\n        ZooKeeperClient client = new ZooKeeperClient();\n        client.observe(\"localhost:2181\", loadListeners(listeners));\n        synchronized (client) {\n            System.out.println(\"Now is waiting for this thread to be terminated...\");\n            while (true) {\n                client.wait();\n            }\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p>\uc5ec\uae30\uc11c \ub208\uc5ec\uaca8 \ubcfc\ub9cc\ud55c \ucf54\ub4dc\ub294 Guardian \uac1d\uccb4\ub97c \uc0dd\uc131\ud560 \ub54c Lambda\ub97c \uc774\uc6a9\ud558\uc5ec \uc55e\uc11c \uc124\uba85\ud55c Proxy\uc640 ZazyListener \uc778\ud130\ud398\uc774\uc2a4\ub97c \uad6c\ud604(implement)\ud588\ub2e4\ub294 \uac83\uc774\ub2e4. \uc774\ub807\uac8c \ud558\uba74 \uc0dd\uc131\uc790 \ud30c\ub77c\ubbf8\ud130\ub85c ZooKeeper\uc640 EventListener \uac1d\uccb4\ub97c \uc9c1\uc811 \uc804\ub2ec\ud558\uc9c0 \uc54a\uace0\ub3c4 Guardian\ub0b4\ubd80\uc5d0\uc11c \ud544\uc694\ud560 \uc2dc\uc810 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub3c4\ub85d Loosely Coupled\ub41c \ubc29\uc2dd\uc73c\ub85c \uac1d\uccb4\ub97c \uc804\ub2ec\ud560 \uc218 \uc788\ub2e4.<\/p>\n\n\n\n<p>\ucc38\uace0\ub85c ZooKeeper API\ub294 \ub2e4\ub978 \ub3c5\ub9bd\ub41c Thread\ub85c Async\ud558\uac8c \ub3d9\uc791\ud558\ubbc0\ub85c Main Thread\uac00 \uc885\ub8cc\ub418\uc9c0 \uc54a\ub3c4\ub85d main \uba54\uc18c\ub4dc\uc5d0\uc11c Thread.wait()\ub97c \ud65c\uc6a9\ud55c\ub2e4. ZooKeeperClient \uc2e4\ud589\uc911\uc5d0 \uc885\ub8cc\ub97c \ud574\uc57c \ud55c\ub2e4\uba74 \ud504\ub85c\uc138\uc2a4\ub97c \uc885\ub8cc\uc2dc\ud0a4\uba74 \ub41c\ub2e4.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>ZooKeeper\ub294 MSA\uc5d0\uc11c Scalable\ud55c \uad6c\uc870\ub97c \uc720\uc9c0\uac00\ud788 \uc704\ud574 \ub9ce\uc740 \uc624\ud508\uc18c\uc2a4 \uc194\ub8e8\uc158\uc774 \ub0b4\ubd80\uc801\uc73c\ub85c \ud65c\uc6a9\ud558\ub294 Coordination \uc11c\ube44\uc2a4\ub77c \ud560 \uc218 \uc788\ub2e4. \ub300\ud45c\uc801\uc73c\ub85c Kafka\uc5d0\uc11c \ub0b4\ubd80\uc801\uc73c\ub85c \ud658\uacbd\uc744 \uad00\ub9ac\ud558\uace0 \ud074\ub7ec\uc2a4\ud130\ub97c \uc720\uc9c0\ud558\uae30 \uc704\ud574 \uc0ac\uc6a9\ud558\ub294 \uc815\ub3c4\ub85c \uc54c\uace0 \uc788\ub2e4. ZooKeeper\uc758 \ud0c4\uc0dd\uc744 \ubcf4\uba74 \uc57c\ud6c4(Yahoo)\uac00 \uc544\uc9c1\uc740 \uadf8 \uba85\uc131\uc774 \uc0b4\uc544 \uc788\uc744 \ub54c \ud074\ub77c\uc6b0\ub4dc \ud658\uacbd\uc73c\ub85c \uc804\ud658\uacfc \ub9de\ubb3c\ub824 \uc790\uccb4\uc73c\ub85c \ud544\uc694\uc5d0 \uc758\ud574 \ub9cc\ub4e0 \ud6c4 \uc624\ud508\uc18c\uc2a4\ub85c \uacf5\uac1c\ud55c \uc11c\ube44\uc2a4\uc774\ub2e4(2011). \ud074\ub77c\uc6b0\ub4dc\/MSA\ud658\uacbd\uc5d0\uc11c\ub294 \uc548\uc815\uc801\uc778 \uc11c\ube44\uc2a4 \uc6b4\uc601\uc744 \uc704\ud574 Scalable\ud55c \uad6c\uc870\uac00 \ud544\uc218\uc801\uc774\ub2e4. \uc774\ub54c \uc11c\ube44\uc2a4\uc758 \ud2b8\ub798\ud53d \uc0c1\ud669\uc5d0 \ub530\ub77c \ub178\ub4dc \uac1c\uc218\uac00 \ub298\uc5b4\ub098\uac70\ub098 \uc904\uc5b4\ub4e4\uc5b4\uc57c \ud558\uba70 \ubc14\uc774\ub108\ub9ac \uc5c5\ub370\uc774\ud2b8 \ubc30\ud3ec\uc2dc\uc5d0\ub3c4 \ubaa8\ub4e0 \ub178\ub4dc\ub4e4\uc5d0 \ub3d9\uc77c\ud55c \ud658\uacbd\uc774 \uad6c\uc131\ub418\ub3c4\ub85d \ud574\uc57c \ud55c\ub2e4. \uc608\ub97c \ub4e4\uba74&#8230;<\/p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https:\/\/skanto.co.kr\/?p=1181\"> Read More<span class=\"screen-reader-text\">  Read More<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_import_markdown_pro_load_document_selector":0,"_import_markdown_pro_submit_text_textarea":"","footnotes":""},"categories":[14],"tags":[106],"class_list":["post-1181","post","type-post","status-publish","format-standard","hentry","category-sw-development","tag-zookeeper"],"_links":{"self":[{"href":"https:\/\/skanto.co.kr\/index.php?rest_route=\/wp\/v2\/posts\/1181","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/skanto.co.kr\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/skanto.co.kr\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/skanto.co.kr\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/skanto.co.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1181"}],"version-history":[{"count":8,"href":"https:\/\/skanto.co.kr\/index.php?rest_route=\/wp\/v2\/posts\/1181\/revisions"}],"predecessor-version":[{"id":1190,"href":"https:\/\/skanto.co.kr\/index.php?rest_route=\/wp\/v2\/posts\/1181\/revisions\/1190"}],"wp:attachment":[{"href":"https:\/\/skanto.co.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1181"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/skanto.co.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1181"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/skanto.co.kr\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1181"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}