Ngunci disebarake nggunakake Redis

Hey Habr!

Dina iki kita nggawa menyang manungsa waé terjemahan saka artikel Komplek bab implementasine saka mbagekke ngunci nggunakake Redis lan ngajak sampeyan kanggo pirembagan bab prospek saka Redis minangka topik. Analisis algoritma Redlock ing pitakonan saka Martin Kleppmann, penulis buku "Aplikasi Beban Dhuwur", diwenehi kene.

Pengunci sing disebarake minangka primitif sing migunani banget sing digunakake ing akeh lingkungan ing ngendi proses beda kudu nggarap sumber daya sing dienggo bareng kanthi cara sing eksklusif.

Ana sawetara perpustakaan lan kiriman metu ana sing njlèntrèhaké carane ngleksanakake DLM (Distributed Kunci Manager) nggunakake Redis, nanging saben perpustakaan njupuk pendekatan beda lan njamin padha nyedhiyani cukup ringkih dibandhingake apa sing bisa digayuh karo desain rada luwih canggih.

Ing artikel iki kita bakal nyoba kanggo njlèntrèhaké algoritma kanonik kondisional sing nduduhake carane ngleksanakake ngunci disebaraké nggunakake Redis. Kita bakal ngomong babagan algoritma sing diarani Redlock, ngleksanakake manajer kunci sing disebarake lan, miturut pendapat kita, algoritma iki luwih aman tinimbang pendekatan siji-kaya biasa. Muga-muga masyarakat bakal nganalisa, menehi saran, lan nggunakake minangka titik wiwitan kanggo proyek sing luwih rumit utawa alternatif.

Implementasi

