Bilinmeyen dağıtılmış kilit uygulamalarının hepsi burada

1 İş senaryolarını tanıtın

İlk olarak, bir sahne tarafından tanıtıldı:

Son zamanlarda, patron büyük bir sipariş aldı ve APP'mizi belirli bir terminal cihazına yüklememize izin verdi. Terminal ekipmanı üreticisinin günlük faaliyeti en az yüz binlerce ila bir milyon arasında olmalıdır. Bu APP aynı zamanda pazardaki rakiplerin analizine göre tasarlanmış yeni bir üründür. Birkaç küçük kod Nong Xiao Da Dan tarafından geliştirilen ana işlev, tek noktadan çevrimiçi alışveriş hizmetidir.Arkaplan, satılması gereken ürün bilgilerini korumak için çeşitli satıcılara izinler atayabilir.

Patron büyük O : Hakkında konuşmak kolay değil. Bir sonraki adım, terminal ekipmanına kaydolmak için daha fazla kullanıcıyı nasıl çekeceğinizi ve kullanıcıları satın almaya nasıl yönlendireceğinizi düşünmektir. Bu, küçük P.'ye teslim edilecek. Bunu mümkün olan en kısa sürede yapmam gerekiyor. Yarın iş başında olacağım!

Ürün küçük P : Hehe ~, gözlerimi devirdiğimde bunu düşünmek kolaydı ve düşündüm: "Kolay değil, en azından ana sayfada bir etkinlik sayfası yap ...".

Teknik küçük T: Ürünün ihtiyaçlarını hızlı bir şekilde anladım.Şu anda bunun başlıca sorumlusu Xiao J.Aktivite sayfasını neredeyse yapacak olan ön uç ve arka uç sınıf arkadaşları buldum.

İş senaryosu ortaya çıkar çıkmaz :

Çünkü Xiao T projeyi yeni devraldı ve artık kod ve dağıtım mimarisine aşina. Koda bakma sürecinde, sipariş verirken sorunlar olabileceğini fark ettim. Bu dağıtılmış bir dağıtımdır. Birden fazla kullanıcı aynı ürünü aynı anda satın alırsa, aşırı satıma neden olabilir (tutarsız veriler). Bunun için Bu durumda kodda kontrol yoktur.

Sorduğumda, bunların hepsinin geçmişte satılan sanal ürünler olduğu ve envanter olmadığı ortaya çıktı, bu yüzden o kadar düşünmedim ...

Bu sefer farklı. Bu sefer satış için fiziksel bir ürün olduğu için envanter olduğu söyleniyor. En azından belirlenen envanter miktarını aşamayacağı garanti edilmeli.

Little T ekrana iri gözlerle baktı ve nefesini tuttu. Neyse ki, bu sorunu önceden keşfetti ve çabucak çözmenin bir yolunu buldu. Para kazanmasaydı, para kaybedecekti. Patron deli olamazdı ve yine de yapmak istiyor ~

İşletme senaryosu iki görünür :

Küçük T'nin altındaki bir kardeş basınç testi altında ve küçük bir sorun buldu, çünkü terminal ekipmanında kaz fabrikası ile yakın işbirliği var ve arayüzlerini çağırırken erişim_token elde edilmesi gerekiyor, ancak bu erişim belirtecinin sona erme süresi 2 saat ve süresinin dolması gerekiyor. Yeniden edinin.

Stres testi sırasında, sona erme süresine ulaşıldığında, günlüğün birkaç farklı access_tokens gösterdiği, çünkü bu hizmet aynı zamanda dağıtılmış bir şekilde konuşlandırıldı ve çoklu düğümlerin aynı anda üçüncü taraf arayüz isteklerini başlattığı bulundu.

En son elde edilen erişim belirteci geçerli olmasına ve istenmeyen yan etkileri olmamasına rağmen, üçüncü taraf arabirimlerine çok sayıda gereksiz çağrıya neden olacak ve ayrıca kısa bir süre içinde erişim belirtecinin tekrar tekrar geçersiz edinilmesine neden olacaktır (yinelenen çalışma).

Üç iş senaryosu görünür :

Sipariş verildikten sonra, depolama ve lojistik bildirilmelidir. Kullanıcının ödemesi tamamlandıktan sonra, ödeme geri araması MQ'ya birden fazla sipariş mesajı gönderebilir. Depolama hizmeti, MQ'dan gelen sipariş mesajlarını tüketecektir. Şu anda, idempotence sağlamak gerekir. Mesaj tekilleştirilir.

Yukarıdakiler, herkesin sorunu çözmek için neden dağıtılmış bir kilidin kullanılabileceğini anlaması kolaydır ve birkaç iş senaryosu özetlenmiştir.

Yukarıdaki sorunların hiçbir istisnası yoktur, güvenli ve makul işlemleri sağlamak için hepsi paylaşılan kaynaklar için serileştirmeyi gerektirir.

Deneyimlemek için bir resim kullanın:

Şu anda, Synchronized, ReentrantLock, ReentrantReadWriteLock kullanımı ... Java tarafından sağlanan tek bir JVM işleminde çok iş parçacıklı ve paylaşılan kaynaklar için yalnızca iş parçacığı güvenliğini garanti edebilir. Dağıtılmış bir sistem ortamında, tümünün kullanımı kolay değildir. Üşüyor musunuz? Evet.

Dağıtılmış kilit ailesinden bu sorunu desteklemesini istemeliyim.Ailelerinde çok sayıda üye olduğunu ve her üyenin bu dağıtılmış kilit işlevine sahip olduğunu duydum, sonra keşfetmeye başlayacağım.

2 Dağıtılmış Kilit Ailesi Üyelerine Giriş

Çözmek için neden dağıtılmış kilitlere ihtiyacımız var?

Martin'in büyük adamlarının ne verdiğini dinleyin:

