Notkun mcrouter til að skala minnisgeymslu lárétt

Notkun mcrouter til að skala minnisgeymslu lárétt

Að þróa mikið álagsverkefni á hvaða tungumáli sem er krefst sérstakrar nálgunar og notkunar á sérstökum verkfærum, en þegar kemur að forritum í PHP getur ástandið versnað svo að þú þarft að þróa td. eigin forritaþjón. Í þessari athugasemd munum við tala um kunnuglega sársaukann við dreifða lotugeymslu og skyndiminni gagna í memcached og hvernig við leystum þessi vandamál í einu „deild“ verkefni.

Hetja tilefnisins er PHP forrit byggt á symfony 2.3 ramma, sem er alls ekki innifalið í viðskiptaáætlunum um að uppfæra. Til viðbótar við nokkuð staðlaða lotugeymslu, nýtti þetta verkefni sig að fullu stefnu um að „geyma allt“ í memcached: svör við beiðnum til gagnagrunnsins og API netþjóna, ýmsir fánar, læsingar til að samstilla keyrslu kóða og margt fleira. Í slíkum aðstæðum verður niðurbrot á memcached banvænt fyrir rekstur forritsins. Að auki leiðir skyndiminnitap til alvarlegra afleiðinga: DBMS byrjar að springa í saumana, API þjónusta byrjar að banna beiðnir osfrv. Að koma á jafnvægi getur tekið tugi mínútna og á þessum tíma verður þjónustan hræðilega hæg eða algjörlega ófáanleg.

Við þurftum að veita hæfileikinn til að skala forritið lárétt með lítilli fyrirhöfn, þ.e. með lágmarksbreytingum á frumkóðanum og fullri virkni varðveitt. Gerðu skyndiminni ekki aðeins ónæm fyrir bilunum heldur reyndu einnig að lágmarka gagnatap frá því.

Hvað er athugavert við memcached sjálft?

Almennt séð styður memcached viðbótin fyrir PHP dreifð gögn og setugeymslu úr kassanum. Vélbúnaðurinn fyrir stöðuga lyklaþjöppun gerir þér kleift að setja gögn jafnt á marga netþjóna, með einstaka hætti að sérhvern tiltekinn lykil að tilteknum netþjóni úr hópnum, og innbyggð bilunarverkfæri tryggja mikið aðgengi að skyndiminni (en því miður, engin gögn).

Hlutirnir eru aðeins betri með setugeymslu: þú getur stillt memcached.sess_number_of_replicas, sem leiðir til þess að gögnin verða geymd á nokkrum netþjónum í einu, og ef bilun verður í einu memcached tilviki verða gögnin flutt frá öðrum. Hins vegar, ef þjónninn kemur aftur á netið án gagna (eins og venjulega gerist eftir endurræsingu), verður sumum lyklunum endurdreift honum í hag. Í raun mun þetta þýða tap á lotugögnum, þar sem það er engin leið til að „fara“ í aðra eftirmynd ef sleppa.

Stöðluð verkfæri bókasafns miða aðallega að lárétt stigstærð: þeir leyfa þér að auka skyndiminni í risastórar stærðir og veita aðgang að því frá kóða sem hýst er á mismunandi netþjónum. Hins vegar, í okkar aðstæðum, er rúmmál vistaðra gagna ekki meira en nokkur gígabæt, og árangur eins eða tveggja hnúta er alveg nóg. Í samræmi við það gætu einu gagnlegu staðlaðu verkfærin verið að tryggja framboð á memcached á meðan viðhalda að minnsta kosti einu skyndiminni tilviki í vinnuástandi. Hins vegar var ekki hægt að nýta jafnvel þetta tækifæri... Hér er rétt að rifja upp fornöld rammans sem notuð var í verkefninu, þess vegna var ómögulegt að fá forritið til að vinna með hópi netþjóna. Við skulum heldur ekki gleyma tapi á lotugögnum: auga viðskiptavinarins kipptist við af miklu útskráningu notenda.

Helst var þess krafist afritun skráa í minnistækjum og framhjá eftirmyndum ef um mistök eða mistök er að ræða. Hjálpaði okkur að innleiða þessa stefnu mcrouter.

mcrouter

Þetta er memcached leið þróað af Facebook til að leysa vandamál sín. Það styður memcached textasamskiptareglur, sem leyfir mælikvarða memcached uppsetningar í geðveikum hlutföllum. Ítarlega lýsingu á mcrouter er að finna í þessari tilkynningu. Meðal annars breiður virkni það getur gert það sem við þurfum:

  • endurtaka skrá;
  • gera fallback til annarra netþjóna í hópnum ef villa kemur upp.

Komdu þér í gang!

mcrouter stillingar

Ég fer beint í stillingar:

{
 "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"
       ]
     }
   }
 }
}

