Mcrouter használata a memcached horizontális skálázására

Mcrouter használata a memcached horizontális skálázására

Bár bármilyen nyelven készült magas terhelésű projektek fejlesztése különleges megközelítést és speciális eszközök alkalmazását igényli, amikor PHP alkalmazásokról van szó, a helyzet annyira fokozódhat, hogy például saját alkalmazás szervert. Jelen cikkben a már jól ismert problémával foglalkozunk, amely a munkamenetek elosztott tárolásával és a memcached adatok gyorsítótárazásával kapcsolatos, és arról, hogyan oldottuk meg ezeket a kihívásokat egy "betegek" projektben.

Az ünnepelt a PHP alapú alkalmazás, amely a symfony 2.3 keretrendszeren alapul, amelyet a vállalkozás tervei között sem szerepel frissíteni. A hagyományos munkamenet tároláson túl a projektben teljes mértékben alkalmazták a "mindent gyorsítótárazás" politikát a memcached-ben: válaszok az adatbázis és API szerverekhez intézett kérésekre, különféle zászlók, blokkok a kód végrehajtásának szinkronizálására és sok másra. Ilyen helyzetben a memcached meghibásodása végzetes következményekkel jár az alkalmazás működésében. Továbbá, a gyorsítótár elvesztése súlyos következményekkel jár: az adatbázis lehetetlenné válik, az API-szolgáltatások elutasítják a kéréseket, stb. A helyzet stabilizálása akár több percet is igénybe vehet, és ez idő alatt a szolgáltatás rendkívül lelassul, vagy akár elérhetetlenné válik.

Biztosítani kellett, hogy az alkalmazás vízszintes skálázásának lehetősége minimális összes változással, azaz a forráskód minimális módosítása mellett, és a funkciók teljes megőrzésével. A gyorsítótárat nemcsak a meghibásodásokkal szemben kell ellenállóvá tenni, hanem törekedni arra is, hogy minimalizáljuk a benne lévő adatok elvesztését.

Miért nem megfelelő magával a memcached-del?

Általában a memcached PHP bővítménye "dobozból" támogatja az adatok és munkamenetek elosztott tárolását. A kulcsok konzisztens hash-elési mechanizmusa lehetővé teszi, hogy az adatokat egyenlően helyezzük el több szerveren, minden egyes kulcsot egy csoportból egy adott szerverhez rendelve, a beépített hibaátalakító eszközök pedig magas rendelkezésre állást biztosítanak a gyorsítótárazási szolgáltatáshoz (de sajnos, nem az adatokhoz).

. A munkamenetek tárolása egy kicsit jobban alakul: beállítható, memcached.sess_number_of_replicas, ennek következtében az adatok több szerverre lesznek mentve, és ha az egyik memcached példány meghibásodik, az adatok a másikról érkeznek. Azonban, ha a szerver visszatér az üzembe, de adatok nélkül (ahogyan ez általában egy újraindítás után történik), néhány kulcsot újraosztanak a javára. Ez lényegében azt jelentheti az ülések adatainak elvesztését, mivel nincs lehetőség „elmenni” egy másik replikához hiba esetén.

A könyvtár alapvető eszközei elsősorban vízszintes skálázásra irányulnak: lehetővé teszik a cache óriási méretekre történő bővítését és a különböző szervereken elhelyezett kódból való hozzáférés biztosítását. Azonban a jelen helyzetben a tárolt adatok mennyisége nem haladja meg néhány gigabájtot, és egy-két csomópont elegendő teljesítményt nyújt. Ennek megfelelően az alapvető eszközök legfeljebb azt biztosíthatnák, hogy a memcached elérhetősége megmaradjon, ha legalább egy cache példány működő állapotban van. Azonban még ezzel a lehetőséggel sem tudtunk élni… Emlékeztetni kell arra, hogy a projektben használt keretrendszer régisége miatt nem tudtuk működésre bírni az alkalmazást szerverekből álló csoporttal. Ne feledkezzünk meg az ülések adatainak elvesztéséről sem: a megrendelőnél a felhasználók tömeges kijelentkezése miatt remegtek a szemek.

