Բաշխված արգելափակումներ
1) Ինչո՞ ւ (և երբ) բաշխված արգելափակում է անհրաժեշտ)
Բաշխված արգելափակումը մի մեխանիզմ է, որը երաշխավորում է փոխկապակցվածությունը մի քանի հանգույցների միջև կրիտիկական հատվածի համար։ Տիպիկ առաջադրանքներ
Առաջնորդությունը (leader elect) ֆոնային առաջադրանքի/գլուխգործոցի համար։
Միակ նկարչի սահմանափակումը ընդհանուր ռեսուրսի վրա (ֆայլերի տեղափոխումը, սխեմայի իրականացումը, բացառիկ հիբրիդային քայլը)։
Ագրեգատի հաջորդական մշակումը (wallet/order) եթե անհնար է այլ կերպ հասնել idempotention/կարգավորում։
Երբ ավելի լավ է չօգտագործել ամրոցը
Եթե դուք կարող եք անել ippotent ups.ru, CAS (compare-and-2019) կամ հերթը (per-key ordering)։
Եթե ռեսուրսը թույլ է տալիս բաղադրիչ վիրահատություններ (CRDT, հաշվիչներ)։
Եթե խնդիրը լուծվում է գործարքով մեկ պահեստում։
2) Սպառնալիքների և հատկությունների մոդել
Ձախողումներ և դժվարություններ
Ցանցը 'ուշացումներ, բաժանումը (part.ru), միգրանտների կորուստը։
Գործընթացները 'GC դադար, stop-the-world, crash ամրոցը վերցնելուց հետո։
Ժամանակը 'ժամացույցի և շարժիչների խաչմերուկը կոտրում են TTL մոտեցումները։
Կրկին, «զոմբի» գործընթացը ցանցից հետո կարող է մտածել, որ դեռ ունի ամրոց։
Ցանկալի հատկություններ
Անվտանգություն 'ոչ ավելի քան մեկ իրական սեփականատեր (safety)։
Գոյատևումը 'ամրոցը ազատվում է սեփականատիրոջ խնայողության ժամանակ (liveness)։
Արդարություն 'սովամահ չկա։
Անկախությունը ժամից 'ճկունությունը կախված չէ wall-clock-ից (կամ փոխհատուցվում է fencing tokens)։
3) Հիմնական մոդելները
3. 1 Least (վարձակալական ամրոց)
Ամրոցը տրվում է TTL-ից։ Սեփականատերը պարտավոր է այն երկարացնել մինչև եզրափակիչ (heartbeat/keepalive)։
Պլյուսներ 'ինքնաբերություն քրեշի ժամանակ։
Ռիսկերը. Եթե սեփականատերը «վարսակ» է և շարունակում է աշխատել, բայց կորցրել է երկարացումը, կարող է առաջանալ երկակի սեփականություն։
3. 2 Fencing token (Oair)
Յուրաքանչյուր հաջողակ գրավման դեպքում տրվում է միապաղաղ աճող համարը։ Ռեսուրսի սպառողները (BD) ստուգում են հոսանքը և մերժում վիրահատությունները հին համարով։
Սա չափազանց կարևոր է TTL/lement և ցանցային բաժիններում, պաշտպանում է «հին» սեփականատիրոջից։
3. 3 Direrum-ամրոցներ (CP համակարգեր)
Օգտագործվում են բաշխված համաձայնություն (Raft/Paxos; etcd/ZooKeeper/Consul), ձայնագրությունը կապված է wwww.dplit-brine մեծ մասի հետ։
Գումարած ՝ անվտանգության ուժեղ երաշխիքներ։
Մինուս 'կվորումի զգայունությունը (երբ նա կորցնում է իր գոյատևումը)։
3. 4 AP ամրոցներ (in-memory/cash + կրկնօրինակումը)
Օրինակ, Redis-կլաստերը։ Բարձր հասանելիություն և արագություն, բայց առանց անվտանգության խիստ երաշխիքների ցանցային բաժանումներում։ Պահանջում են fencing կապույտ կողմում։
4) Պլատֆորմներ և արտոնագրեր
4. 1 etcd/ZooKeeper/Consul (առաջարկվել է strong winks)
Էֆեմերական կոմպոզիցիաները (ZK) կամ նստաշրջանները/leases (etcd), բանալին գոյություն ունի մինչ նստաշրջանը։
Սեսիոն keepalive; Քվորումի կորստը նստաշրջանը լրանում է ռուսական ամրոցը ազատվում է։
Սերիական կոմպոզիցիաները (ZK 'EPHEMERAL _ SEQUENTIAL ") սպասելու համար արդարություն։
Etcd (Go) ուրվագիծը
go cli, _:= clientv3. New(...)
lease, _:= cli. Grant(ctx, 10) // 10s lease sess, _:= concurrency. NewSession(cli, concurrency. WithLease(lease. ID))
m:= concurrency. NewMutex(sess, "/locks/orders/42")
if err:= m. Lock(ctx); err!= nil { / handle / }
defer m. Unlock(ctx)
4. 2 Redis (ուշադիր)
Դասական '«DISkey value NX PX ttl»։
Խնդիրները
Կրկնօրինակումը/ֆեյլերը կարող են թույլ տալ միաժամանակ սեփականատերերին։
Redlock-ը մի քանի ինստանսներից նվազեցնում է ռիսկը, բայց չի վերացնում։ վիճելի է ոչ հուսալի ցանցի հետ։
Ավելի ապահով է օգտագործել Redis-ը որպես արագ կոորդինացիոն շերտ, բայց միշտ լրացնել fencing token-ը բուժական ինտերֆեյսում։
Օրինակ (Lua-unlock)
lua
-- release only if value matches if redis. call("GET", KEYS[1]) == ARGV[1] then return redis. call("DEL", KEYS[1])
else return 0 end
4. 3 BD ամրոցներ
PostgreSQL advisory infadisory: lok-ի շրջանակներում Postgres (գործընթացը/նստաշրջան)։
Լավ է, երբ բոլոր կրիտիկական հատվածները և այսպես նույն BD-ում։
SQL:sql
SELECT pg_try_advisory_lock(42); -- take
SELECT pg_advisory_unlock(42); -- let go
4. 4 Ալյումինե/ամպային «ամրոցներ»
S3/GCS + օբյեկտի մետատվական լոկը '«If-Match» (ETag) պայմաններով, ըստ էության CAS-ի։
Հարմար է Բեքապների/միգրացիաների համար։
5) Անվտանգ ամրոցի դիզայնը
5. 1 Սեփականատիրոջ ինքնությունը
Պահեք «owner _ id» (հանգույց # pid # start _ time) + պատահական հոսանք unlock-ի համար։
Կրկնվող unlock չպետք է հանի օտար ամրոցը։
5. 2 TTL և երկարացում
TTL Երկարացումը պարբերաբար է (օրինակ, յուրաքանչյուր «TTL/3»), deadron-ից։ 5. 3 Fencing token կապույտ Արտաքին ռեսուրսը փոխող հատվածը պետք է փոխանցի «fencing _ token»։ Սինքը (BD/kash/պահեստ) պահպանում է «lection _ token» և մերժում է ավելի քիչ 5. 4 Սպասելու և արդարության փոփոխություն ZK-ում '«EPHEMERAL _ SEQUENTIAL» և դիտորդները' հաճախորդը սպասում է մոտակա նախորդի ազատագրմանը։ Etcd-ում բանալիներ են, որոնք ունեն ստուգում/տարբերակիչ; մեկ այլ «mod _ revision»։ 5. 5 Վարք split-brain CP մոտեցում. Առանց կվորումի դուք չեք կարող վերցնել ամրոցը, ավելի լավ է դիմակայել, քան կոտրել safety-ը։ AP-մոտեցում. Բաժանված կղզիներում ռուսական առաջընթացը անհրաժեշտ է fencing-ի համար։ 6) Առաջնորդություն (leader elect) Etcd/ZK-ում «առաջնորդը» բացառիկ էպեմերիկ բանալին է։ մյուսները ստորագրված են փոփոխության վրա։ Առաջնորդը գրում է heartbeats; կորուստը վերընտրումն է։ Առաջնորդի բոլոր վիրահատությունները ուղեկցեք fencing token (դարաշրջանի/2019)։ 7) Սխալներն ու դրանց վերամշակումը Հաճախորդը վերցրեց ամրոցը, բայց քրեշը մինչև կանոնավոր նորմերի աշխատանքը, ոչ ոք չի վնասվի։ TTL/նստաշրջանը կազատվեն։ Ամրոցն ավարտված է աշխատանքի մեջտեղում Պարտական է watchdog: Եթե երկարաձգումը անհաջող է, կրիտիկական հատվածը ընդհատելը և փոխհատուցելը։ Ոչ մի «ավելի ուշ», առանց ամրոցի կրիտիկական հատվածը չի կարելի շարունակել։ Երկար դադար (GC/stop-the-world) ռուսական երկարացումը տեղի չունեցավ, մյուսը վերցրեց ամրոցը։ Աշխատանքային գործընթացը պետք է հայտնաբերի սեփականության կորուստը (keepalive ալիքը) և ընդհատի։ 8) Դեդլոկները, առաջնահերթությունները և հակադարձումները Դեդլոկները բաշխված աշխարհում հազվադեպ են (ամրոցը սովորաբար մեկ է), բայց եթե ամրոցները մի քանի են, պահպանեք մեկ գրավման կարգը (www.k ordering)։ Գերակայությունների հակադարձումը 'ցածր պրիմիտիվ սեփականատերը պահում է ռեսուրսը, մինչև բարձր պրիմիտիվ սպասում են։ Որոշումները ՝ TTL-limits, emption (եթե բիզնեսը թույլ է տալիս), sharding ռեսուրսը։ Սոված 'օգտագործեք սպասման գծերը (ZK-ստորգետնյա կոդեր) արդարության համար։ 9) Դիտողականությունը Մետրիկները `lock_acquire_total{status=ok|timeout|error}` `lock_hold_seconds{p50,p95,p99}` «fencing _ token _ value» (մոնոտոնիում) `lease_renew_fail_total` «split _ brain _ medvented _ total» (որքա՞ ն փորձեր են մերժվել քվորումի բացակայության պատճառով) `preemptions_total`, `wait_queue_len` Լոգա/թրեյսինգ `lock_name`, `owner_id`, `token`, `ttl`, `attempt`, `wait_time_ms`, `path` (для ZK), `mod_revision` (etcd). «Acquire www.critical softw.rele.ru» -ի սպանները արդյունքով։ Ալերտա Աճը 'leance _ renew _ fail _ total "։ `lock_hold_seconds{p99}` > SLO. «Orfen» ամրոցները (առանց heartbeat)։ Սպասվող հերթերը։ 10) Գործնական օրինակներ 10. 1 Անվտանգ Redis-ամրոցը fencing (կեղծ) 1. Մենք կառուցում ենք հոսանքների հաշվիչ վստահելի սարքում (օրինակ ՝ Postgres/etcd)։ 2. Հաջողակ «MSNX PX» -ի դեպքում մենք կարդում ենք/երկարացնում ենք հոսանքը, և ռեսուրսի բոլոր փոփոխությունները կատարվում են BD/ծառայության մեջ հոսանքի ստուգմամբ։ 10. 2 etcd Mutex + watchdog (Go) 10. 3 Առաջնորդություն ZK (Java, Curance) 10. 4 Postgres advisory dedeline (SQL + app) 11) Թեստային պլեյբուկները (Game Days) Քվորումի կորուստ 'անջատել 1-2 հանգույցները etcd-ը, փորձելով վերցնել ամրոցը չպետք է անցնի։ GC-դադար/stop-the-world: արհեստականորեն ձերբակալել սեփականատիրոջ հոսքը փորձարկելու համար, որ watchdog-ը ընդհատում է աշխատանքը։ Split-brain: Ցանցի բաժանման էմուլյացիան սեփականատիրոջ և ամրոցի դռների միջև նոր սեփականատերը ստանում է ավելի բարձր fencing token, հին 'մերժում է կապույտը։ Clock skew/drift: Պահել ժամացույցը սեփականատիրոջից (Redis/leport-ի համար) խորհուրդ է տալիս համոզվել, որ ճիշտ ուղղությունը տրամադրվում է հոսանքներով/ստուգումներով։ Crash before rele.ru-ը 'ռուսական գործընթացի անկումը ազատվում է TTL/նստաշրջանով։ 12) Anti-patterna Մաքուր TTL-ամրոցը առանց fencing-ի, երբ հասանելի է արտաքին ռեսուրսին։ Ապավինել տեղական ժամանակներին ճկունության համար (առանց HLC/fencing)։ Ամրոցների բաժանումը մեկ Redis-ի միջոցով ֆեյլերի հետ միջավայրում և առանց կրկնությունների։ Անվերջ կրիտիկական հատվածը (TTL «դարի վրա»)։ «Օտար» ամրոցի հեռացումը առանց «owner _ id »/token։ Backoff + jitter-ի բացակայությունը բացատրում է «փոթորիկ» փորձերը։ Միակ համաշխարհային ամրոցը «ամեն ինչի վրա» հակամարտությունների պայուսակն է։ Շարդինգը ավելի լավ է։ 13) Ներդրման չեկի ցուցակ 14) FAQ Q: Արդյո՞ ք Redis-ամրոցը «MSNX PX»։ Ա 'Միայն եթե ռեսուրսը ստուգում է fencing token-ը։ Հակառակ դեպքում ցանցային բաժանման դեպքում երկու սեփականատեր հնարավոր է։ Q 'Ի՞ նչ ընտրել լռելյայն։ A 'Խիստ երաշխիքների համար' etcd/ZooKeeper/Consul (CP)։ Մեկ BD-ի ներսում հեշտ առաջադրանքների համար 'advisory windows Postgres։ Redis-ը միայն fencing-ից է։ Q: Ի՞ նչ TTL տեղադրել։ A: 'TTL 24p99 կրիտիկական հատվածի տևողությունը 242 "և բավականին կարճ" զոմբիի "արագ մաքրման համար։ Երկարացումը յուրաքանչյուր «TTL/3» է։ Q 'Ինչպե՞ ս խուսափել սովից։ A 'Սպասման հերթը (ZK sequential) կամ fairness-ալգորիթմը; փորձերի սահմանափակում և արդար պլանավորում։ Q 'Արդյո՞ ք ժամանակի համաժամեցումը անհրաժեշտ է։ A 'Ճիշտ լինելու համար' ոչ (օգտագործեք fencing)։ Վիրահատական կանխատեսելիության համար այո (NTP/PTP), բայց մի ապավինեք wall-clock-ին ամրոցի տրամաբանության մեջ։ 15) Արդյունքները Հուսալի բաշխված կողպեքները կառուցվում են քրոմային (etcd/ZK/Consul) leport + keepalive-ով և անպայման ավելացվում են fencing token-ի վրա փոփոխվող ռեսուրսի մակարդակում։ Ցանկացած TTL/Redis մոտեցումներ առանց ցանկության ռիսկի ռիսկ են։ Նախ մտածեք քաուզալության և գաղափարախոսության մասին, օգտագործեք արգելափակումներ այնտեղ, որտեղ դուք չեք կարող, չափեք, փորձարկեք ձախողված ռեժիմները, և ձեր «քննադատական հատվածները» կմնան քննադատական միայն իմաստով, և ոչ թե վճարումների քանակով։sql
UPDATE wallet
SET balance = balance +:delta, last_token =:token
WHERE id =:id AND:token > last_token;python acquire token = db. next_token ("locks/orders/42") # monotone ok = redis. set("locks:orders:42", owner, nx=True, px=ttl_ms)
if not ok:
raise Busy()
critical op guarded by token db. exec("UPDATE orders SET... WHERE id=:id AND:token > last_token",...)
release (compare owner)go ctx, cancel:= context. WithCancel(context. Background())
sess, _:= concurrency. NewSession(cli, concurrency. WithTTL(10))
m:= concurrency. NewMutex(sess, "/locks/job/cleanup")
if err:= m. Lock(ctx); err!= nil { /... / }
// Watchdog go func() {
<-sess. Done ()//loss of session/quorum cancel ()//stop working
}()
doCritical (ctx )//must respond to ctx. Done()
_ = m. Unlock(context. Background())
_ = sess. Close()java
LeaderSelector selector = new LeaderSelector(client, "/leaders/cron", listener);
selector. autoRequeue();
selector. start(); // listener. enterLeadership() с try-finally и heartbeatsql
SELECT pg_try_advisory_lock(128765); -- attempt without blocking
-- if false --> return via backoff + jitter