Pengenalan kepada Apache Curator

1. Pengenalan

Apache Curator adalah pelanggan Java untuk Apache Zookeeper, perkhidmatan koordinasi yang popular untuk aplikasi yang diedarkan.

Dalam tutorial ini, kami akan memperkenalkan beberapa ciri paling relevan yang disediakan oleh Kurator:

  • Pengurusan Sambungan - menguruskan sambungan dan mencuba semula dasar
  • Async - meningkatkan pelanggan sedia ada dengan menambahkan keupayaan async dan penggunaan Java 8 lambdas
  • Pengurusan Konfigurasi - mempunyai konfigurasi terpusat untuk sistem
  • Model yang Sangat Diketik - berfungsi dengan model yang ditaip
  • Resipi - melaksanakan pemilihan pemimpin, kunci atau kaunter yang diedarkan

2. Prasyarat

Sebagai permulaan, disarankan untuk melihat sekilas Apache Zookeeper dan ciri-cirinya.

Untuk tutorial ini, kami menganggap bahawa sudah ada contoh Zookeeper yang berdiri sendiri yang berjalan pada 127.0.0.1 : 1818 ; berikut adalah arahan mengenai cara memasangnya dan menjalankannya, jika anda baru memulakannya.

Pertama, kita perlu menambahkan pergantungan kurator-x-async ke pom.xml kami :

 org.apache.curator curator-x-async 4.0.1   org.apache.zookeeper zookeeper   

Versi terbaru Apache Curator 4.XX mempunyai kebergantungan keras dengan Zookeeper 3.5.X yang masih dalam versi beta sekarang.

Oleh itu, dalam artikel ini, kita akan menggunakan Zookeeper 3.4.11 stabil terkini.

Oleh itu, kita perlu mengecualikan ketergantungan Zookeeper dan menambahkan kebergantungan untuk versi Zookeeper kami ke pom.xml kami :

 org.apache.zookeeper zookeeper 3.4.11 

Untuk maklumat lebih lanjut mengenai keserasian, sila rujuk pautan ini.

3. Pengurusan Sambungan

Kes penggunaan asas Apache Curator adalah menyambung ke contoh Apache Zookeeper yang sedang berjalan .

Alat ini menyediakan kilang untuk membina sambungan ke Zookeeper menggunakan dasar percubaan semula:

int sleepMsBetweenRetries = 100; int maxRetries = 3; RetryPolicy retryPolicy = new RetryNTimes( maxRetries, sleepMsBetweenRetries); CuratorFramework client = CuratorFrameworkFactory .newClient("127.0.0.1:2181", retryPolicy); client.start(); assertThat(client.checkExists().forPath("/")).isNotNull();

Dalam contoh ringkas ini, kami akan mencuba semula 3 kali dan akan menunggu 100 ms antara percubaan sekiranya berlaku masalah sambungan.

Setelah disambungkan ke Zookeeper menggunakan klien CuratorFramework , kita kini dapat melihat jalan, mendapatkan / menetapkan data dan pada dasarnya berinteraksi dengan pelayan.

4. Penyegerakan

Modul Curator Async membungkus klien CuratorFramework di atas untuk memberikan keupayaan tanpa penyekat menggunakan API CompletionStage Java 8.

Mari lihat bagaimana contoh sebelumnya seperti menggunakan pembungkus Async:

int sleepMsBetweenRetries = 100; int maxRetries = 3; RetryPolicy retryPolicy = new RetryNTimes(maxRetries, sleepMsBetweenRetries); CuratorFramework client = CuratorFrameworkFactory .newClient("127.0.0.1:2181", retryPolicy); client.start(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); AtomicBoolean exists = new AtomicBoolean(false); async.checkExists() .forPath("/") .thenAcceptAsync(s -> exists.set(s != null)); await().until(() -> assertThat(exists.get()).isTrue());

Sekarang, operasi checkExists () berfungsi dalam mod tidak segerak, tidak menyekat utas utama. Kami juga dapat merantai tindakan satu demi satu menggunakan kaedah thenAcceptAsync () , yang menggunakan API PenyelesaianStage.

5. Pengurusan Konfigurasi

Dalam persekitaran yang diedarkan, salah satu cabaran yang paling biasa adalah menguruskan konfigurasi bersama di antara banyak aplikasi. Kita boleh menggunakan Zookeeper sebagai tempat menyimpan data untuk menyimpan konfigurasi kita.

Mari lihat contoh menggunakan Apache Curator untuk mendapatkan dan menetapkan data:

CuratorFramework client = newClient(); client.start(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); String key = getKey(); String expected = "my_value"; client.create().forPath(key); async.setData() .forPath(key, expected.getBytes()); AtomicBoolean isEquals = new AtomicBoolean(); async.getData() .forPath(key) .thenAccept(data -> isEquals.set(new String(data).equals(expected))); await().until(() -> assertThat(isEquals.get()).isTrue());

Dalam contoh ini, kami membuat jalan simpul, menetapkan data di Zookeeper, dan kemudian kami memulihkannya memeriksa bahawa nilainya sama. Yang utama bidang boleh menjadi laluan nod seperti / config / dev / my_key .

5.1. Pemerhati