Martin Kleppmann, Birleşik Krallık'taki Cambridge Üniversitesi'nde dağıtılmış sistemler araştırmacısıdır. Bir zamanlar Redis'in babası Antirez ile RedLock'un güvenliği (Redis'teki dağıtılmış kilitlerin uygulama algoritması) hakkında hararetli bir tartışma yapmıştı.

Ne tartıştılar, bu kadar endişeli mi? Tek başına bir makale yazabilir

Lütfen Maritin'in blog gönderisini kendiniz okuyun:

https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html

etkinlik:

Dağıtılmış kilitlerin kullanılması, birden fazla müşterinin aynı işi tekrar etmesini önleyebilir ve bu da kaynakları israf eder. Örneğin, kullanıcı ödemeyi tamamladıktan sonra birden çok SMS veya e-posta hatırlatması alabilir.

Örneğin, işletme senaryosu 2'de erişim belirteci tekrar tekrar elde edilir.

Paylaşılan kaynaklar üzerindeki işlemler idempotenttir, kaç kez çalışırsanız çalışın, farklı sonuçlar olmayacaktır. Esasen, paylaşılan kaynaklar üzerinde tekrarlanan işlemlerden kaçınmak ve böylece verimliliği artırmaktır.

Doğruluk:

Dağıtılmış kilitlerin kullanılması, kilit arızasının oluşmasını da önleyebilir. Bir kez meydana geldiğinde, doğruluk hasarına neden olur ve bu da veri tutarsızlığına, veri kaybına veya diğer ciddi sorunlara yol açabilir.

Örneğin, birinci işletme senaryosu aşırı satım emtia envanteri sorunudur.

Paylaşılan kaynaklar üzerindeki işlemler idempotent değildir ve paylaşılan kaynaklardaki birden çok istemci işlemi veri tutarsızlığına neden olur.

Dağıtılmış kilitlerin özellikleri nelerdir?

Aşağıdakiler, dağıtılmış kilitlerin özelliklerinden bazılarıdır: Dağıtılmış kilit ailesinin tüm üyeleri bu gereksinimi karşılamaz ve uygulama mekanizmaları farklıdır.

Karşılıklı dışlama: Dağıtılmış kilitler, birden çok istemci arasında karşılıklı dışlamayı sağlamalıdır.

Yeniden giriş: Aynı istemcinin aynı iş parçacığı birden çok kez kilitlenebilir.

Kilit zaman aşımı: Kilitlenmeyi önlemek için yerel kilit gibi kilit zaman aşımını destekleyin.

Engellemeyen: ReentrantLock gibi, kilidi engellemeyen bir şekilde elde etmek için trylock () 'u destekler.

Adil kilit ve haksız kilidi destekleyin: Adil kilitler, kilitlerin istendiği sırada kilit elde edilmesini ifade eder.Adil olmayan kilitler gerçekten iyidir, aksine kilit istemek bozuktur.

Dağıtılmış Kilit Ailesi Uygulayıcılarına Giriş

Dağıtılmış kilit ailesinin uygulayıcılarının listesi:

Zihin haritası, özellikle kesin olması gerekmeyen ve neredeyse dağıtılmış kilitlerin çeşitli bileşenlerinin uygulayıcılarını içeren basit bir sınıflandırma yaptı.

Kendilerini ayrı ayrı tanıtmalarına izin verin:

1. Veritabanı

Özel kilit (kötümser kilit): SQL deyimini güncellemek için xx = yy olan tablodan seç * 'e dayalı olarak, daha sonra açıklanacak, genellikle kullanılması tavsiye edilmeyen birçok kusur vardır.

İyimser kilit: elde etmek için tabloya bir zaman damgası veya sürüm numarası alanı ekleyin, xx set version = new ... burada id = y ve version = old Güncelleme başarısız olduğunda, müşteri yeniden dener ve en son sürümü tekrar okuyun Numara veya zaman damgası, CAS mekanizmasına benzer şekilde tekrar güncellemeyi deneyin, önerilir.

2. Redis

Özellikleri: CAP modeli, AP | Fikir birliği algoritması yok | İyi performans

Yaygın olarak geliştirmede kullanılır.Projenizde redis kullanırsanız ve ek dağıtılmış kilit bileşenleri eklemek istemezseniz, önerilir.

Endüstri ayrıca dağıtılmış kilitleri desteklemek için bir dizi kullanıma hazır çerçeve sağlar. Redisson , bahar-entegrasyon-redis Redis ile birlikte gelir setnx Komutun doğrudan kullanılması tavsiye edilir.

Ek olarak, redis komutları ve redis lua tarafından desteklenen atomik özelliklere dayalı olarak dağıtılmış kilitleri kendiniz uygulayabilirsiniz.

3. Hayvan Yetkilisi

Özellikleri: CAP modeli, CP | ZAB Tutarlılık algoritması uygulaması | İyi istikrar

Genellikle geliştirme için kullanılır, projenizde zk kümesini kullanırsanız, önerilir.

Sektör var Apache Küratörü Çerçeve, hazır dağıtılmış bir kilit işlevi sağlar, hazırdır, doğrudan kullanılması önerilir.

Ek olarak, dağıtılmış kilitler Zookeeper'in kendi özelliklerine ve yerel Zookeeper API'sine göre uygulanabilir.

4. Diğer

Tombul , Google kaba taneli dağıtım kilitleme hizmetini geliştirdi, ancak bu açık kaynak değil. Belgeler ve bazı ilgili belgeler hakkında daha fazla bilgi edinebilirsiniz. Belgeleri almak için Baidu'ya gidin ve çok fazla tartışma yapmayın.

Tair , Ali'nin açık kaynak kodlu geliştirdiği dağıtılmış bir KV depolama çözümüdür, daha önce kullanmadım ve fazla tartışmıyorum.

Etcd , CAP modeline ait CP , Sal Mutabakat algoritması uygulanmıştır ve daha önce kullanılmamıştır ve çok fazla tartışılmayacaktır.

Hazelcast , Esnek ve ölçeklenebilir dağıtılmış bellek bilgi işlem sağlayan bellek tabanlı veri ızgarası açık kaynaklı bir projedir ve uygulama performansını ve ölçeklenebilirliği iyileştirmek için en iyi çözüm olarak kabul edilir. Harika görünüyor, ancak daha önce kullanmadım, yapma Çok fazla tartışma.

Elbette, yukarıda tavsiye edilen Zookeeper ve Redis'in yaygın olarak kullanılan dağıtılmış kilitleri, istediğiniz işlevi elde etmek için belirli iş senaryolarına göre tartılmalıdır. Prensip çok farklıdır.

Seslendirme: Hangisine aşina olursanız olun, ilkeyi de anlarsınız, tutun, hangisini kullanırsanız kullanın.

3 Dağıtılmış kilit üyelerinin gerçekleşme ilkesinin analizi

Veritabanı kötümser kilit uygulaması

Kaynakları "kötümser bir zihniyetle" çalıştırın ve kilidi başarılı bir şekilde elde edemezsiniz, bu nedenle beklemede bloke edildiler.

1. Bir kaynak kilit tablosu var

TABLO OLUŞTUR "resource_lock" ( "id" int (4) NULL NOT NULL AUTO_INCREMENT COMMENT'birincil anahtar ', `resource_name` varchar (64) NOT NULL DEFAULT '' COMMENT'Kilitli kaynak adı ', `owner` varchar (64) NULL DEFAULT NOT '' COMMENT'lock owner ', `desc` varchar (1024) NOT NULL DEFAULT'Bilgileri açıklar ', `update_time` timestamp NOT NULL DEFAULT '' COMMENT 'Veri zamanından tasarruf edin, otomatik olarak oluşturulur', BİRİNCİL ANAHTAR ("kimlik"), EŞSİZ ANAHTAR "uidx_resource_name` (" resource_name ") BTREE KULLANILARAK ) MOTOR = InnoDB DEFAULT CHARSET = utf8 COMMENT = 'Kaynak kilitli';

resource_name Kilit kaynağı adının benzersiz bir dizini olmalıdır.

2. Duruşu kullanın

İşlemler eklenmeli, sorgulama ve güncelleme işlemlerinin atomik olması ve tek işlemde tamamlanması garanti edilmektedir.

Sözde kod uygulaması:

@Transaction public void lock (Dize adı) { ResourceLock rlock = exeSql ("resource_lock'tan * seçin, burada resource_name = güncelleme adı"); eğer (rlock == null) { exeSql ("resource_lock'a ekle (reosurce_name, owner, count) değerleri (ad, 'ip', 0)"); } }

Güncelleme kilitli kaynakları için kullanın. Yürütme başarılı olursa, hemen geri dönecek, veritabanını ekleyecek ve ardından işlem tamamlanana ve yürütme bitene kadar başka bir iş mantığı yürütecektir; yürütme başarısız olursa, her zaman engellenecektir.

Bu etkiyi veritabanı istemcisi aracında da test edebilirsiniz.Güncelleme için bir terminalde yürütüldüğünde, işlem taahhüt edilmez. Başka bir terminalde aynı koşullarla güncelleme için yürütürseniz, takılıp kalmaya devam eder ...

Dağıtılmış kilitlerin etkisi de elde edilebilmesine rağmen, performans darboğazları olacaktır.

3. Kötümser kilitlemenin avantajları ve dezavantajları

avantaj: Kullanımı kolay, anlaşılması kolay ve güçlü veri tutarlılığı sağlar.

Dezavantaj Hadi bir liste yapalım:

1) RR işlem seviyesinde, seçimin güncelleme işlemi, kötümser bir kilit uygulaması olan boşluk kilidine dayalı olarak gerçekleştirilir, bu nedenle bir engelleme sorunu vardır.

2) Yüksek eşzamanlılık durumunda, isteklerin çoğunun sıraya alınmasına neden olan çok sayıda istek gelir, bu da veritabanının kararlılığını etkiler ve ayrıca hizmet CPU gibi kaynakları tüketir.

