1. Pengantar Kunci Terdistribusi
Kunci terdistribusi terutama digunakan untuk melindungi sumber daya bersama di seluruh proses, di seluruh host, dan di seluruh jaringan dalam lingkungan terdistribusi untuk mencapai akses yang saling eksklusif untuk memastikan konsistensi data.
2. Pendahuluan Arsitektur
Sebelum memperkenalkan penggunaan Zookeeper untuk mengimplementasikan kunci terdistribusi, pertama -tama lihat diagram arsitektur sistem saat ini
Penjelasan: Seluruh area di sebelah kiri mewakili kluster Zookeeper. Locker adalah simpul zookeeper yang persisten , dan node_1, node_2, dan node_3 adalah node berurutan sementara di bawah simpul loker yang persisten. Client_1, Client_2, Client_N berarti banyak klien, dan layanan berarti sumber daya bersama yang membutuhkan akses yang saling eksklusif.
Ide untuk perolehan kunci terdistribusi
1. Gagasan keseluruhan untuk mendapatkan kunci terdistribusi
Saat memperoleh kunci terdistribusi, buat simpul sekuensial sementara di bawah node loker, dan hapus simpul sementara saat melepaskan kunci. Klien memanggil metode CreateNode untuk membuat node berurutan sementara di bawah Locker, dan kemudian memanggil getChildren ("Locker") untuk mendapatkan semua node anak di bawah Locker. Perhatikan bahwa tidak ada pengamat yang diperlukan saat ini. Setelah klien memperoleh semua jalur simpul anak, jika menemukan bahwa nomor simpul anak yang dibuat sebelumnya adalah yang terkecil, dianggap bahwa klien telah memperoleh kunci. Jika Anda menemukan bahwa simpul yang Anda buat bukan yang terkecil di antara semua anak dari loker, itu berarti Anda belum mendapatkan kunci. Pada saat ini, klien perlu menemukan node lebih kecil dari itu, dan kemudian memanggil metode yang ada () di atasnya, dan mendaftarkan pendengar acara di atasnya. Setelah itu, jika simpul yang Anda ketahui dihapus, pengamat klien akan menerima pemberitahuan yang sesuai. Pada saat ini, Anda akan menentukan lagi apakah simpul yang Anda buat adalah nomor seri terkecil di antara node anak loker. Rugao telah memperoleh kunci. Jika tidak, ulangi langkah -langkah di atas untuk terus mendapatkan simpul yang lebih kecil dari Anda dan mendaftar untuk mendengarkan. Masih ada banyak penilaian logis yang diperlukan dalam proses saat ini.
2. Proses algoritma inti untuk memperoleh kunci terdistribusi
Berikut ini adalah diagram alur yang sama untuk menganalisis algoritma lengkap untuk memperoleh kunci terdistribusi, sebagai berikut:
Penjelasan: Ketika klien A ingin memperoleh kunci terdistribusi, pertama-tama buat node sekuensial sementara (Node_N) di bawah loker, dan kemudian segera dapatkan semua node anak (tingkat pertama) di bawah loker.
Pada saat ini, karena banyak klien akan bersaing untuk mengunci pada saat yang sama, jumlah node anak di bawah loker akan lebih besar dari 1. Untuk node berurutan, karakteristiknya adalah bahwa ada nomor numerik setelah nama simpul. Jumlah nomor simpul yang dibuat terlebih dahulu lebih kecil dari yang dibuat nanti. Oleh karena itu, node anak dapat diurutkan dari kecil ke besar dalam urutan akhiran nama simpul. Dengan cara ini, yang pertama adalah simpul berurutan yang dibuat terlebih dahulu. Pada saat ini, itu mewakili klien yang pertama kali berusaha untuk kunci! Pada saat ini, tentukan apakah simpul terkecil adalah node_n yang dibuat oleh klien A sebelumnya. Jika demikian, itu berarti bahwa klien A telah memperoleh kunci. Jika tidak, itu berarti bahwa kunci telah diperoleh oleh klien lain. Oleh karena itu, klien A harus menunggu untuk melepaskan kunci, yaitu, klien B yang telah memperoleh kunci menghapus simpul yang dibuatnya.
Pada saat ini, kita akan tahu apakah klien B telah merilis kunci dengan mendengarkan acara penghapusan node berurutan yang lebih kecil dari waktu node_n. Jika demikian, klien A memperoleh semua anak di bawah Locker lagi dan membandingkannya dengan node_n node yang dibuat dengan sendirinya sampai Node_n yang dibuat dengan sendirinya adalah nomor urutan terkecil di antara semua anak dari Locker, yang berarti bahwa klien A telah memperoleh kunci!
4. Kode Implementasi Kunci Terdistribusi Berdasarkan Zookeeper
1. Tentukan antarmuka kunci terdistribusi
Antarmuka kunci terdistribusi yang ditentukan adalah sebagai berikut:
Public Interface DistributedLock { / ** memperoleh kunci, jika tidak diperoleh, tunggu* / public void acquire () melempar pengecualian; / *** Memperoleh kunci sampai timeout* @param waktu waktu batas waktu* @param unit unit unit waktu unit parameter* @return apakah kunci diperoleh* @throws Exception*/ public boolean memperoleh (waktu lama, unit timeunit) melempar pengecualian; / *** Lepaskan kunci* @throws Exception*/ rilis public void () melempar pengecualian;}2. Tentukan mutex sederhana
Tentukan kelas kunci mutex, mengimplementasikan antarmuka kunci yang ditentukan di atas, dan mewarisi kelas berbasis kelas dasar. Kelas dasar ini terutama digunakan untuk berinteraksi dengan Zookeeper, termasuk metode untuk mencoba memperoleh kunci dan kunci pelepas.
/** Implementasi spesifik dari antarmuka kunci terutama dicapai oleh kelas induk yang diwariskan berdasarkan CLOP. Kelas induk diimplementasikan berdasarkan perincian spesifik ZooKeeper untuk mengimplementasikan kunci terdistribusi* /kelas publik SimplEtributedLockMutex memperluas implementasi yang didistribusikan oleh DistributedLock { /*yang digunakan untuk menyimpan node yang mengimplementasikan kunci yang didistribusikan dalam node ini, seperti pemecatan nama. Buat node sekuensial sementara di bawah node ini untuk mengimplementasikan kunci terdistribusi*/ private final string basepath; /*Awalan nama kunci. Misalnya, node berurutan yang dibuat di bawah loker mulai dengan lock-, yang memfasilitasi penyaringan node yang tidak relevan*Node yang dibuat mirip dengan: LOCK-00000001, LOCK-00000002*/ STATICFINAL STATICFINAL STATICAL LOCK_NAME = "LOCK-"; /* Digunakan untuk menyimpan node berurutan yang berhasil dibuat oleh klien di bawah Locker, untuk operasi terkait berikutnya (seperti penilaian)*/ Private String OurLockPath; /** * Used to acquire lock resources, and obtain locks through the parent class's lock acquisition method* @param time to acquire the timeout time of the lock* @param unit time unit time* @return Whether the lock is obtained* @throws Exception */ private boolean internalLock (long time, TimeUnit unit) throws Exception { //If ourLockPath is not empty, it is considered that the lock has been obtained. Untuk detailnya, silakan merujuk ke implementasi TREPLOCK. Ourlockpath = cowok (waktu, unit); kembalikan OurLockPath! = NULL; } /** * Pass in the Zookeeper client connection object, and basePath * @param client Zookeeper client connection object* @param basePath basePath is a persistent node*/ public SimpleDistributedLockMutex(ZkClientExt client, String basePath){ /*Calling the constructor of the parent class creates a basePath node in Zookeeper, and sets a prefix for the basePath node child Node *Simpan referensi Basepath ke atribut kelas saat ini */ super (klien, basepath, lock_name); this.basepath = Basepath; } /** memperoleh kunci sampai batas waktu, dan pengecualian dilemparkan setelah batas waktu* /public void acquire () melempar pengecualian {//-1 berarti bahwa batas waktu tidak ditetapkan, dan batas waktu ditentukan oleh zookeeper if (! InternalSeP (-1, null)) {lempar ioeksepsi baru ("Kontining" Connection! }} / *** memperoleh kunci dengan batas waktu* / boolean publik memperoleh (lama, unit timeunit) melempar pengecualian {return internallock (waktu, unit); } / ** Rilis lock* / rilis public void () melempar Exception {releaselock (ourlockpath); }}3. Rincian implementasi kunci terdistribusi
Logika kunci untuk mengakuisisi kunci terdistribusi didasarkan pada Bok, yang mengimplementasikan rincian penerapan kunci terdistribusi berdasarkan Zookeeper.
Public Class BasedistributedLock {Private Final ZkClientext Client; jalur string final pribadi; Basepath String Akhir Pribadi; Private Final String LockName; Private Static Final Integer max_retry_count = 10; Public -BasedistributedLock (ZKClientext Client, String Path, String LockName) {this.client = Client; this.basePath = path; this.path = path.concat ("/"). concat (lockname); this.lockname = lockname; } private void deleteourpath (String ourpath) melempar Exception {client.delete (ourpath); } private string createLockNode (klien zkclient, string path) melempar Exception {return client.createEphemeralequential (path, null); } / ** * Metode inti untuk mendapatkan kunci * @param startmillis * @param millistowait * @param ourpath * @return * @throws Exception * / private boolean waittolock (startmillis panjang, millistowait panjang, string ourpath) melempar pengecualian {boolean havethelock = false; boolean dodelete = false; Coba {while (! Havethelock) {// Metode ini mengimplementasikan akuisisi semua node berurutan di bawah node loker, dan mengurutkan dari daftar kecil hingga besar <string> anak -anak = getSortedChildren (); String sequencenodename = ourpath.substring (BasePath.length ()+1); // Hitung posisi penyortiran node pesanan yang dibuat oleh klien sekarang di semua node anak dari loker. Jika jenisnya 0, itu berarti bahwa kunci telah diperoleh. int ourIndex = anak -anak. /*Jika simpul pesanan [sementara] yang saya buat sebelumnya tidak ditemukan di getSortedChildren, ini berarti bahwa simpul yang kami buat dapat dihapus karena jeda flash jaringan. Pengecualian perlu dilemparkan. Biarkan level sebelumnya menangani *level sebelumnya adalah untuk menangkap pengecualian dan menjalankan jumlah retry yang ditentukan. Lihat metode cuplock dalam metode cuplock berikutnya*/ if (OurIndex <0) {Throw New ZknonodeException ("Tidak Ditemukan:" + SequencenOdename); } // Jika simpul yang dibuat oleh klien saat ini lebih besar dari 0 dalam daftar node anak loker, itu berarti bahwa klien lain telah memperoleh kunci // saat ini, klien saat ini perlu menunggu klien lain untuk melepaskan kunci, boolean isGetthelock = ourindex == 0; // Bagaimana cara menentukan apakah klien lain telah merilis kunci? Dapatkan node lebih kecil dari dirinya sendiri dari daftar node anak dan atur sesi mendengarkan untuk TI string pathTowatch = isGetThelock? null: anak -anak. Get (OurIndex - 1); if (isGetthelock) {havethelock = true; } else {// Jika simpul yang lebih kecil dihapus, itu berarti bahwa simpul klien saat ini harus menjadi yang terkecil, jadi gunakan CountdownLatch untuk mewujudkan string waiting sebelumnyaPath = Basepath .concat ("/") .concat (PathToWatch); Latch Final CountdownLatch = CountdownLatch baru (1); final IZKDATALISTERer PriorListener = new izkDataListener () {// Ketika acara penghapusan simpul kecil terjadi, biarkan Countdownlatch berakhir dan tunggu // saat ini, Anda perlu membiarkan program kembali ke saat lagi dan membuat penilaian baru! public void handleDaDeleted (String Datapath) melempar Exception {latch.countdown (); } public void handledatachange (string datapath, data objek) melempar Exception {// abaikan}}; coba {// Pengecualian Jika node tidak ada klien. if (millistowait! = null) {millistowait - = (System.currentTimeMillis () - startMillis); startMillis = system.currentTimemillis (); if (millistowait <= 0) {dodelete = true; // Timeed Out - Hapus Node Break kami; } latch.Await (millistowait, timeunit.microseconds); } else {latch.aWait (); }} catch (zknonodeException e) {// abaikan} akhirnya {client.unsubscribedatachanges (sebelumnya equestrePath, priorListener); }}}}} catch (Exception e) {// Pengecualian perlu dihapus dodelete = true; lempar e; } akhirnya {// jika Anda perlu menghapus node if (dodelete) {deleteourpath (ourpath); }} return havethelock; } private string getLockNodEnumber (string str, string lockName) {int index = str.LastIndexof (lockName); if (index> = 0) {index += lockName.length (); indeks pengembalian <= str.length ()? str.substring (indeks): ""; } return str; } Daftar Privat <String> getSortedChildren () melempar Exception {coba {Daftar <String> anak -anak = client.getChildren (BasePath); Collections.sort (anak -anak, pembanding baru <string> () {public int compare (string LHS, string rhs) {return getLockNodenumber (lhs, lockname) .compareto (getLockNodenumber (RHS, LockName));}}); anak -anak yang kembali; } catch (zknonodeException e) {client.createPersistent (Basepath, true); return getsortedchildren (); }} void releaselock yang dilindungi (String LockPath) melempar Exception {deleteourpath (LockPath); } String TROWLOCK yang Diprotes (Long Time, TimeUnit Unit) melempar Exception {final long startMillis = system.currentTimeMillis (); final long millistowait = (unit! = null)? unit.tomillis (waktu): null; String ourpath = null; boolean hasthelock = false; boolean isDone = false; int retrycount = 0; // net flash break membutuhkan Retry while (! IsDone) {isDone = true; Coba {// CreateLockNode digunakan untuk membuat simpul pesanan [sementara] untuk klien untuk memperoleh kunci di bawah loker (node persisten basepath). Ourpath = createLockNode (klien, path); /** * This method is used to determine whether the lock has been obtained, that is, whether the order nodes created by yourself are the smallest among all child nodes of the locker* If the lock is not acquired, wait for the release of the lock to be released, and try again later until the lock is acquired or timed out*/ hasTheLock = waitToLock(startMillis, millisToWait, ourPath); } catch (zknonodeException e) {if (retrycount ++ <max_retry_count) {isDone = false; } else {throw e; }}}} if (hasthelock) {return ourpath; } return null; }Di atas adalah semua konten artikel ini. Saya berharap ini akan membantu untuk pembelajaran semua orang dan saya harap semua orang akan lebih mendukung wulin.com.