Kunci ialah alat yang sangat biasa dalam proses pembangunan Anda mesti biasa dengannya, kunci pesimis, kunci optimis, kunci eksklusif, adil kunci, Kunci tidak adil, dsb., banyak konsep Jika anda tidak memahami kunci dalam Java, anda boleh merujuk artikel ini: "Kunci" Java yang mesti disebutkan Artikel ini sangat komprehensif, tetapi untuk pemula, Mengetahui konsep daripada kunci ini, kerana kekurangan pengalaman kerja sebenar, mungkin tidak memahami senario penggunaan sebenar membuka kunci Di Java, keselamatan benang boleh dicapai melalui tiga kata kunci Volatile, Synchronized, dan ReentrantLock Bahagian pengetahuan ini akan disertakan dalam pusingan pertama temuduga asas Anda pasti akan bertanya (anda mesti mahir di dalamnya).
Dalam sistem yang diedarkan, teknologi kunci Java ini tidak boleh mengunci kod pada dua mesin pada masa yang sama, jadi ia mesti dilaksanakan melalui kunci yang diedarkan dengan mahir juga merupakan kemahiran yang mesti dikuasai oleh pemaju kilang besar.
Analisis masalah: Soalan ini digunakan terutamanya sebagai pengenalan Anda mesti memahami senario apa kunci yang diedarkan perlu digunakan dan masalah apa yang perlu diselesaikan oleh kunci yang diedarkan. Di bawah premis ini, ia akan membantu anda lebih memahami kunci pelaksanaan yang diedarkan.
Senario untuk menggunakan kunci teragih secara amnya perlu memenuhi senario berikut:
Sistem ialah sistem teragih dan kunci Java tidak boleh dikunci lagi.
Memanipulasi sumber yang dikongsi, seperti satu-satunya data pengguna dalam pustaka.
Akses segerak, iaitu berbilang proses mengendalikan sumber dikongsi pada masa yang sama.
Jawapan: Biar saya beritahu anda contoh senario di mana saya menggunakan kunci teragih dalam projek:
Mata penggunaan tersedia dalam banyak sistem, termasuk kad kredit dan laman web e-dagang. Mata boleh ditukar dengan hadiah, dsb. Operasi "makan mata" di sini adalah senario biasa yang memerlukan penggunaan kunci.
Mengambil mata penebusan untuk hadiah sebagai contoh, proses penggunaan mata yang lengkap hanya dibahagikan kepada 3 langkah:
A1: Pengguna memilih produk, memulakan pertukaran dan menyerahkan pesanan .
A2: Sistem membaca baki mata pengguna: untuk menentukan sama ada mata semasa pengguna mencukupi.
A3: Mata pengguna akan ditolak.
Sistem mengedarkan mata kepada pengguna dalam tiga langkah mudah:
B1: Kira mata yang layak diterima pengguna untuk hari itu
B2 : Baca mata asal pengguna
B3: Tambahkan mata yang sepatutnya kali ini kepada mata asal
Kemudian masalah datang, jika mata penggunaan pengguna dan mata terkumpul pengguna berlaku pada masa yang sama (mata pengguna dikendalikan pada masa yang sama) Apakah yang akan berlaku?
Andaian: Semasa pengguna menggunakan mata, tugas luar talian adalah mengira mata dan mengeluarkan mata kepada pengguna (contohnya, berdasarkan jumlah penggunaan pengguna pada hari itu). masa yang sama. Logik berikut agak berbelit, harap bersabar dan faham.
User U mempunyai 1,000 mata (data yang merekodkan mata pengguna boleh difahami sebagai sumber dikongsi), dan penebusan ini akan menggunakan 999 mata.
Situasi tidak berkunci: Apabila acara A program mencapai langkah kedua untuk membaca mata, hasil yang dibaca oleh operasi A:2 ialah 1000 mata Adalah dinilai bahawa mata yang tinggal sudah cukup untuk penebusan ini, dan maka langkah kedua dilaksanakan Langkah 3 A: Mata akan ditolak untuk 3 operasi (1000 - 999 = 1). Tetapi peristiwa B juga sedang dilaksanakan pada masa ini, 100 mata akan dikeluarkan kepada pengguna U. Dua utas akan melakukannya pada masa yang sama (akses segerak, akan ada kemungkinan berikut: A: 2 -> B :2 -> A:3 -> B:3, sebelum A:3 selesai (dipotong mata, 1000 - 999), jumlah mata pengguna dibaca oleh urutan acara B, dan akhirnya pengguna Jumlah mata U Ia menjadi 1100 mata, dan saya menebus hadiah sebanyak 999 mata dengan sia-sia, yang jelas tidak memenuhi keputusan yang diharapkan.
Sesetengah orang mengatakan bagaimana mungkin untuk mengendalikan mata pengguna pada masa yang sama secara kebetulan, dan CPU begitu pantas selagi terdapat pengguna yang mencukupi dan konkurensinya cukup besar, Undang-undang Murphy akan berkuat kuasa lambat laun. Ia hanya menunggu masa sebelum pepijat di atas muncul, dan ia mungkin digodam bahaya tersembunyi, anda mesti memahami penggunaan membuka kunci.
(Menulis kod adalah perkara yang ketat!)
Java sendiri menyediakan dua pelaksanaan kunci terbina dalam, satu disegerakkan oleh JVM dan Kunci yang disediakan oleh JDK Dan banyak operasi atom kelas adalah selamat untuk benang Apabila aplikasi anda adalah aplikasi yang berdiri sendiri atau proses tunggal, anda boleh menggunakan dua kunci ini untuk melaksanakan kunci.
Walau bagaimanapun, kebanyakan sistem semasa syarikat Internet diedarkan Pada masa ini, yang disegerakkan atau Lock yang disertakan dengan Java tidak lagi dapat memenuhi keperluan kunci dalam persekitaran yang diedarkan, kerana kod tersebut akan digunakan pada. berbilang mesin. Untuk menyelesaikan masalah ini, kunci yang diedarkan menjadi wujud penyelesaian pelaksanaan diedarkan kunci berdasarkan Redis atau kunci diedarkan ZooKeeper.
(Saya tidak dapat menganalisisnya dengan lebih terperinci, mengapa penemuduga tidak berpuas hati?)
Jawapan:
Kaedah 1: Gunakan perintah setnx untuk mengunci
public static void wrongGetLock1(Jedis jedis, String lockKey, String requestId, int expireTime) { // 第一步:加锁 Long result = jedis.setnx(lockKey, requestId); if (result == 1) { // 第二步:设置过期时间 jedis.expire(lockKey, expireTime); } }
Penjelasan kod:
setnx
arahan, yang bermaksud tetapkan jika tidak wujud. Jika lockKey tidak wujud, simpan kunci dalam Redis Jika keputusan mengembalikan 1 selepas berjaya disimpan, ia bermakna tetapan itu berjaya.
expire()
, tetapkan masa tamat tempoh untuk mengelakkan kebuntuan. Andaikan jika kunci tidak dipadamkan selepas ditetapkan, maka kunci akan sentiasa wujud, mengakibatkan kebuntuan.
(Pada ketika ini, saya ingin menekankan "tetapi" kepada penemuduga)
Fikir-fikirkan, apakah kelemahan kaedah saya di atas? Teruskan menerangkan kepada penemuduga...
Terdapat dua langkah dalam mengunci Langkah pertama ialah jedis.setnx, dan langkah kedua ialah jedis.expire untuk menetapkan masa tamat tempoh dan bukan an operasi atom. Jika program dilaksanakan selepas Pengecualian berlaku selepas langkah pertama Dalam langkah kedua, jedis.expire(lockKey, expireTime) tidak dilaksanakan, yang bermaksud bahawa kunci tidak mempunyai masa tamat tempoh, dan kebuntuan mungkin berlaku. . Bagaimana untuk memperbaiki masalah ini?
Peningkatan:
public class RedisLockDemo { private static final String SET_IF_NOT_EXIST = "NX"; private static final String SET_WITH_EXPIRE_TIME = "PX"; /** * 获取分布式锁 * @param jedis Redis客户端 * @param lockKey 锁 * @param requestId 请求标识 * @param expireTime 超期时间 * @return 是否获取成功 */ public static boolean getLock(Jedis jedis, String lockKey, String requestId, int expireTime) { // 两步合二为一,一行代码加锁并设置 + 过期时间。 if (1 == jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime)) { return true;//加锁成功 } return false;//加锁失败 } }
Penjelasan kod:
Gabungkan mengunci dan menetapkan masa tamat tempoh kepada satu, satu baris kod, operasi atom.
(Penemuduga sangat berpuas hati sebelum bertanyakan soalan lanjut)
Jawapan:
Melepaskan kunci bermakna memadamkan kunci
public static void unLock(Jedis jedis, String lockKey, String requestId) { // 第一步: 使用 requestId 判断加锁与解锁是不是同一个客户端 if (requestId.equals(jedis.get(lockKey))) { // 第二步: 若在此时,这把锁突然不是这个客户端的,则会误解锁 jedis.del(lockKey); } }
Penjelasan kod: Gunakan requestId untuk menentukan sama ada kunci dan buka kunci adalah sama Dua langkah klien dan jedis.del(lockKey) bukan operasi atom Secara teori, ia akan muncul selepas melaksanakan operasi penghakiman yang pertama bahawa kunci telah tamat tempoh dan telah diperolehi oleh benang lain ialah masa untuk melaksanakan operasi jedis.del(lockKey ) adalah bersamaan dengan melepaskan kunci orang lain, yang tidak munasabah. Sudah tentu, ini adalah situasi yang sangat melampau Jika tiada operasi perniagaan lain dalam langkah pertama dan kedua kaedah buka kunci, membuang kod di atas dalam talian mungkin tidak benar-benar menyebabkan masalah tidak tinggi, kecacatan ini tidak akan terdedah sama sekali, jadi masalahnya tidak besar.
Tetapi menulis kod adalah kerja yang ketat, dan untuk menjadi sempurna, anda mesti sempurna. Penambahbaikan dicadangkan untuk menangani masalah dalam kod di atas.
Peningkatan kod:
public class RedisTool { private static final Long RELEASE_SUCCESS = 1L; /** * 释放分布式锁 * @param jedis Redis客户端 * @param lockKey 锁 * @param requestId 请求标识 * @return 是否释放成功 */ public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) { String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId)); if (RELEASE_SUCCESS.equals(result)) { return true; } return false; } }
Penjelasan kod:
Gunakan kaedah eval klien jedis dan hanya satu baris skrip untuk menyelesaikan masalah atomicity yang terlibat dalam kaedah satu.
Jawapan: Ia masih merupakan contoh penggunaan mata dan pengumpulan mata: peristiwa A dan peristiwa B perlu untuk dijalankan pada masa yang sama Operasi pengubahsuaian mata dilakukan pada dua mesin pada masa yang sama Logik perniagaan yang betul adalah untuk membiarkan satu mesin melaksanakannya dahulu dan kemudian sama ada peristiwa A dilaksanakan terlebih dahulu, atau peristiwa B dilaksanakan terlebih dahulu, untuk memastikan bahawa A tidak akan berlaku :2 -> A:3 -> jenis pepijat masuk dalam talian, bos akan marah, saya mungkin menangis).
Apa yang perlu dilakukan? Gunakan kunci yang diedarkan oleh penjaga zoo.
Selepas mesin menerima permintaan, ia mula-mula mendapatkan kunci teragih pada zookeeper (zk akan mencipta znod) dan menjalankan operasi kemudian mesin lain juga cuba mencipta znod, tetapi mendapati ia tidak boleh menciptanya . , kerana ia dicipta oleh orang lain, anda hanya boleh menunggu sehingga mesin pertama selesai dilaksanakan sebelum anda boleh mendapatkan kunci.
Menggunakan ciri nod berjujukan ZooKeeper, jika kita mencipta 3 nod dalam direktori /lock/, gugusan ZK akan mencipta nod mengikut susunan nod tersebut terbahagi kepada /lock/. 0000000001, /lock/0000000002 , /lock/0000000003, digit terakhir ditambah dalam urutan, dan nama nod dilengkapkan dengan zk.
ZK juga mempunyai nod yang dipanggil nod sementara Nod sementara dicipta oleh klien Apabila klien memutuskan sambungan daripada gugusan ZK, nod tersebut dipadamkan secara automatik. EPHEMERAL_SEQUENTIAL ialah nod jujukan sementara.
Logik asas kunci teragih ialah menggunakan kehadiran atau ketiadaan nod dalam ZK sebagai status kunci untuk melaksanakan kunci teragih
Pelanggan memanggil create() kaedah Cipta nod jujukan sementara bernama "/dlm-locks/lockname/lock-".
Pelanggan memanggil kaedah getChildren("nama kunci") untuk mendapatkan semua nod anak yang dicipta.
Selepas pelanggan memperoleh laluan semua nod anak, jika ia mendapati bahawa nod yang dicipta dalam langkah 1 mempunyai nombor urutan terkecil antara semua nod, ia akan menyemak sama ada nombor jujukan ia mencipta kedudukan pertama, jika ia adalah yang pertama, maka ia dianggap bahawa pelanggan ini telah memperoleh kunci, dan tiada pelanggan lain telah memperoleh kunci sebelum itu.
Jika nod yang dicipta bukan yang terkecil daripada semua nod, maka adalah perlu untuk memantau nod terbesar dengan nombor urutan yang lebih kecil daripada nod yang diciptanya, dan kemudian masukkan keadaan menunggu . Selepas nod anak yang dipantau berubah, dapatkan nod anak dan tentukan sama ada untuk mendapatkan kunci.
Walaupun proses melepaskan kunci agak mudah, yang sebenarnya memadamkan nod anak yang dicipta, anda masih perlu mempertimbangkan situasi tidak normal seperti kegagalan pemadaman nod.
Kunci yang diedarkan juga boleh menyelesaikan masalah daripada pangkalan data
利用 Mysql 的锁表,创建一张表,设置一个 UNIQUE KEY 这个 KEY 就是要锁的 KEY,所以同一个 KEY 在mysql表里只能插入一次了,这样对锁的竞争就交给了数据库,处理同一个 KEY 数据库保证了只有一个节点能插入成功,其他节点都会插入失败。
这样 lock 和 unlock 的思路就很简单了,伪代码:
def lock : exec sql: insert into locked—table (xxx) values (xxx) if result == true : return true else : return false def unlock : exec sql: delete from lockedOrder where order_id='order_id'
使用流水号+时间戳做幂等操作,可以看作是一个不会释放的锁。
Atas ialah kandungan terperinci Bagaimana untuk melaksanakan kunci teragih Redis dan apakah senario aplikasinya. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!