İstemci kilidi bekleyen çok uzun olduğunda, şu soruyu soracaktır:

Kilit bekleme zaman aşımı aşıldı; işlemi yeniden başlatmayı deneyin

Yüksek eşzamanlılık durumunda, aynı zamanda çok fazla uygulama iş parçacığının meşgul olmasına ve işletmenin normal şekilde yanıt vermemesine neden olacaktır.

3) Kilidi ilk alan iplik herhangi bir nedenle kilidi açmamışsa, kilitlenme meydana gelebilir.

4) Kilit uzun bir süre serbest bırakılmazsa, veritabanı bağlantısı her zaman meşgul olur ve bu, veritabanı bağlantı havuzunu patlatabilir ve diğer hizmetleri etkileyebilir.

5) MySql veritabanı sorgu optimizasyonu yapacak Bir indeks kullanılsa bile, optimizasyon sırasında tam tablo tarama verimliliği daha yüksek bulunursa, satır kilidi bir tablo kilidine yükseltilebilir, bu daha da trajik olabilir.

6) Reentrant özellikleri desteklenmez ve zaman aşımı bekleme süresi geneldir ve keyfi olarak değiştirilemez.

Veritabanı iyimser kilit uygulaması

İyimser kilitleme, paylaşılan kaynakları "iyimser bir zihniyetle" çalıştırma, kilidi başarılı bir şekilde alamıyorum, bir süre sonra tekrar denemekte sorun yok, işe yaramazsa, sadece çıkın, belirli sayıda deneyin veya denemeyin? Bunun hakkında daha sonra konuşabilirsiniz, böylece engellemeye ve beklemeye devam etmek zorunda kalmazsınız.

1. Bir kaynak tablosu var

Tabloya sürüm numarası veya zaman damgası olmak üzere bir alan ekleyin. Sürüm numarası veya zaman damgası, aynı anda paylaşılan kaynaklar üzerindeki çok iş parçacıklı işlemlerin sırasını ve doğruluğunu sağlamak için kullanılır.

TABLO "kaynak" ( "id" int (4) NULL NOT NULL AUTO_INCREMENT COMMENT'birincil anahtar ', `resource_name` varchar (64) NOT NULL DEFAULT '' COMMENT'resource name ', `share` varchar (64) NOT NULL DEFAULT '' COMMENT'status ', `version` int (4) NOT NULL DEFAULT '' COMMENT'version number ', `desc` varchar (1024) NOT NULL DEFAULT'Bilgileri açıklar ', `update_time` timestamp NOT NULL DEFAULT '' COMMENT 'Veri zamanından tasarruf edin, otomatik olarak oluşturulur', BİRİNCİL ANAHTAR ("kimlik"), EŞSİZ ANAHTAR "uidx_resource_name` (" resource_name ") BTREE KULLANILARAK ) MOTOR = InnoDB VARSAYILAN CHARSET = utf8 YORUM = 'Kaynaklar';

2. Duruşu kullanın

Sözde kod uygulaması:

Kaynak kaynağı = exeSql ("kaynak_adı = xxx olan kaynaktan * seçin"); boolean succ = exeSql ("kaynak setini güncelle version = 'newVersion' ... burada resource_name = xxx ve version = 'oldVersion'"); if (! succ) { // yeniden deneme başlat }

Gerçek kodda, yeniden denemeye devam etmek için bir while döngüsü yazabilirsiniz, sürüm numarası tutarsızsa, güncelleme başarısız olur ve güncelleme başarılı olana kadar yeni sürüm numarası tekrar alınır.

3. İyimser kilitlemenin avantajları ve dezavantajları

Avantajlar: kullanımı kolay, veri tutarlılığı sağlar.

Dezavantajları:

1) Sıralı kilitlerin performansında belirli bir ek yük vardır

2) Yüksek eşzamanlılık senaryolarında, iş parçacıkları içindeki döndürme işlemleri belirli miktarda CPU kaynağı tüketecektir.