Ideális esetben a memcached-ben történő feljegyzés replikálása és a replikák megkerülése hiba vagy probléma esetén. Ezt a stratégiát segített megvalósítani mcrouter.

mcrouter

Ez egy memcached router, amelyet a Facebook fejlesztett ki, hogy megoldja a problémáit. Támogatja a memcached szöveges protokollját, amely lehetővé teszi a memcached telepítések skálázását őrült méretekig. A mcrouter részletes leírását megtalálhatja ebben a bejelentésben. Ezen kívül széles funkcionalitást nyújt, és pontosan azt tudja, amire szükségünk van:

  • a feljegyzés replikálása;
  • a hiba esetén a csoport más szervereire történő visszaállás.

Lássunk neki!

mcrouter konfigurációja

Azonnal áttérek a konfigurációra:

{
 "pools": {
   "pool00": {
     "servers": [
       "mc-0.mc:11211",
       "mc-1.mc:11211",
       "mc-2.mc:11211"
   },
   "pool01": {
     "servers": [
       "mc-1.mc:11211",
       "mc-2.mc:11211",
       "mc-0.mc:11211"
   },
   "pool02": {
     "servers": [
       "mc-2.mc:11211",
       "mc-0.mc:11211",
       "mc-1.mc:11211"
 },
 "route": {
   "type": "OperationSelectorRoute",
   "default_policy": "AllMajorityRoute|Pool|pool00",
   "operation_policies": {
     "get": {
       "type": "RandomRoute",
       "children": [
         "MissFailoverRoute|Pool|pool02",
         "MissFailoverRoute|Pool|pool00",
         "MissFailoverRoute|Pool|pool01"
       ]
     }
   }
 }
}

Miért van három medence? Miért ismétlődnek a szerverek? Nézzük meg, hogyan működik ez.

  • Ebben a konfigurációban a mcrouter kiválasztja azt az utat, amelyen a kérés készült a kérés parancsa alapján. Ezt mondja meg neki a típus: OperationSelectorRoute.
  • A GET-kérések a kezelőbe kerülnek: RandomRoute, amely véletlenszerűen választja ki a medencét vagy utat a tömb objektumai közül. children. Ennek a tömbnek minden eleme egy kezelő: MissFailoverRoute, amely végigmegy az összes szerveren a medencében, amíg választ nem kap az adatokból, ami vissza fog térni az ügyfélnek.
  • Ha csak MissFailoverRoute a három szerverből álló medencét használnánk, akkor minden kérés először az első memcached példányra kerülne, míg a többiek az adatok hiánya miatt tartalékos alapon kapnák a kéréseket. Ez a megközelítés túlnyomó terhelést okozna az első szerveren a listán,ezért úgy döntöttek, hogy három medencét generálnak különböző címsorokkal, és véletlenszerűen választanak belőlük.
  • Minden további kérés (ez írás) az AllMajorityRoutesegítségével kerül feldolgozásra. Ez a kezelő a medence összes szerverére küldi a kéréseket, és válaszokat vár legalább az N/2 + 1-től. Az AllSyncRoute használatát az írási műveletekhez el kellett vetni, mert ez a módszer pozitív választ igényel a csoport szervereitől — ellenkező esetben visszaadja a adattal, tehát csak a TXE jelző nem lesz elegendő. SERVER_ERROR . Bár a mcrouter az elérhető gyorsítótárakban tárolja az adatokat, a PHP hívó funkcióhibát fog visszaadni , és generál egy értesítést. kevésbé szigorú, és lehetővé teszi, hogy akár felének a csomópontokat üzemeltessük a fent említett problémák nélkül. AllMajorityRoute A fő hátránya

ennek a sémának az, hogy ha az adatok valóban nem állnak rendelkezésre a gyorsítótárban, akkor minden egyes ügyfélszolgálati kérés ténylegesen N kérdéshez fog vezetni a memcachedhez — a medencében lévő szerverekhez. Csökkenthetjük a szerverek számát a medencékben, például kettőre: a tárolás megbízhatóságának feláldozásával kevesebb erőforrást nyerhetünk. minden Csökkenthető a poolokban lévő szerverek száma, például kettőre: a tárolás megbízhatóságának feláldozásával egy jobbónagyobb sebességet és kisebb terhelést biztosít a hiányzó kulcsokhoz intézett lekérdezések esetén.

NB: Hasznos hivatkozások a mcrouter tanulmányozásához a wiki dokumentációja és a projekt problémái (beleértve a zártak is), amelyek különféle konfigurációk kincsesbányáját jelentenek.

A mcrouter összetevői és futtatása

Az alkalmazás (és magának a memcached-nek) Kubernetesben fut, így itt van a mcrouter helye is. A konténer összeszereléséhez a következőt használjuk werf, a konfiguráció pedig a következőképpen néz ki:

NB: A cikkben bemutatott listák a repóban találhatóak flant/mcrouter.

configVersion: 1
project: mcrouter
deploy:
 namespace: '[[ env ]]'
 helmRelease: '[[ project ]]-[[ env ]]'
---
image: mcrouter
from: ubuntu:16.04
mount:
- from: tmp_dir
 to: /var/lib/apt/lists
- from: build_dir
 to: /var/cache/apt
ansible:
 beforeInstall:
 - name: Előfeltételek telepítése
   apt:
     name: [ 'apt-transport-https', 'tzdata', 'locales' ]
     update_cache: yes
 - name: mcrouter APT kulcs hozzáadása
   apt_key:
     url: https://facebook.github.io/mcrouter/debrepo/xenial/PUBLIC.KEY
 - name: mcrouter repó hozzáadása
   apt_repository:
     repo: deb https://facebook.github.io/mcrouter/debrepo/xenial xenial contrib
     filename: mcrouter
     update_cache: yes
 - name: Időzóna beállítása
   timezone:
     name: "Europe/Moscow"
 - name: Ellenőrizzük, hogy létezik-e egy nyelv
   locale_gen:
     name: en_US.UTF-8
     state: present
 install:
 - name: mcrouter telepítése
   apt:
     name: [ 'mcrouter' ]

(werf.yaml)

… és létrehozunk egy vázlatot a konfigurációhoz Helm-chart. Az érdekes az, hogy itt csak a konfiguráció generátor van a példányok számától függően (ha valakinek van egy tömörebb és elegánsabb változata — osszátok meg a megjegyzésekben):

{{- $count := (pluck .Values.global.env .Values.memcached.replicas | first | default .Values.memcached.replicas._default | int) -}}
{{- $pools := dict -}}
{{- $servers := list -}}
{{- \/\* Két másolatot töltünk be a szerverekből: "0 1 2 0 1 2" *\/ -}}
{{- range until 2 -}}
 {{- range $i, $_ := until $count -}}
 {{- $servers = append $servers (printf "mc-%d.mc:11211" $i) -}}
 {{- end -}}
{{- end -}}
{{- \/\* A tömbön mozogva N szeletet kapunk: "[0 1 2] [1 2 0] [2 0 1]" *\/ -}}
{{- range $i, $_ := until $count -}}
 {{- $pool := dict "servers" (slice $servers $i (add $i $count)) -}}
 {{- $_ := set $pools (printf "MissFailoverRoute|Pool|poold" $i) $pool -}}
{{- end -}}
---
apiVersion: v1
kind: ConfigMap
metadata:
 name: mcrouter
data:
 config.json: |
 {
 "pools": {{- $pools | toJson | replace "MissFailoverRoute|Pool|" "" -}},
 "route": {
 "type": "OperationSelectorRoute",
 "default_policy": "AllMajorityRoute|Pool|pool00",
 "operation_policies": {
 "get": {
 "type": "RandomRoute",
 "children": {{- keys $pools | toJson }}
 }
 }
 }
 }

(10-mcrouter.yaml)

Bevezetjük a tesztkörnyezetbe, és ellenőrizzük:

# php -a
Interactive mode enabled

php > # Проверяем запись и чтение
php > $m = new Memcached();
php > $m->addServer('mcrouter', 11211);
php > var_dump($m->set('test', 'value'));
bool(true)
php > var_dump($m->get('test'));
string(5) "value"
php > # Работает! Тестируем работу сессий:
php > ini_set('session.save_handler', 'memcached');
php > ini_set('session.save_path', 'mcrouter:11211');
php > var_dump(session_start());
PHP Warning:  Uncaught Error: Failed to create session ID: memcached (path: mcrouter:11211) in php shell code:1
Stack trace:
#0 php shell code(1): session_start()
#1 {main}
  thrown in php shell code on line 1
php > # Не заводится… Попробуем задать session_id:
php > session_id("zzz");
php > var_dump(session_start());
PHP Warning:  session_start(): Cannot send session cookie - headers already sent by (output started at php shell code:1) in php shell code on line 1
PHP Warning:  session_start(): Failed to write session lock: UNKNOWN READ FAILURE in php shell code on line 1
PHP Warning:  session_start(): Failed to write session lock: UNKNOWN READ FAILURE in php shell code on line 1
PHP Warning:  session_start(): Failed to write session lock: UNKNOWN READ FAILURE in php shell code on line 1
PHP Warning:  session_start(): Failed to write session lock: UNKNOWN READ FAILURE in php shell code on line 1
PHP Warning:  session_start(): Failed to write session lock: UNKNOWN READ FAILURE in php shell code on line 1
PHP Warning:  session_start(): Failed to write session lock: UNKNOWN READ FAILURE in php shell code on line 1
PHP Warning:  session_start(): Unable to clear session lock record in php shell code on line 1
PHP Warning:  session_start(): Failed to read session data: memcached (path: mcrouter:11211) in php shell code on line 1
bool(false)
php >

A hibaüzenet szövegére keresve nem találtunk eredményt, azonban a "mcrouter php" keresésnél a projekt legidősebb nyitott problémája került előtérbe — a memcached bináris protokoll támogatásának hiánya. memcached bináris protokoll.

NB: Az ASCII protokoll a memcached-ben lassabb, mint a bináris, és az alapértelmezett kulcsok konzisztens hash-elése csak a bináris protokollal működik. De ez egy konkrét esetben nem okoz problémát.

A dolog biztos: csak az ASCII-protokollra kell váltani, és minden működni fog…. Azonban ebben az esetben a php.net dokumentációban keresett válaszok szokása rossz tréfát űzött. Ott nem találja a helyes választ… kivéve, ha el nem scrolloz a végéig, ahol a „Felhasználói hozzájárulások megjegyzések” szekcióban lesz helyes és méltatlanul leárazott válasz. Igen, a helyes opció neve —.

memcached.sess_binary_protocol . Ezt ki kell kapcsolni, és az ülések működni fognak. Csak annyit kell tennie, hogy a mcrouter konténert a PHP podjába helyezi!Így az infrastruktúra-változtatásokkal sikerült megoldani a kitűzött feladatot: a memcached magas rendelkezésre állásának kérdése megoldódott, a cache tárolásának megbízhatósága nőtt. Az alkalmazásnak nyújtott nyilvánvaló előnyök mellett ez mozgásteret biztosított a platform fejlesztésében: amikor minden komponensnek van tartalékja, az adminisztrátor élete sokkal egyszerűbb. Igen, ennek a módszernek is megvannak a hátrányai, és „tákolmánynak” tűnhet, de ha pénzt takarít meg, eltünteti a problémát, és nem okoz újakat — miért ne?

Zárás

„Gyakorlat dapp-al”

P.S.

Olvasson további információkat a blogunkban:

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster