Introduzione a Java e Zookeeper

1. Panoramica

Apache ZooKeeper è un servizio di coordinamento distribuito che facilita lo sviluppo di applicazioni distribuite. Viene utilizzato da progetti come Apache Hadoop, HBase e altri per diversi casi d'uso come l'elezione del leader, la gestione della configurazione, il coordinamento dei nodi, la gestione del lease del server, ecc.

I nodi all'interno del cluster ZooKeeper memorizzano i propri dati in uno spazio dei nomi gerarchico condiviso che è simile a un file system standard oa una struttura di dati ad albero.

In questo articolo, esploreremo come utilizzare l'API Java di Apache Zookeeper per archiviare, aggiornare ed eliminare le informazioni archiviate in ZooKeeper.

2. Configurazione

L'ultima versione della libreria Java di Apache ZooKeeper può essere trovata qui:

 org.apache.zookeeper zookeeper 3.4.11 

3. Modello di dati ZooKeeper - ZNode

ZooKeeper ha uno spazio dei nomi gerarchico, molto simile a un file system distribuito in cui memorizza i dati di coordinamento come informazioni sullo stato, informazioni sulla coordinazione, informazioni sulla posizione, ecc. Queste informazioni sono memorizzate su nodi diversi.

Ogni nodo in un albero ZooKeeper è denominato ZNode.

Ogni ZNode conserva i numeri di versione e i timestamp per qualsiasi modifica di dati o ACL. Inoltre, questo consente a ZooKeeper di convalidare la cache e di coordinare gli aggiornamenti.

4. Installazione

4.1. Installazione

L'ultima versione di ZooKeeper può essere scaricata da qui. Prima di farlo, dobbiamo assicurarci di soddisfare i requisiti di sistema descritti qui.

4.2. Modalità standalone

Per questo articolo, eseguiremo ZooKeeper in modalità standalone poiché richiede una configurazione minima. Segui i passaggi descritti nella documentazione qui.

Nota: in modalità standalone, non c'è replica, quindi se il processo di ZooKeeper non riesce, il servizio verrà interrotto.

5. Esempi della CLI di ZooKeeper

Ora utilizzeremo l'interfaccia a riga di comando (CLI) di ZooKeeper per interagire con ZooKeeper:

bin/zkCli.sh -server 127.0.0.1:2181

Il comando precedente avvia un'istanza autonoma in locale. Vediamo ora come creare uno ZNode e memorizzare le informazioni all'interno di ZooKeeper:

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

Abbiamo appena creato uno ZNode "MyFirstZNode" alla radice dello spazio dei nomi gerarchico di ZooKeeper e scritto "ZNodeVal" .

Poiché non abbiamo passato alcun flag, uno ZNode creato sarà persistente.

Emettiamo ora un comando 'get' per recuperare i dati e i metadati associati a uno 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

Possiamo aggiornare i dati di uno ZNode esistente utilizzando l' operazione set .

Per esempio:

set /MyFirstZNode ZNodeValUpdated

Questo aggiornerà i dati su MyFirstZNode da ZNodeVal a ZNodeValUpdated.

6. Esempio di API Java di ZooKeeper

Diamo ora un'occhiata all'API Java di Zookeeper e creiamo un nodo, aggiorniamo il nodo e recuperiamo alcuni dati.

6.1. Pacchetti Java

I collegamenti Java di ZooKeeper sono composti principalmente da due pacchetti Java:

  1. org.apache.zookeeper : che definisce la classe principale della libreria client ZooKeeper insieme a molte definizioni statiche dei tipi e degli stati degli eventi ZooKeeper
  2. org.apache.zookeeper.data : che definisce le caratteristiche associate agli ZNode, come ACL (Access Control List), ID, statistiche e così via

Esistono anche API Java di ZooKeeper utilizzate nell'implementazione del server come org.apache.zookeeper.server , org.apache.zookeeper.server.quorum e org.apache.zookeeper.server.upgrade .

Tuttavia, vanno oltre lo scopo di questo articolo.

6.2. Connessione a un'istanza ZooKeeper

Creiamo ora la classe ZKConnection che verrà utilizzata per connettersi e disconnettersi da uno ZooKeeper già in esecuzione:

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(); } }

Per utilizzare un servizio ZooKeeper, un'applicazione deve prima creare un'istanza di un oggetto della classe ZooKeeper , che è la classe principale della libreria client ZooKeeper .

Nel metodo connect , stiamo istanziando un'istanza della classe ZooKeeper . Inoltre, abbiamo registrato un metodo di callback per elaborare WatchedEvent da ZooKeeper per l'accettazione della connessione e di conseguenza terminare il metodo di connessione utilizzando il metodo di conto alla rovescia di CountDownLatch .

Una volta stabilita una connessione a un server, al client viene assegnato un ID di sessione. Per mantenere valida la sessione, il client dovrebbe inviare periodicamente heartbeat al server.

L'applicazione client può chiamare le API ZooKeeper fintanto che il suo ID di sessione rimane valido.

6.3. Operazioni client

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 fornisce anche un'elegante API basata su Java da utilizzare nel codice dell'applicazione lato client per una comunicazione senza interruzioni con ZooKeeper ZNodes.

E come sempre, tutte le fonti per questo tutorial possono essere trovate su Github.