Ek olarak, örneğin, veri durumunu güncellemenin bazı senaryolarında idempotence dikkate alınmadan, veri tutarlılığını sağlamak için doğrudan satır kilitlerini kullanabilirsiniz Örnek: tablo setini güncelle state = 1, burada id = xxx ve state = 0;

İyimser kilit, CAS Karşılaştırma ve Değiştirme güncelleme mekanizmasına benzer, önerilen okuma: CAS uygulama ilkelerinin tam olarak anlaşılması

Redis dağıtılmış kilit uygulamasına göre

SetNX'e göre dağıtılmış kilidi gerçekleştirin

Redis'e dayalı dağıtılmış kilit, performansta en iyisidir ve uygulamada en karmaşık olanıdır.

Önceki makalede bahsedilen RedLock, Redis'in babası Antirez tarafından önerilen dağıtılmış kilitler için "sağlam" bir uygulama algoritmasıdır, ancak aynı zamanda tartışmalıdır ve genellikle tavsiye edilmez.

2.6.12'den önceki Redis sürümleri, dağıtılmış kilitleri uygulamak için setnx + expire yöntemini kullandı. Örnek kod aşağıdaki gibidir:

public static boolean lock (Jedis jedis, String lockKey, String requestId, int expireTime) { Uzun sonuç = jedis.setnx (lockKey, requestId); // Kilidi ayarlayın eğer (sonuç == 1) { // Kilidi başarıyla elde edin // Program burada aniden çökerse, sona erme süresi ayarlanamaz ve bir kilitlenme meydana gelir // Son kullanma tarihine kadar kilidi sil jedis.expire (lockKey, expireTime); doğruya dön; } yanlış dönüş; }

LockKey varsa, başarısızlıkla sonuçlanır, aksi takdirde başarı döndürür. Ayar başarılı olduktan sonra, senkronizasyon kodunu tamamladıktan sonra kilidi başarılı bir şekilde serbest bırakmak için, yöntem lockKey'e bir sona erme süresi ayarlamak, anahtar değerinin silindiğini onaylamak ve kilidin serbest bırakılamamasını önlemek için expire () yöntemini kullanır, bu da bir sonraki iş parçacığının kilidi elde edememesine neden olur. Kilitlenme sorunu.

Ancak atomik bir işlem olmayan ve kolaylıkla sorun yaratabilen programda setnx + expire olmak üzere iki komut çalıştırılır.

Program kilidi ayarlarsa, bu zamanda, sona erme süresi ayarlanmadan önce program çöker .. LockKey sona erme süresini ayarlamazsa, bir kilitlenme sorunu ortaya çıkar.

Yukarıdaki sorunları çözmenin iki yolu vardır:

1) Yöntem 1: lua yazısı

Lua komut dosyası aracılığıyla kilit ayarının ve sona erme süresinin atomikliğini de elde edebilir ve ardından komut dosyasını jedis.eval () yöntemiyle çalıştırabiliriz:

// Kilitleme betiği, KEYS kilitlenecek anahtardır, ARGV rastgele bir UUID değeridir, ARGV sona erme zamanıdır private static final String SCRIPT_LOCK = "redis.call ('setnx', KEYS, ARGV) == 1 ise redis.call ('pexpire', KEYS, ARGV) 1 döndür, aksi takdirde 0 son döndür"; // Komut dosyasının kilidini açın, KEYS tarafından kilidi açılacak anahtar, ARGV rastgele bir UUID değeridir private static final String SCRIPT_UNLOCK = "redis.call ('get', KEYS) == ARGV ise redis.call ('del', KEYS) döndürürse 0 sonunu döndürür";

2) İkinci yöntem: yerel komutu ayarla

Redis 2.6.12 sürümünden sonra SETNX, sona erme süresi parametresini ekledi:

SET lockKey anystring NX PX maksimum kilit süresi

Program uygulama kodu aşağıdaki gibidir:

public static boolean lock (Jedis jedis, String lockKey, String requestId, int expireTime) { Dize sonucu = jedis.set (lockKey, requestId, "NX", "PX", expireTime); eğer ("Tamam" .equals (sonuç)) { doğruya dön; } yanlış dönüş; }

SETNX yöntemi, kilit ve sona erme süresinin ayarlanmasının atomikliğini garanti edebilse de, sona erme süresini nispeten kısa olarak ayarlarsak ve yürütme süresi nispeten uzunsa, kilit kodu bloğunun başarısız olması ve diğer istemcilerin de başarısızlıktan sonra bunu elde edebilmesi gibi bir sorun olacaktır. Aynı kilit ve aynı işletme ile şu anda bazı sorunlar ortaya çıkabilir.

Yukarıdaki sorunların ortaya çıkmamasını sağlamak için sona erme süresini yeterince uzun ayarlamamız gerekir, ancak ayarın ne kadar makul olduğu belirli işletmeye göre tartılmalıdır. Diğer müşterilerin kilidi almak için engellemesi gerekiyorsa, bir döngü zaman aşımı bekleme mekanizması ve diğer sorunlar tasarlamak gerekir, bu oldukça zahmetli geliyor, değil mi?

İlkbahar kurumsal entegrasyon modu, dağıtılmış kilidi gerçekleştirir

Jedis istemcisini kullanmaya ek olarak, birçok dağıtılmış kilit yöntemi sağlayan Spring tarafından resmi olarak sağlanan kurumsal entegrasyon modu çerçevesini doğrudan kullanabilirsiniz. Yay, birleşik dağıtılmış kilit soyutlaması sağlar. Özel uygulama şu anda şunları desteklemektedir:

  • Gemfire
  • Jdbc
  • Hayvan bakıcısı
  • Redis

İlk günlerde, dağıtılmış kilitlerle ilgili kod, Spring Cloud'un bir alt projesi olan Spring Cloud Cluster'da mevcuttu ve daha sonra Spring Integration'a taşındı.

Bahar Entegrasyonu proje adresi: https://github.com/spring-projects/spring-integration

Spring'in gücü, Lock dağıtılmış kilidi küresel olarak soyutlayan bunda yatmaktadır.

Soyut yapı aşağıdaki gibidir:

En üst düzey soyut arayüz olarak LockRegistry:

/ ** * Paylaşılan kilitlerin kaydını tutmak için strateji * * @ yazar Oleg Zhurakousky * @ yazar Gary Russell * @ 2.1.1'den beri * / @FunctionalInterface genel arayüz LockRegistry { / ** * Parametre nesnesiyle ilişkili kilidi elde eder. * @param lockKey Kilidin ilişkili olduğu nesne. * @return İlişkili kilit. * / Kilit alma (Object lockKey); }

