Първи стъпки с Java и Zookeeper

1. Общ преглед

Apache ZooKeeper е разпределена услуга за координация, която улеснява разработването на разпределени приложения. Използва се от проекти като Apache Hadoop, HBase и други за различни случаи на употреба като избор на лидер, управление на конфигурация, координация на възли, управление на наем на сървър и др.

Възлите в клъстера ZooKeeper съхраняват данните си в споделено йерархично пространство от имена, което е подобно на стандартна файлова система или дървовидна структура от данни.

В тази статия ще проучим как да използваме Java API на Apache Zookeeper за съхраняване, актуализиране и изтриване на информация, съхранявана в ZooKeeper.

2. Настройка

Най-новата версия на библиотеката Apache ZooKeeper Java можете да намерите тук:

 org.apache.zookeeper zookeeper 3.4.11 

3. Модел на данни на ZooKeeper - ZNode

ZooKeeper има йерархично пространство от имена, подобно на разпределена файлова система, където съхранява координационни данни като информация за състоянието, информация за координация, информация за местоположението и т.н. Тази информация се съхранява на различни възли.

Всеки възел в дърво на ZooKeeper се нарича ZNode.

Всеки ZNode поддържа номера на версиите и времеви клейма за всякакви данни или промени в ACL. Също така, това позволява на ZooKeeper да проверява кеша и да координира актуализациите.

4. Инсталация

4.1. Инсталация

Последната версия на ZooKeeper може да бъде изтеглена от тук. Преди да направим това, трябва да се уверим, че отговаряме на системните изисквания, описани тук.

4.2. Самостоятелен режим

За тази статия ще стартираме ZooKeeper в самостоятелен режим, тъй като изисква минимална конфигурация. Следвайте стъпките, описани в документацията тук.

Забележка: В самостоятелен режим няма репликация, така че ако процесът на ZooKeeper не успее, услугата ще спре.

5. Примери за CLI на ZooKeeper

Сега ще използваме интерфейса на командния ред на ZooKeeper (CLI), за да взаимодействаме със ZooKeeper:

bin/zkCli.sh -server 127.0.0.1:2181

Горната команда стартира самостоятелен екземпляр локално. Нека сега разгледаме как да създадем ZNode и да съхраняваме информация в ZooKeeper:

[zk: localhost:2181(CONNECTED) 0] create /MyFirstZNode ZNodeVal Created /FirstZnode

Току-що създадохме ZNode 'MyFirstZNode' в корена на йерархичното пространство от имена на ZooKeeper и му написахме 'ZNodeVal' .

Тъй като не сме подали нито едно знаме, създаденият ZNode ще бъде постоянен.

Нека сега издадем команда „get“, за да извлечем данните, както и метаданните, свързани със ZNode:

[zk: localhost:2181(CONNECTED) 1] get /FirstZnode “Myfirstzookeeper-app” cZxid = 0x7f ctime = Sun Feb 18 16:15:47 IST 2018 mZxid = 0x7f mtime = Sun Feb 18 16:15:47 IST 2018 pZxid = 0x7f cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 22 numChildren = 0

Можем да актуализираме данните на съществуващ ZNode, използвайки зададената операция.

Например:

set /MyFirstZNode ZNodeValUpdated

Това ще актуализира данните в MyFirstZNode от ZNodeVal до ZNodeValUpdated.

6. Пример за API на ZooKeeper Java

Нека сега разгледаме API на Zookeeper Java и да създадем възел, да актуализираме възела и да извлечем някои данни.

6.1. Java пакети

Java обвързванията на ZooKeeper са съставени главно от два Java пакета:

  1. org.apache.zookeeper : който дефинира основния клас на клиентската библиотека на ZooKeeper заедно с много статични дефиниции на типовете и състоянията на ZooKeeper
  2. org.apache.zookeeper.data : който определя характеристиките, свързани със ZNodes, като списъци за контрол на достъпа (ACL), идентификатори, статистика и т.н.