Satu lagi ciri menarik di Zookeeper adalah keupayaan untuk melihat kunci atau nod. Ini membolehkan kita mendengarkan perubahan dalam konfigurasi dan mengemas kini aplikasi kita tanpa perlu menggunakan semula .

Mari lihat bagaimana contoh di atas ketika menggunakan pemerhati:

CuratorFramework client = newClient() client.start(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); String key = getKey(); String expected = "my_value"; async.create().forPath(key); List changes = new ArrayList(); async.watched() .getData() .forPath(key) .event() .thenAccept(watchedEvent -> { try { changes.add(new String(client.getData() .forPath(watchedEvent.getPath()))); } catch (Exception e) { // fail ... }}); // Set data value for our key async.setData() .forPath(key, expected.getBytes()); await() .until(() -> assertThat(changes.size()).isEqualTo(1));

Kami mengkonfigurasi pemerhati, menetapkan data, dan kemudian mengesahkan peristiwa yang ditonton dicetuskan. Kita dapat menonton satu nod atau satu set nod sekaligus.

6. Model yang sangat ditaip

Zookeeper terutamanya berfungsi dengan tatasusunan bait, jadi kita perlu membuat siri dan mendeserisasi data kita. Ini memungkinkan kita sedikit kelonggaran untuk bekerja dengan sebarang contoh yang dapat diselaraskan, tetapi sukar untuk dijaga.

Untuk membantu di sini, Kurator menambahkan konsep model yang ditaip yang mewakilkan serialisasi / deserialisasi dan membolehkan kami bekerja dengan jenis kami secara langsung . Mari lihat bagaimana ia berfungsi.

Pertama, kita memerlukan kerangka penyusun. Kurator mengesyorkan menggunakan pelaksanaan Jackson, jadi mari kita tambahkan kebergantungan Jackson ke pom.xml kami :

 com.fasterxml.jackson.core jackson-databind 2.9.4 

Sekarang, mari cuba teruskan HostConfig kelas khusus kami :

public class HostConfig { private String hostname; private int port; // getters and setters }

Kita perlu memberikan pemetaan spesifikasi model dari kelas HostConfig ke jalur, dan menggunakan pembungkus kerangka model yang disediakan oleh Apache Curator:

ModelSpec mySpec = ModelSpec.builder( ZPath.parseWithIds("/config/dev"), JacksonModelSerializer.build(HostConfig.class)) .build(); CuratorFramework client = newClient(); client.start(); AsyncCuratorFramework async = AsyncCuratorFramework.wrap(client); ModeledFramework modeledClient = ModeledFramework.wrap(async, mySpec); modeledClient.set(new HostConfig("host-name", 8080)); modeledClient.read() .whenComplete((value, e) -> { if (e != null) { fail("Cannot read host config", e); } else { assertThat(value).isNotNull(); assertThat(value.getHostname()).isEqualTo("host-name"); assertThat(value.getPort()).isEqualTo(8080); } });

Kaedah WhenComplete () ketika membaca path / config / dev akan mengembalikan instance HostConfig di Zookeeper.

7. Recipes

Zookeeper provides this guideline to implement high-level solutions or recipes such as leader election, distributed locks or shared counters.

Apache Curator provides an implementation for most of these recipes. To see the full list, visit the Curator Recipes documentation.

All of these recipes are available in a separate module:

 org.apache.curator curator-recipes 4.0.1 

Let's jump right in and start understanding these with some simple examples.

7.1. Leader Election

In a distributed environment, we may need one master or leader node to coordinate a complex job.

This is how the usage of the Leader Election recipe in Curator looks like:

CuratorFramework client = newClient(); client.start(); LeaderSelector leaderSelector = new LeaderSelector(client, "/mutex/select/leader/for/job/A", new LeaderSelectorListener() { @Override public void stateChanged( CuratorFramework client, ConnectionState newState) { } @Override public void takeLeadership( CuratorFramework client) throws Exception { } }); // join the members group leaderSelector.start(); // wait until the job A is done among all members leaderSelector.close();

When we start the leader selector, our node joins a members group within the path /mutex/select/leader/for/job/A. Once our node becomes the leader, the takeLeadership method will be invoked, and we as leaders can resume the job.

7.2. Shared Locks

The Shared Lock recipe is about having a fully distributed lock:

CuratorFramework client = newClient(); client.start(); InterProcessSemaphoreMutex sharedLock = new InterProcessSemaphoreMutex( client, "/mutex/process/A"); sharedLock.acquire(); // do process A sharedLock.release();

When we acquire the lock, Zookeeper ensures that there's no other application acquiring the same lock at the same time.

7.3. Counters

The Counters recipe coordinates a shared Integer among all the clients:

CuratorFramework client = newClient(); client.start(); SharedCount counter = new SharedCount(client, "/counters/A", 0); counter.start(); counter.setCount(counter.getCount() + 1); assertThat(counter.getCount()).isEqualTo(1);

In this example, Zookeeper stores the Integer value in the path /counters/A and initializes the value to 0 if the path has not been created yet.

8. Conclusion

Dalam artikel ini, kami telah melihat cara menggunakan Apache Curator untuk berhubung dengan Apache Zookeeper dan memanfaatkan ciri utamanya.

Kami juga telah memperkenalkan beberapa resipi utama dalam Kurator.

Seperti biasa, sumber boleh didapati di GitHub.