Tanımlanan get () yöntemi, karşılık gelen XxxLockRegitry uygulama sınıfında oluşturulan belirli Lock uygulama sınıfını alır.

RedisLockRegistry'deki get () yönteminin uygulama sınıfı RedisLock'tur.RedisLock içinde, Springboot 2.x (Spring5) sürümünde lua betiği ile birlikte SET + PEXIPRE komutu ve Springboot 1.x (Spring4) sürümünde SETNX komutu ile gerçekleştirilir. nın-nin.

ZookeeperLockRegistry'deki get () yönteminin uygulama sınıfı, Apache Curator çerçevesine dayalı olarak dahili olarak uygulanan ZkLock'tur.

JdbcLockRegistry'deki get () yönteminin uygulama sınıfı JdbcLock'tur. JdbcLock, bir INT_LOCK veritabanı kilit tablosuna göre dahili olarak uygulanır ve JdbcTemplate tarafından çalıştırılır.

İstemci kullanım yöntemi:

private final String registryKey = "sb2"; RedisLockRegistry lockRegistry = new RedisLockRegistry (getConnectionFactory (), this.registryKey); Kilit kilidi = lockRegistry.obtain ("foo"); lock.lock (); Deneyin { // doSth ... } en sonunda { lock.unlock (); } }

Aşağıda, belirli kilitleme ve kilit açma sürecini göstermek için mevcut en son sürümün uygulaması verilmiştir.

RedisLockRegistry $ RedisLock sınıf kilidi () kilitleme işlemi:

Kilitleme adımları:

1) LockKey, bu örnekte sb2: foo olan registryKey: yoludur ve C1 istemcisi önce kilit için geçerlidir.

2) lua betiğini çalıştırın ve lockKey'i alın mevcut değil, ardından lockKey'i başarılı olarak ayarlayın, değer clientid (UUID) ve sona erme süresi varsayılan olarak 60 saniyedir.

3) İstemci C1 aynı iş parçacığında art arda kilitlenir, lockKey'i pexpire, sona erme süresini 60 saniyeye sıfırlar.

4) İstemci C2 kilitleme için başvurur, lua betiğini çalıştırır, kilit anahtarını alır ve kilitli istemci kimliğinden farklıdır ve kilit başarısız olur

5) C2 istemcisi telefonu kapatır ve her 100 ms'de bir tekrar kilitlemeye çalışır.

RedisLock # lock () kilit kaynak kodu uygulaması:

Anlamanıza yardımcı olması için yukarıdaki akış şemasını karşılaştırabilirsiniz.