Има и API на ZooKeeper Java, които се използват при изпълнението на сървъра, като например org.apache.zookeeper.server , org.apache.zookeeper.server.quorum и org.apache.zookeeper.server.upgrade .

Те обаче са извън обхвата на тази статия.

6.2. Свързване към екземпляр на ZooKeeper

Нека сега създадем клас ZKConnection, който ще се използва за свързване и изключване от вече работещ ZooKeeper:

public class ZKConnection { private ZooKeeper zoo; CountDownLatch connectionLatch = new CountDownLatch(1); // ... public ZooKeeper connect(String host) throws IOException, InterruptedException { zoo = new ZooKeeper(host, 2000, new Watcher() { public void process(WatchedEvent we) { if (we.getState() == KeeperState.SyncConnected) { connectionLatch.countDown(); } } }); connectionLatch.await(); return zoo; } public void close() throws InterruptedException { zoo.close(); } }

За да използва услуга ZooKeeper, приложението трябва първо да създаде екземпляр на обект от клас ZooKeeper , който е основният клас на клиентската библиотека на ZooKeeper .

In connect method, we're instantiating an instance of ZooKeeper class. Also, we've registered a callback method to process the WatchedEvent from ZooKeeper for connection acceptance and accordingly finish the connect method using countdown method of CountDownLatch.

Once a connection to a server is established, a session ID gets assigned to the client. To keep the session valid, the client should periodically send heartbeats to the server.

The client application can call ZooKeeper APIs as long as its session ID remains valid.

6.3. Client Operations

We'll now create a ZKManager interface which exposes different operations like creating a ZNode and saving some data, fetching and updating the ZNode Data:

public interface ZKManager { public void create(String path, byte[] data) throws KeeperException, InterruptedException; public Object getZNodeData(String path, boolean watchFlag); public void update(String path, byte[] data) throws KeeperException, InterruptedException; }

Let's now look at the implementation of the above interface:

public class ZKManagerImpl implements ZKManager { private static ZooKeeper zkeeper; private static ZKConnection zkConnection; public ZKManagerImpl() { initialize(); } private void initialize() { zkConnection = new ZKConnection(); zkeeper = zkConnection.connect("localhost"); } public void closeConnection() { zkConnection.close(); } public void create(String path, byte[] data) throws KeeperException, InterruptedException { zkeeper.create( path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } public Object getZNodeData(String path, boolean watchFlag) throws KeeperException, InterruptedException { byte[] b = null; b = zkeeper.getData(path, null, null); return new String(b, "UTF-8"); } public void update(String path, byte[] data) throws KeeperException, InterruptedException { int version = zkeeper.exists(path, true).getVersion(); zkeeper.setData(path, data, version); } }

In the above code, connect and disconnect calls are delegated to the earlier created ZKConnection class. Our create method is used to create a ZNode at given path from the byte array data. For demonstration purpose only, we've kept ACL completely open.

Once created, the ZNode is persistent and doesn't get deleted when the client disconnects.

The logic to fetch ZNode data from ZooKeeper in our getZNodeData method is quite straightforward. Finally, with the update method, we're checking the presence of ZNode on given path and fetching it if it exists.

Beyond that, for updating the data, we first check for ZNode existence and get the current version. Then, we invoke the setData method with the path of ZNode, data and current version as parameters. ZooKeeper will update the data only if the passed version matches with the latest version.

7. Conclusion

When developing distributed applications, Apache ZooKeeper plays a critical role as a distributed coordination service. Specifically for use cases like storing shared configuration, electing the master node, and so on.

ZooKeeper също така предлага елегантни API, базирани на Java, които да се използват в кода на приложението от страна на клиента за безпроблемна комуникация със ZooKeeper ZNodes.

И както винаги, всички източници за този урок могат да бъдат намерени в Github.