Sadurunge pindhah menyang katrangan babagan algoritma, kita nyedhiyakake sawetara pranala menyang implementasi sing wis siap. Padha bisa digunakake kanggo referensi.

  • Redlock-rb (implementasine kanggo Ruby). Ana uga garpu Redlock-rb, kang nambah paket (permata) kanggo ease saka distribusi, lan ora mung kanggo.
  • Redlock-py (Implementasi Python).
  • Aioredlock (implementasine kanggo Asyncio Python).
  • Redlock-php (implementasine kanggo PHP).
  • PHPRedisMutex (implementasine liyane kanggo PHP)
  • cheprasov/php-redis-lock (Perpustakaan PHP kanggo kunci)
  • Redsync (implementasine kanggo Go).
  • Redisson (implementasine kanggo Jawa).
  • Redis::DistLock (implementasine kanggo Perl).
  • Redlock-cpp (implementasi kanggo C++).
  • Redlock-cs (implementasine kanggo C # / .NET).
  • RedLock.net (implementasine kanggo C # / .NET). Kanthi dhukungan kanggo ekstensi async lan kunci.
  • ScarletLock (implementasine kanggo C# .NET karo nyimpen data sing bisa dikonfigurasi)
  • Redlock4Net (implementasi kanggo C# .NET)
  • node-redlock (implementasi kanggo NodeJS). Kalebu dhukungan kanggo nggedhekake kunci.

Keamanan lan kasedhiyan njamin

Kita bakal nggawe model desain kanthi mung telung properti sing kita pikir menehi jaminan minimal sing dibutuhake kanggo nggunakake kunci sing disebarake kanthi efektif.

  1. Properti keamanan: Mutual exclusion. Ing wektu tartamtu, mung siji klien sing bisa nyekel kunci kasebut.
  2. Kasedhiyan Properti A: Ora ana deadlocks. Iku tansah bisa kanggo pungkasanipun ndarbeni kunci, sanajan klien sing ngunci sumber gagal utawa ndharat ing bagean disk beda.
  3. Kasedhiyan Properti B: Fault Toleransi. Sanalika mayoritas simpul Redis mlaku, klien bisa entuk lan ngeculake kunci.

Napa implementasine adhedhasar pemulihan kegagalan ora cukup ing kasus iki
Kanggo ngerti apa sing bakal kita tambah, ayo analisa kahanan saiki karo perpustakaan kunci sing disebarake adhedhasar Redis.

Cara paling gampang kanggo ngunci sumber daya nggunakake Redis yaiku nggawe kunci ing conto kasebut. Biasane, kunci digawe kanthi umur winates, iki digayuh nggunakake fitur kadaluwarsa sing kasedhiya ing Redis, supaya cepet utawa mengko kunci iki dirilis (properti 2 ing dhaptar kita). Nalika klien kudu ngeculake sumber daya, mbusak tombol kasebut.

Sepisanan, solusi iki bisa digunakake kanthi apik, nanging ana masalah: arsitektur kita nggawe siji titik kegagalan. Apa sing kedadeyan yen instance Redis host gagal? Ayo ditambahake budak! Lan kita bakal nggunakake yen presenter ora kasedhiya. Sayange, pilihan iki ora bisa digunakake. Kanthi nindakake iki, kita ora bakal bisa ngetrapake properti mutual exclusion sing dibutuhake kanggo njamin keamanan, amarga replikasi ing Redis ora sinkron.

Temenan, ing model kaya mengkono kondisi balapan dumadi:

  1. Klien A entuk kunci ing master.
  2. Master gagal sadurunge entri kunci ditransfer menyang budak.
  3. Pengikut diangkat dadi pimpinan.
  4. Klien B entuk kunci ing sumber sing padha karo sing wis dikunci A. PELANGGARAN KEAMANAN!

Kadhangkala pancen normal yen ing kahanan khusus, kayata gagal, akeh klien bisa nyekel kunci kasebut bebarengan. Ing kasus kasebut, solusi adhedhasar replikasi bisa ditrapake. Yen ora, disaranake solusi sing diterangake ing artikel iki.

Implementasine sing bener karo siji conto

Sadurunge nyoba kanggo ngatasi shortcomings saka konfigurasi siji-kaya sing diterangake ing ndhuwur, ayo kang ngerti carane bener nangani kasus prasaja iki, amarga solusi iki bener bener ing aplikasi ngendi kondisi lomba bisa ditampa saka wektu kanggo wektu, lan uga amarga mblokir ing a conto siji serves minangka basis sing digunakake ing algoritma mbagekke diterangake kene.

Kanggo entuk kunci, tindakake iki:

SET resource_name my_random_value NX PX 30000

Printah iki nginstal tombol mung yen durung ana (opsi NX), kanthi wektu validitas 30000 milliseconds (pilihan PX). Tombol disetel kanggo "myrandomvalue" Nilai iki kudu unik ing kabeh klien lan kabeh panjalukan kunci.
Sejatine, Nilai acak digunakake kanggo aman nerbitaké kunci, karo script marang Redis: mung mbusak tombol yen ana lan nilai disimpen ing iku persis apa samesthine. Iki ditindakake kanthi nggunakake skrip Lua ing ngisor iki:

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

Iki penting kanggo nyegah kunci sing dicekel klien liyane ora dicopot. Contone, klien bisa entuk kunci, banjur dikunci ing sawetara operasi sing luwih suwe tinimbang kunci sing sepisanan (supaya kunci duwe wektu kanggo kadaluwarsa), banjur copot kunci sing dilebokake sawetara klien liyane.
Nggunakake DEL prasaja ora aman amarga klien bisa mbusak kunci sing dicekel klien liyane. Ing kontras, nalika nggunakake skrip ing ndhuwur, saben kunci "ditandatangani" kanthi senar acak, dadi mung klien sing sadurunge diselehake bisa nyopot.

Apa kudu string acak iki? Aku ngira kudu 20 bita saka / dev / urandom, nanging sampeyan bisa nemokake cara sing luwih murah kanggo nggawe string cukup unik kanggo tujuan sampeyan. Contone, bakal apik kanggo wiji RC4 karo / dev / urandom banjur generate stream pseudo-acak saka iku. Solusi sing luwih prasaja kalebu kombinasi wektu unix ing resolusi mikrodetik plus ID klien; iku ora minangka aman, nanging mbokmenawa nganti tugas ing paling konteks.

Wektu sing digunakake minangka ukuran umur kunci diarani "umur kunci". Nilai iki minangka jumlah wektu sadurunge kunci dirilis kanthi otomatis lan jumlah wektu klien kudu ngrampungake operasi sadurunge klien liyane bisa ngunci sumber daya kasebut, tanpa bener-bener nglanggar jaminan pengecualian. Jaminan iki diwatesi mung ing wektu tartamtu, sing diwiwiti saka kunci dituku.

Dadi kita wis ngrembug cara sing apik kanggo entuk lan ngeculake kunci. Sistem kasebut (yen kita ngomong babagan sistem sing ora disebarake sing kasusun saka siji lan tansah kasedhiya) aman. Ayo ngluwihi konsep iki menyang sistem sing disebarake, sing ora ana jaminan kasebut.

Algoritma Redlock

Versi algoritma sing disebarake nganggep yen kita duwe master N Redis. Node iki pancen bebas saka siji liyane, mula kita ora nggunakake replikasi utawa sistem koordinasi implisit liyane. Kita wis nutupi carane ndarbeni lan ngeculake kunci kanthi aman ing siji conto. Kita nganggep manawa algoritma kasebut, nalika nggarap siji conto, bakal nggunakake metode iki. Ing conto kita nyetel N kanggo 5, kang nilai cukup. Mangkono, kita kudu nggunakake 5 master Redis ing komputer beda utawa mesin virtual kanggo mesthekake yen padha tumindak umumé independen saka saben liyane.

Kanggo entuk kunci, klien nindakake operasi ing ngisor iki:

  1. Entuk wektu saiki ing milliseconds.
  2. Sequentially nyoba kanggo njupuk kunci ing kabeh kasus N, nggunakake jeneng tombol padha lan nilai acak ing kabeh kasus. Ing Stage 2, nalika klien nyetel kunci ing basis saben-kayata, klien nggunakake wektu tundha kanggo ndarbeni sing cukup cendhak dibandhingake wektu sawise kunci dirilis kanthi otomatis. Contone, yen wektu pamblokiran 10 detik, wektu tundha bisa ana ing kisaran ~5-50 milidetik. Iki ngilangi kahanan sing klien bisa tetep diblokir kanggo dangu nyoba nggayuh simpul Redis sing gagal: yen conto ora kasedhiya, mula kita nyoba nyambung menyang conto liyane sanalika bisa.
  3. Kanggo njupuk kunci, klien ngetung sepira suwene wektu; Kanggo nindakake iki, subtracts saka nilai wektu nyata timestamp sing dipikolehi ing langkah 1. Yen lan mung yen klien bisa njupuk kunci ing mayoritas kedadean (paling 3), lan total wektu iku njupuk kanggo. entuk kunci, kurang saka durasi kunci, kunci dianggep wis entuk.
  4. Yen kunci wis dipikolehi, durasi kunci dianggep dadi durasi kunci asli dikurangi wektu sing wis diwilang ing langkah 3.
  5. Yen klien gagal entuk kunci kanggo sawetara alasan (salah siji ora bisa ngunci N / 2 + 1 kedadean, utawa durasi kunci negatif), banjur bakal nyoba kanggo mbukak kunci kabeh kedadean (malah sing dianggep ora bisa mblokir. ).

Apa algoritma ora sinkron?

Algoritma iki adhedhasar asumsi yen, sanajan ora ana jam sing disinkronake sing kabeh proses bakal bisa digunakake, wektu lokal ing saben proses isih mili kanthi kecepatan sing padha, lan kesalahane cilik dibandhingake karo total wektu sawise kunci kasebut. dirilis kanthi otomatis. Panyangka iki meh padha karo kahanan sing khas kanggo komputer biasa: saben komputer duwe jam lokal, lan kita biasane bisa ngetung kasunyatan manawa bedane wektu antarane komputer beda-beda cilik.

Ing titik iki, kita kudu luwih tliti ngrumusake aturan mutual exclusion kita: mutual exclusion dijamin mung yen klien nyekeli kunci metu sak wektu kunci bener (nilai iki dijupuk ing langkah 3), minus sawetara wektu maneh (total sawetara milliseconds kanggo ngimbangi prabédan wektu antarane proses).

Artikel menarik ing ngisor iki nyritakake luwih akeh babagan sistem kasebut sing mbutuhake koordinasi interval wektu: Leases: mekanisme fault-tolerant efisien kanggo konsistensi cache file sing disebarake.

Coba maneh gagal

Nalika klien gagal ndarbeni kunci, kudu nyoba maneh sawise wektu tundha acak; iki wis rampung kanggo de-sinkronisasi sawetara klien nyoba kanggo ndarbeni kunci ing sumber padha ing wektu sing padha (sing bisa mimpin kanggo kahanan "pamisah-otak" kang ora ana pemenang). Kajaba iku, luwih cepet klien nyoba ndarbeni kunci ing mayoritas kasus Redis, luwih sempit jendhela ing ngendi kahanan pamisah-otak bisa kedadeyan (lan kurang perlu nyoba maneh). Mulane, saenipun, klien kudu nyoba ngirim printah SET menyang N instans bebarengan nggunakake multiplexing.

Iku worth nandheske kene carane penting iku kanggo klien sing gagal kanggo ndarbeni mayoritas kunci kanggo ngeculake (sebagian) kunci angsal, supaya padha ora kudu ngenteni tombol kadaluwarsa sadurunge kunci ing sumber bisa angsal maneh (sanajan yen ana fragmentasi jaringan, lan klien kelangan kontak karo Redis kedadean, sampeyan kudu mbayar paukuman kasedhiyan nalika nunggu tombol kadaluwarsa).

Mbukak kunci

Ngeculake kunci iku operasi prasaja sing mung mbutuhake mbukak kunci kabeh kedadean, preduli saka klien katon wis kasil ngunci conto tartamtu.

Pertimbangan Keamanan

Apa algoritma aman? Coba mbayangno apa sing kedadeyan ing macem-macem skenario.

Kanggo miwiti, ayo nganggep manawa klien bisa entuk kunci ing mayoritas kasus. Saben conto bakal ngemot kunci kanthi umur sing padha kanggo kabeh. Nanging, saben tombol kasebut dipasang ing wektu sing beda, mula bakal kadaluwarsa ing wektu sing beda. Nanging, yen tombol pisanan diinstal ing wektu sing ora luwih elek tinimbang T1 (wektu sing kita pilih sadurunge ngubungi server pisanan), lan tombol pungkasan diinstal ing wektu sing ora luwih elek tinimbang T2 (wektu nalika respon ditampa. saka server pungkasan), banjur kita manteb ing ati sing tombol pisanan ing pesawat sing kadaluwarsa bakal urip ing paling MIN_VALIDITY=TTL-(T2-T1)-CLOCK_DRIFT. Kabeh tombol liyane bakal kadaluwarsa mengko, supaya kita bisa mesthekake yen kabeh tombol bakal bener bebarengan kanggo paling wektu iki.

Sajrone wektu sing paling tombol tetep bener, klien liyane ora bakal bisa kanggo ndarbeni kunci, amarga N / 2 + 1 SET NX operasi ora bisa sukses yen N / 2 + 1 tombol wis ana. Mulane, yen kunci wis dipikolehi, ora bisa dipikolehi maneh ing wektu sing padha (iki bakal nglanggar properti pengecualian bebarengan).
Nanging, kita pengin nggawe manawa sawetara klien nyoba ndarbeni kunci ing wektu sing padha ora bisa sukses ing wektu sing padha.

Yen klien wis ngunci mayoritas kedadean kanggo babagan utawa luwih saka durasi kunci maksimum, iku bakal nimbang kunci ora bener lan mbukak kunci kedadean. Mulane, kita mung kudu nganggep kasus nalika klien bisa mblokir mayoritas kasus ing wektu sing kurang saka tanggal kadaluwarsa. Ing kasus iki, babagan argumentasi ing ndhuwur, sajrone wektu kasebut MIN_VALIDITY ora klien kudu bisa kanggo reacquire kunci. Mulane, akeh klien bakal bisa ngunci N / 2 + 1 kedadean ing wektu sing padha (sing ends ing mburi tataran 2) mung nalika wektu kanggo ngunci mayoritas luwih saka wektu TTL, kang ndadekake kunci ora bener.

Apa sampeyan bisa menehi bukti keamanan resmi, nuduhake algoritma sing padha, utawa nemokake bug ing ndhuwur?

Pertimbangan Aksesibilitas

Kasedhiyan sistem gumantung marang telung ciri utama:

  1. Bukak kunci kanthi otomatis (minangka kunci kadaluwarsa): Tombol bakal kasedhiya maneh kanggo digunakake kanggo kunci.
  2. Kasunyatan sing klien biasane bantuan saben liyane dening njabut kunci nalika kunci sing dikarepake durung angsal, utawa wis angsal lan proyek wis rampung; dadi kamungkinan sing kita ora kudu ngenteni tombol kadaluwarsa kanggo entuk maneh kunci.
  3. Kasunyatan manawa klien kudu nyoba maneh kanggo entuk kunci, ngenteni wektu sing luwih suwe tinimbang wektu sing dibutuhake kanggo entuk kunci paling akeh. Iki nyuda kemungkinan kahanan pamisah-otak njedhul nalika saingan kanggo sumber daya.

Nanging, ana paukuman kasedhiya sing padha karo TTL saka segmen jaringan, dadi yen ana segmen sing cedhak, paukuman bisa uga ora mesthi. Iki kedadeyan nalika klien entuk kunci lan banjur nyuwek menyang bagean liyane sadurunge bisa ngeculake.

Ing asas, diwenehi bagean jaringan contiguous tanpa wates, sistem bisa tetep kasedhiya kanggo wektu tanpa wates wektu.

Kinerja, failover lan fsync

Akeh wong sing nggunakake Redis amarga mbutuhake kinerja server kunci sing dhuwur babagan latensi sing dibutuhake kanggo entuk lan ngeculake kunci, lan jumlah akuisisi / rilis sing bisa rampung saben detik. Kanggo nyukupi syarat kasebut, ana strategi kanggo komunikasi karo server N Redis kanggo nyuda latensi. Iki strategi multiplexing (utawa "multiplexing wong miskin", ngendi soket sijine ing mode non-blocking, ngirim kabeh printah, lan maca printah mengko, assuming sing wektu babak-trip antarane klien lan saben Kayata padha) .

Nanging, kita uga kudu nimbang pertimbangan sing ana gandhengane karo panyimpenan data jangka panjang yen kita ngupayakake nggawe model kanthi pemulihan sing dipercaya saka kegagalan.

Sejatine, kanggo njlentrehake masalah kasebut, ayo nganggep manawa kita ngatur Redis tanpa panyimpenan data jangka panjang. Klien bisa mblokir 3 saka 5 conto. Salah sawijining conto sing bisa diblokir klien diwiwiti maneh, lan saiki ana 3 kedadeyan maneh kanggo sumber daya sing padha, sing bisa diblokir, lan klien liyane bisa mblokir conto sing diwiwiti maneh, nglanggar properti keamanan sing nganggep eksklusivitas kunci.

Yen sampeyan ngaktifake data ahead (AOF), kahanan bakal rada apik. Contone, sampeyan bisa promosi server kanthi ngirim printah SHUTDOWN lan miwiti maneh. Wiwit operasi kadaluwarsa ing Redis dileksanakake sacara semantik kanthi cara sing wektu terus mili sanajan server dipateni, kabeh syarat kita apik. Iki normal anggere mati normal wis mesthekake. Apa sing kudu ditindakake yen ana pemadaman listrik? Yen Redis dikonfigurasi kanthi gawan, kanthi fsync nyinkronake ing disk saben detik, mula bisa uga sawise restart kita ora bakal duwe kunci. Secara teoritis, yen kita pengin njamin keamanan kunci sajrone kedadeyan maneh, kita kudu ngaktifake fsync=always ing setelan kanggo panyimpenan data jangka panjang. Iki bakal mateni kinerja, nganti tingkat sistem CP sing digunakake kanthi tradisional kanggo ngleksanakake kunci sing disebarake kanthi aman.

Nanging kahanan luwih apik tinimbang sing katon sepisanan. Ing asas, keamanan algoritma iki wadi amarga nalika Kayata diwiwiti maneh sawise gagal, iku ora melu ing sembarang kunci sing saiki aktif.

Kanggo mesthekake iki, kita mung kudu mesthekake yen sawise gagal, contone tetep ora kasedhiya kanggo sawetara wektu rada ngluwihi TTL maksimum sing digunakake. Kanthi cara iki, kita bakal ngenteni nganti tanggal kadaluwarsa lan rilis otomatis kabeh tombol sing aktif nalika gagal.

Nggunakake restart telat, ing prinsip bisa entuk keamanan sanajan ora ana ketekunan jangka panjang ing Redis. Elinga, yen iki bisa nyebabake denda amarga nglanggar aksesibilitas. Contone, yen mayoritas kasus gagal, sistem bakal dadi global ora kasedhiya kanggo TTL (lan ora ana sumber bisa diblokir sak iki wektu).

Kita nambah kasedhiyan algoritma: kita ngluwihi pamblokiran

Yen karya sing dileksanakake dening klien kasusun saka langkah-langkah cilik, bisa nyuda durasi kunci standar lan ngleksanakake mekanisme kanggo nggedhekake kunci. Ing asas, yen klien sibuk komputasi lan nilai kadaluwarsa kunci kurang mbebayani, sampeyan bisa ngirim skrip Lua kanggo kabeh kedadean sing ngluwihi TTL tombol yen tombol isih ana lan nilaine isih nilai acak dijupuk nalika kunci wis angsal.

Klien mung kudu nganggep kunci bakal dipikolehi maneh yen wis bisa ngunci mayoritas kasus sajrone periode validitas.

Bener, kanthi teknis algoritma kasebut ora owah, mula jumlah maksimal upaya bola-bali kanggo entuk kunci kudu diwatesi, yen ora, properti aksesibilitas bakal dilanggar.

Source: www.habr.com

Add a comment