@Override public void lock () { this.localLock.lock (); while (true) { Deneyin { while (! getLock ()) { Thread.sleep (100); // NOSONAR } kırmak; } catch (InterruptedException e) { / * * Bu yöntem kesintisiz olmalıdır, bu yüzden yakalayın ve görmezden gelin * kesintiye uğrar ve yalnızca while döngüsünden çıktığında * kilidi alıyoruz. * / } catch (İstisna e) { this.localLock.unlock (); rethrowAsLockException (e); } } } // Çalışmak üzere Spring tarafından paketlenen RedisTemplate temel alınmıştır private boolean retriLock () { Boole başarısı = RedisLockRegistry.this.redisTemplate.execute (RedisLockRegistry.this.obtainLockScript, Collections.singletonList (this.lockKey), RedisLockRegistry.this.clientId, String.valueOf (RedisLockRegistry.this.expireAfter)); boole sonucu = Boolean.TRUE.equals (başarılı); if (sonuç) { this.lockedAt = System.currentTimeMillis (); } dönüş sonucu; }

Lua komut dosyası kodu yürütüldü:

özel statik final String OBTAIN_LOCK_SCRIPT = "local lockClientId = redis.call ('GET', ANAHTARLAR) \ n" + "lockClientId == ARGV ise \ n" + "redis.call ('PEXPIRE', KEYS, ARGV) \ n" + "doğru dönüş \ n" + "değilse, o zaman lockClientId \ n" + "redis.call ('SET', KEYS, ARGV, 'PX', ARGV) \ n" + "doğru dönüş \ n" + "bitiş \ n" + "yanlış dönüş";

RedisLockRegistry $ RedisLock sınıfı unlock () kilit açma işlemi:

RedisLock # unlock () kaynak kodu uygulaması:

@Override public void unlock () { eğer (! this.localLock.isHeldByCurrentThread ()) { yeni IllegalStateException ("+ this.lockKey'de kilidiniz yok" + this.lockKey); } eğer (this.localLock.getHoldCount () > 1) { this.localLock.unlock (); dönüş; } Deneyin { eğer (! isAcquiredInThisProcess ()) { yeni IllegalStateException ("Son kullanma tarihi nedeniyle mağazada kilit serbest bırakıldı." + "Bu kilitle korunan verilerin bütünlüğü tehlikeye atılmış olabilir."); } eğer (Thread.currentThread (). isInterrupted ()) { RedisLockRegistry.this.executor.execute (this :: removeLockKey); } Başka { removeLockKey (); } eğer (LOGGER.isDebugEnabled ()) { LOGGER.debug ("Kilit açıldı;" + bu); } } catch (İstisna e) { ReflectionUtils.rethrowRuntimeException (e); } en sonunda { this.localLock.unlock (); } } // Önbellek anahtarını silin private void removeLockKey () { eğer (this.unlinkAvailable) { Deneyin { RedisLockRegistry.this.redisTemplate.unlink (this.lockKey); } catch (Exception ex) { LOGGER.warn ("UNLINK komutu başarısız oldu (Redis sunucusunda desteklenmiyor mu?);" + "normal DELETE komutuna geri dönme", örn); this.unlinkAvailable = false; RedisLockRegistry.this.redisTemplate.delete (this.lockKey); } } Başka { RedisLockRegistry.this.redisTemplate.delete (this.lockKey); } }

Unlock () unlock yönteminde, Redis DEL komutunun doğrudan Anahtarı silmek için çağrılmadığı bulunmuştur.Bu aynı zamanda Springboot 2.x versiyonunda yapılan bir optimizasyondur. UNLINK komutu Redis 4.0 ve üzerinde sağlanır.

Başka bir deyişle, dağıtılmış kilit uygulamasının en son sürümü, Redis 4.0 veya üstünün kullanılmasını gerektirir.

Redis'in resmi web sitesinde verilen açıklamaya bir göz atın:

Bu komut DEL'e çok benzer: belirtilen anahtarları kaldırır. Tıpkı DEL gibi, bir anahtar yoksa yoksayılır. komutu, gerçek bellek geri kazanımını farklı bir iş parçacığında gerçekleştirir, yani DEL ise engellemez. Bu, komut adının nereden gelir: komut yalnızca anahtarların anahtar alanındaki bağlantısını kaldırır. gerçek kaldırma daha sonra eşzamansız olarak gerçekleşecektir.

DEL, engelleme modunda her zaman değer bölümünü serbest bırakır. Ancak değer, büyük LIST veya HASH için çok fazla ayırma gibi çok büyükse, Redis'i uzun süre bloke edecektir.Bu sorunu çözmek için Redis, UNLINK komutunu, yani "engellemesiz" silme işlemini uygular. Değer küçükse, DEL genellikle UNLINK ile aynı verimliliktedir.

Esasen, bu kilitleme yöntemi SETNX kullanılarak uygulanır ve Yay, yeniden giriş kilitlemesini, zaman aşımı beklemesini ve kesinti kilitlemeyi desteklemek için yalnızca ince bir kapsülleme katmanı oluşturur.

Ancak bir sorun var Kilit süre sonu esnek bir şekilde ayarlanamıyor İstemci başlatıldığında, RedisLockRegistry oluşturulduğunda ayarlanmasına izin verilir, ancak bu globaldir.

/ ** * Verilen kilit süre sonu ile bir kilit kayıt defteri oluşturur. * @param connectionFactory Bağlantı fabrikası. * @param registryKey Kilitler için anahtar öneki. * @param expireAfter Milisaniye cinsinden süre sonu. * / public RedisLockRegistry (RedisConnectionFactory connectionFactory, String RegistryKey, long expireAfter) { Assert.notNull (connectionFactory, "'connectionFactory' boş olamaz"); Assert.notNull (registryKey, "'registryKey' boş olamaz"); this.redisTemplate = new StringRedisTemplate (connectionFactory); this.obtainLockScript = new DefaultRedisScript < > (OBTAIN_LOCK_SCRIPT, Boolean.class); this.registryKey = registryKey; this.expireAfter = expireAfter; }

ExpireAfter parametresi globaldir ve ayrıca sorunlar da vardır.Kilitin sona erme süresi dolmuş olabilir, ancak işletme henüz işlenmemiş ve kilit başka bir müşteri tarafından alınmış ve bu da başka sorunlara neden olacaktır.

Kaynak kodunu analiz ettikten sonra, aslında, RedisLockRegistry'nin uygulanmasına dayalı olarak dağıtılmış kilitleri kendi başımıza kapsülleyebilir ve uygulayabiliriz, örneğin:

1. Son kullanma süresini global yerine farklı anahtarlara göre ayarlamaya izin verilsin mi?

2. İş işleme alınmadığında, mevcut istemci bir zamanlayıcı görev tespiti başlatır ve sona erme süresini otomatik olarak uzatır?

Kendin Yap? Zahmetli mi? Merak etmeyin, endişelenmeyin! Sektörün halihazırda hazır bir uygulama planı, yani daha sonra Redisson bölümünde daha ayrıntılı analiz edilecek olan Redisson çerçevesi var.

Redis kümesinin perspektifinden soruna bakın

Redis ana-köle mimarisine bakıldığında, hala sorunlar var. Redis küme verileri her bir düğümle senkronize edildiğinde eşzamansız olduğundan, Ana düğüm kilidi alırsa Ana düğüm diğer düğümlerle senkronize edilmediğinde çöker. Şu anda, yeni Ana düğüm hala kilidi alabilir, bu nedenle birden fazla uygulama Servis kilidi aynı anda alabilir.

Redis'in babası Antirez, yukarıdaki hususlara dayanarak bir RedLock algoritması önerdi.

RedLock algoritması uygulama süreci analizi:

Redis dağıtım modunun Redis Kümesi olduğunu varsayarsak, toplam 5 ana düğüm vardır ve aşağıdaki adımlar aracılığıyla bir kilit elde edilir:

1) Geçerli zaman damgasını milisaniye cinsinden alın

2) Sırayla her ana düğümde bir kilit oluşturmaya çalışın, sona erme süresi daha kısa olacak şekilde ayarlanır, genellikle onlarca milisaniye

3) Çoğu düğümde bir kilit oluşturmaya çalışın, örneğin, 5 düğüm 3 düğüm gerektirir (n / 2 +1)

4) Müşteri, kilit kurma süresi, zaman aşımı süresinden az ise, kurulum başarılı olsa bile, kilidi kurma süresini hesaplar.

5) Kilit kurulumu başarısız olursa, sırayla kilidi silin

6) Bir istemci başarılı bir şekilde dağıtılmış bir kilit oluşturduğu sürece, diğer müşterilerin kilidi almaya çalışmak için sürekli olarak anket yapması gerekecektir.

Yukarıdaki süreçte daha önce bahsedildiği gibi, RedLock algoritmasının uygulanmasının daha ileri analizinde hala sorunlar olabilir, bu da Martain ve Antirez arasındaki anlaşmazlığın odak noktasıdır.

Soru 1: Düğüm çöküyor ve yeniden başlıyor

Düğüm çöktüğünde ve yeniden başladığında, birden çok istemci kilitleri tutacaktır.

5 Redis düğümü olduğunu varsayalım: A, B, C, D, E. Aşağıdaki olaylar dizisinin gerçekleştiğini varsayalım:

1) C1 istemcisi, Redis kümesindeki üç A, B ve C düğümünü başarıyla kilitler (ancak D ve E kilitli değildir).

2) Duang C düğümü çöktü ve yeniden başlatıldı, ancak C1 istemcisi C düğümünü kilitledi ve kayboldu.

3) C düğümü yeniden başlatıldıktan sonra, C2 istemcisi Redis kümesinde C, D ve E'yi başarıyla kilitlemeye çalışır.

Bu şekilde trajedi! C1 ve C2 müşterileri aynı anda aynı dağıtılmış kilidi almışlardır.

Antirez, düğümün yeniden başlatılmasının neden olduğu kilit hatası sorununu çözmek için, gecikmeli yeniden başlatma konseptini önermiştir, yani bir düğüm çöktükten sonra hemen yeniden başlamaz, ancak bir süre bekler ve sonra yeniden başlar.Bekleme süresi, kilidin etkin süresinden daha fazladır.

Bu şekilde, bu düğümün katıldığı kilitlerin süresi yeniden başlatmadan önce sona erecek ve yeniden başlatmanın ardından mevcut kilitleri etkilemeyecektir.

Aslında, bu aynı zamanda tutarsızlık olasılığını azaltmak için yapay tazminat önlemleri yoluyla da yapılmaktadır.

Problem 2: Saat atlama