Af hverju þrjár sundlaugar? Af hverju eru netþjónar endurteknir? Við skulum reikna út hvernig það virkar.

  • Í þessari stillingu velur mcrouter slóðina sem beiðnin verður send á út frá beiðni skipuninni. Gaurinn segir honum þetta OperationSelectorRoute.
  • GET beiðnir fara til stjórnandans RandomRoutesem velur af handahófi laug eða leið meðal fylkishluta children. Hver þáttur í þessu fylki er síðan meðhöndlun MissFailoverRoute, sem mun fara í gegnum hvern netþjón í lauginni þar til hann fær svar með gögnum, sem verður skilað til viðskiptavinarins.
  • Ef við notuðum eingöngu MissFailoverRoute með hópi þriggja netþjóna, þá myndu allar beiðnir koma fyrst í fyrsta memcached tilvikið, og restin fengi beiðnir á eftirstöðvum þegar engin gögn eru til. Slík nálgun myndi leiða til of mikið álag á fyrsta netþjóninn á listanum, svo það var ákveðið að búa til þrjár laugar með vistföngum í mismunandi röð og velja þær af handahófi.
  • Allar aðrar beiðnir (og þetta er skrá) eru unnar með því að nota AllMajorityRoute. Þessi stjórnandi sendir beiðnir til allra netþjóna í lauginni og bíður eftir svörum frá að minnsta kosti N/2 + 1 þeirra. Frá notkun AllSyncRoute fyrir skrifa aðgerðir þurfti að yfirgefa, þar sem þessi aðferð krefst jákvæð viðbrögð frá allt netþjóna í hópnum - annars kemur hann aftur SERVER_ERROR. Þrátt fyrir að mcrouter muni bæta gögnunum við tiltæk skyndiminni, þá virkar kalla PHP mun skila villu og mun gefa tilkynningu. AllMajorityRoute er ekki svo strangt og leyfir að allt að helmingur eininga sé tekinn úr notkun án vandkvæða sem lýst er hér að ofan.

Helsti ókosturinn Þetta kerfi er að ef það eru í raun engin gögn í skyndiminni, þá fyrir hverja beiðni frá viðskiptavininum verða N beiðnir til memcached í raun framkvæmdar - til til allra netþjóna í sundlauginni. Við getum fækkað fjölda netþjóna í laugum, til dæmis í tvo: fórna áreiðanleika geymslunnar, fáum viðоmeiri hraði og minna álag frá beiðnum til lykla sem vantar.

NB: Þú gætir líka fundið gagnlega tengla til að læra á mcrouter skjöl á wiki и verkefnamál (þar á meðal lokaðar), sem táknar heilt geymsluhús af ýmsum uppsetningum.

Byggir og keyrir mcrouter

Forritið okkar (og memcached sjálft) keyrir í Kubernetes - í samræmi við það er mcrouter einnig staðsett þar. Fyrir gámasamsetning við notum werf, stillingin fyrir sem mun líta svona út:

NB: Skráningarnar sem gefnar eru upp í greininni eru birtar í geymslunni 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: Install prerequisites
   apt:
     name: [ 'apt-transport-https', 'tzdata', 'locales' ]
     update_cache: yes
 - name: Add mcrouter APT key
   apt_key:
     url: https://facebook.github.io/mcrouter/debrepo/xenial/PUBLIC.KEY
 - name: Add mcrouter Repo
   apt_repository:
     repo: deb https://facebook.github.io/mcrouter/debrepo/xenial xenial contrib
     filename: mcrouter
     update_cache: yes
 - name: Set timezone
   timezone:
     name: "Europe/Moscow"
 - name: Ensure a locale exists
   locale_gen:
     name: en_US.UTF-8
     state: present
 install:
 - name: Install mcrouter
   apt:
     name: [ 'mcrouter' ]

(werf.yaml)

... og teikna það upp Hjálmarkort. Það áhugaverða er að það er aðeins stillingarrafall byggt á fjölda eftirmynda (ef einhver hefur lakónískari og glæsilegri valmöguleika, deildu honum í athugasemdum):

{{- $count := (pluck .Values.global.env .Values.memcached.replicas | first | default .Values.memcached.replicas._default | int) -}}
{{- $pools := dict -}}
{{- $servers := list -}}
{{- /* Заполняем  массив двумя копиями серверов: "0 1 2 0 1 2" */ -}}
{{- range until 2 -}}
 {{- range $i, $_ := until $count -}}
   {{- $servers = append $servers (printf "mc-%d.mc:11211" $i) -}}
 {{- end -}}
{{- end -}}
{{- /* Смещаясь по массиву, получаем N срезов: "[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|pool%02d" $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)

Við rúllum því út í prófunarumhverfið og athugum:

# 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 >

Leit að texta villunnar gaf engar niðurstöður, en fyrir fyrirspurnina „mcrouter php„Í forgrunni var elsta óleysta vandamál verkefnisins - skortur á stuðningi memcached tvöfaldur siðareglur.

NB: ASCII samskiptareglan í memcached er hægari en sú tvöfalda, og staðlaðar leiðir til samkvæmrar lyklaþjöppunar virka aðeins með tvíundarsamskiptareglunum. En þetta skapar ekki vandamál fyrir tiltekið mál.

Bragðið er í pokanum: allt sem þú þarft að gera er að skipta yfir í ASCII siðareglur og allt mun virka.... Hins vegar, í þessu tilfelli, er venjan að leita að svörum í skjöl á php.net lék grimman brandara. Þú munt ekki finna rétta svarið þar... nema auðvitað að þú flettir til enda, hvar í kaflanum "Notandi lagði fram athugasemdir" verður trúr og ósanngjarnt niðurkjörið svar.

Já, rétt valmöguleikaheiti er memcached.sess_binary_protocol. Það verður að vera óvirkt, eftir það byrja loturnar að virka. Það eina sem er eftir er að setja ílátið með mcrouter í belg með PHP!

Ályktun

Þannig gátum við leyst vandamálið með aðeins innviðabreytingum: vandamálið með villuþol í geymsluplássi hefur verið leyst og áreiðanleiki skyndiminnisgeymslu hefur verið aukinn. Til viðbótar við augljósa kosti forritsins gaf þetta svigrúm til að vinna á pallinum: þegar allir íhlutir eru með varaforða er líf stjórnandans einfaldað til muna. Já, þessi aðferð hefur líka sína galla, hún kann að líta út eins og „hækja“, en ef hún sparar peninga, grafir vandamálið og veldur ekki nýjum - hvers vegna ekki?

PS

Lestu líka á blogginu okkar:

Heimild: www.habr.com

Bæta við athugasemd