5 Redis düğümü olduğunu varsayalım: A, B, C, D, E. Aşağıdaki olaylar dizisinin gerçekleştiğini varsayalım:

1) C1 istemcisi, Redis kümesindeki üç A, B ve C düğümünü başarıyla kilitler. Ancak ağ sorunları nedeniyle D ve E ile iletişim başarısız oldu.

2) C düğümündeki saat ileri atlayarak üzerinde tutulan kilidin hızlı bir şekilde sona ermesine neden olur.

3) İstemci C2, Redis kümesindeki C, D ve E düğümlerine aynı kilidi başarıyla ekledi.

Şu anda yine trajedi! C1 ve C2 istemcileri aynı anda aynı dağıtılmış kilidi tutar.

Antirez, saat atlamasının neden olduğu kilit arızası sorununu çözmek için, sistem saatini yapay olarak değiştirmenin yasaklanması gerektiğini ve sistem saatine "atlama" ayarlamaları yapmayan bir ntpd programı kullanmanın önerisinde bulundu. Bu aynı zamanda tutarsızlık olasılığını azaltmak için yapay tazminat önlemleri yoluyla yapılır.

Ancak ..., RedLock algoritması, paylaşılan kaynakların çalıştırılmasının zaman aşımına uğramasından kaynaklanan kilit hatası sorununu çözmedi.

Böyle tartışmalı bir algoritma uygulamasının kullanılması tavsiye edilmez.

Normal koşullar altında, bu makalede sunulan çerçeve tarafından sağlanan dağıtılmış kilit uygulaması, ihtiyaçların çoğunu zaten karşılayabilir.

özet:

Yukarıda, yay entegrasyonu-redis uygulama ilkesinin derinlemesine bir analizini yaptık ve ayrıca RedLock'un tartışmalı konularını analiz ettik.

Ek olarak, yay entegrasyonunun Jdbc, Zookeeper ve Gemfire tarafından uygulanan dağıtılmış kilitleri entegre ettiğini de belirttik.Gemfire ve Jdbc ile ilgileniyorsanız, bir göz atabilirsiniz.

Neden bir Jdbc dağıtılmış kilit uygulaması sağlıyoruz?

Tahmin edin, uygulama eşzamanlılığınız yüksek olmadığında, örneğin, arka uç bir iştir ve Zookeeper, Redis gibi ek bileşenlere dayanmaz ve yalnızca veritabanına güvenir.

Ama yine de dağıtılmış kilitlerle bir şeyler yapmak istiyorsunuz. Bunu yapmak çok kolay. Spring-integration-jdbc'yi doğrudan kullanabilirsiniz.İç uygulama ayrıca veritabanı satır kilitlerine dayanır.Kilit tablosunu önceden oluşturmanız ve tablo için SQL oluşturmanız gerekir. böyle:

TABLO INT_LOCK OLUŞTUR ( LOCK_KEY CHAR (36) BOŞ DEĞİL, BÖLGE VARŞARI (100) BOŞ DEĞİL, CLIENT_ID CHAR (36), CREATED_DATE DATETIME (6) BOŞ DEĞİL, kısıtlama INT_LOCK_PK birincil anahtar (LOCK_KEY, REGION) ) MOTOR = InnoDB;

Spesifik uygulama mantığı da çok basittir, bu yüzden kendinize bakalım.

Entegre Zookeeper tarafından uygulanan dağıtılmış kilitler, Küratör çerçevesine göre uygulanır ve bu bölümde genişletilmeyecek ve daha sonra analiz edilecektir.

Redisson'a dayalı dağıtılmış kilit uygulayın

Redisson, Redis'in Java uygulamasının bir istemcisidir ve API'si kapsamlı Redis komut desteği sağlar.

Jedis, Redis ile etkileşim kurmak için basitçe engelleyici G / Ç kullanır ve Redission, Netty üzerinden engellemeyen G / Ç'yi destekler.

Redisson, kilidin uygulanmasını kapsüller ve yerel Kilidimizi çalıştırır gibi kullanmamıza izin verir.Ayrıca, kullanımı kolay olan koleksiyonların, nesnelerin ve ortak önbellek çerçevelerinin dostça kapsüllenmesine de sahiptir.

Şimdiye kadar Github'daki Yıldız sayısı 11,8 bin olup bu açık kaynak projesinin dikkat ve kullanıma değer olduğunu gösteriyor.

Redisson dağıtılmış kilit Github:

https://github.com/redisson/redisson/wiki/8.-Distributed-locks-and-synchronizers

Redisson, birden çok Redis dağıtım mimarisini kolayca destekleyebilir:

1) Redis bağımsız

2) Efendi-Köle + Nöbetçi

3) Redis-Küme kümesi

// Master-Slave yapılandırması Config config = new Config (); MasterSlaveServersConfig serverConfig = config.useMasterSlaveServers () .setMasterAddress ("") .addSlaveAddress ("") .setReadMode (ReadMode.SLAVE) .setMasterConnectionPoolSize (maxActiveSize) .setMasterConnectionMinimumIdleSize (maxIdleSize) .setSlaveConnectionPoolSize (maxActiveSize) .setSlaveConnectionMinimumIdleSize (maxIdleSize) .setConnectTimeout (CONNECTION_TIMEOUT_MS) // varsayılan 10 saniye .setTimeout (socketTimeout) ; RedissonClient redisson = Redisson.create (config); RLock kilidi = redisson.getLock ("myLock"); // kilitlen lock.lock (); // Kilidi almadan 10 saniye bekleyin, otomatik bırakın lock.lock (10, TimeUnit.SECONDS); // Kilit süresinin 100 saniyeyi geçmemesini bekleyin // 10 saniye sonra kilidi otomatik olarak serbest bırakın boolean res = lock.tryLock (100, 10, TimeUnit.SECONDS); if (res) { Deneyin { ... } en sonunda { lock.unlock (); } }

Kullanımı çok basit.RedissonClient istemcisi, eksiksiz uygulamalar sağlayan yeniden girişli kilitleri, adil kilitleri, okuma-yazma kilitlerini, kilit zaman aşımlarını, RedLock vb. Destekleyen birçok arayüz uygulaması sağlar.

lock () kilitleme işlemi:

Redis komutları eski versiyonla uyumlu olması için Redisson'da lua betikleri üzerinden çalıştırılırken atomik işlemler sağlanır.

Kilitle çalıştırılan Lua betiği:

Redis'teki Hash yapısı saklanır.

Parametre açıklaması:

ANAHTAR: Örnekteki myLock gibi kilitlenecek anahtarın adı.

ARGV: Kilitli anahtar için belirlenen sona erme süresi

ARGV: Hash yapısındaki anahtar adı, lockName UUID'dir: thread ID

korumalı String getLockName (long threadId) { dönüş kimliği + ":" + threadId; }

1) İstemci C1 kilit için başvurur, anahtar myLock'dur.

2) Anahtar yoksa, hset aracılığıyla değeri ayarlayın ve sona erme süresini pexpire ile ayarlayın. Aynı zamanda, Watchdog görevini başlatın.Varsayılan olarak, her 10 saniyede bir değerlendirilecektir. Anahtar hala oradaysa, sona erme süresini 30 saniyeye sıfırlayın.

WatchDog kaynak kodunu açın:

3) Aynı istemci C1 iş parçacığı tekrar kilitlenir Anahtar varsa, Redis'deki Hash'teki lockName'in mevcut iş parçacığının lockName ile aynı olduğuna ve Hash'teki lockName değerinin 1 arttığına karar verilir, bu da reentrant kilitlemenin desteklendiği anlamına gelir.

4) Müşteri C2'ye bir kilit için başvurma emri verir Anahtar varsa, Redis'deki Hash'teki lockName'in mevcut iş parçacığının lockName'inden farklı olduğuna karar verilir ve kalan sona erme süresini döndürmek için pttl yürütülür.

5) C2 iş parçacığı pttl zamanını denemeye devam eder.Bu, Semafor semaforuna dayanır.İzin varsa, hemen geri dönecektir.Aksi takdirde, pttl zamanı izin alamayacak ve tekrar denemeye devam edecektir.

Kaynak kodunu yeniden deneyin:

Redisson'un uygulanması, iş işleme süresi sona erme süresinden uzun olduğunda sorunu çözer.

Aynı zamanda Redisson, Anahtar için sona erme süresinin ayarlanması, engellemesiz + zaman aşımı süresi vb. Gibi kilit arabirimini genişletmek için RLock arabirimi olarak adlandırılan Kilit arabirimini de genişletir.

geçersiz kilit (uzun leaseTime, TimeUnit birimi); boolean tryLock (long waitTime, long leaseTime, TimeUnit birimi) InterruptedException oluşturur;

Redisson'daki WatchDog (bekçi köpeği) mantığı, herhangi bir kilitlenme oluşmamasını sağlar.

İstemci düşerse, WatchDog görevi de durur. Bu zamanda, anahtarın sona erme süresi sıfırlanmayacaktır. Asılı istemci tarafından tutulan anahtarın sona erme süresi dolduğunda, kilit otomatik olarak serbest bırakılır ve diğer istemciler kilidi almaya çalışır.

WatchDog'un açıklamasını resmi web sitesinde de görebilirsiniz:

If Redisson instance which acquired lock crashes then such lock could hang forever in acquired state. To avoid this Redisson maintains lock watchdog, it prolongs lock expiration while lock holder Redisson instance is alive. By default lock watchdog timeout is 30 seconds and can be changed through Config.lockWatchdogTimeout setting.

unlock()lua

lua

Zookeeper

Zookeeper Paxos ZookeeperWatch

Zookeeper Znode /

PERSISTENT EPHEMERAL SEQUENTIAL

Watch

Zookeeper Watcher

ZooKeeper WatcherZooKeeper

Zookeeper

PERSISTENT /locks/lock_name1 EPHEMERAL SEQUENTIAL + + /0000000001 /0000000002 /0000000003

name

zk

// InterProcessMutex lock = new InterProcessMutex(client, lockPath); if ( lock.acquire(maxWait, waitUnit) ) { Deneyin { // do some work inside of the critical section here } en sonunda { lock.release(); } } public void acquire() throws Exception { if ( !internalLock(-1, null) ) { throw new IOException("Lost connection while trying to acquire lock: " + basePath); } } private boolean internalLock(long time, TimeUnit unit) throws Exception { / * Note on concurrency: a given lockData instance can be only acted on by a single thread so locking isn't necessary * / Thread currentThread = Thread.currentThread(); LockData lockData = threadData.get(currentThread); if ( lockData != null ) { // re-entering lockData.lockCount.incrementAndGet(); doğruya dön; } String lockPath = internals.attemptLock(time, unit, getLockNodeBytes()); if ( lockPath != null ) { LockData newLockData = new LockData(currentThread, lockPath); threadData.put(currentThread, newLockData); doğruya dön; } yanlış dönüş; } // ...

InterProcessMutex Curator

1 ConcurrentMap threadData Map

2 threadData.get(currentThread) 1

3 /0000000002 EPHEMERAL_SEQUENTIAL

4

5

6 /0000000002 /0000000001 WatcherWatcher object.notifyAll()

7object.wait(timeout) object.wait()

11 0 10

2

3 threadDataMap

4 Nihai özet

Apache CuratorRedissonSpring

RedissonCurator

CuratorInterProcessLockRedissonRLock java.util.concurrent.locks.Lock

Redis

Redis

redis

Redlock

Zookeeper:

Zookeeper

Zookeeper

ZK

Spring-integrationRedis RedissonSpringbootjar

Zamanlanmış görev planı ansiklopedisi
önceki
Derinlemesine yirmi bin kelime, dağıtılmış sistemlerin ilkelerini tanıtır
Sonraki
JVM röportajı 4 çekim, buna dayanabilir misin?
Saniyede milyonlarca eşzamanlılık elde etmek için "IO çoğullama" kullanma prensibini biliyor musunuz?
SpringBoot + RabbitMQ, mesajın% 100'ünün başarıyla teslim edilmesini ve tüketilmesini sağlamak için
Sadece okulun iyi seçilmesi değil, aynı zamanda bu popüler ana dalların da dikkatlice referans alınması gerekir.
Tayland'da Eğitim | Tayland'da eğitim hakkında bilmediğiniz şeyler
çok farklı! Taylandlı uğurlu sayılar yaptınız mı?
Neden çocukları Tayland'da okumaya göndermelisiniz? Bu anları okuduktan sonra anlıyorum
Tayland'da okumak öğrenci olmak mı istiyor? Sana sadece bu sırları söylüyorum
Adam restoranda bir kase erişte yedikten sonra 2 saat mahsur kaldı
İntikam mı geliyor? İran ve Irak aynı gün birbiri ardına savaştı! Trump endişeli ve acımasız
Yang Ying'in çilli yüzü oldukça lüks, modaya uygun ateşli anne genç bir kıza dönüşüyor, resim çok güzel.
Lisa yeni bir rekor kırdı ve aynı anda 3 ülkede 4 moda dergisinin kapağını kazandı.
To Top