mcrouter erabiliz, memcached horizontalean eskalatzeko

mcrouter erabiliz, memcached horizontalean eskalatzeko

Edozein hizkuntzatan karga handiko proiektuak garatzeak ikuspegi berezia eta tresna bereziak erabiltzea eskatzen du, baina PHP aplikazioei dagokienez, egoera hainbeste areagotu daiteke, non garatu behar duzula, adibidez, aplikazio-zerbitzari propioa. Ohar honetan, saioen biltegiratze banatua eta datuen cachean memcached-en denek ezagutzen duten minari buruz hitz egingo dugu eta arazo hauek "ward" proiektu batean nola konpondu genituen.

Okasioaren heroia symfony 2.3 esparruan oinarritutako PHP aplikazio bat da, eta hori ez dago batere eguneratzeko negozio-planetan sartuta. Saioen biltegiratze nahiko estandarraz gain, proiektu honek guztiz aprobetxatu zuen "Dena gordetzeko" politika memcached-en: datu-baseari eta API zerbitzariei egindako eskaerei erantzunak, hainbat bandera, kodearen exekuzioa sinkronizatzeko blokeoak eta askoz gehiago. Egoera horretan, memcached-eko eten bat larri bihurtzen da aplikazioak funtziona dezan. Horrez gain, cachea galtzeak ondorio larriak dakartza: DBMSa lehertzen hasten da, API zerbitzuak eskaerak debekatzen, etab. Baliteke hamar minutu behar izatea egoera egonkortzeko, eta denbora horretan zerbitzua izugarri motelduko da edo are erabilgarri geratuko da.

Eman behar genuen odol isurketa gutxirekin aplikazioaren eskala horizontala egiteko aukera, hau da. iturburu-kodean gutxieneko aldaketarekin eta funtzionalitate osoarekin. Egin cachea akatsak jasan ez ezik, datu-galera gutxitzen ere saiatu.

Zer dago gaizki memcached-ekin?

Oro har, PHP-rako memcached luzapenak kutxatik kanpo datuen eta saioen biltegiratze banatua onartzen du. Gakoen hashing koherentearen mekanismoari esker, datuak zerbitzari askotan berdin jar ditzakezu, gako espezifiko bakoitza taldeko zerbitzari zehatz bati zuzenduz, eta integratutako hutsegite-tresnek cache-zerbitzuaren erabilgarritasun handia bermatzen dute (baina, zoritxarrez, daturik ez).

Saioaren biltegiratzearekin, gauzak apur bat hobeak dira: konfigura dezakezu memcached.sess_number_of_replicas, horren ondorioz datuak hainbat zerbitzaritan gordeko dira aldi berean, eta memcached instantzia baten hutsegiterik gertatuz gero, datuak beste batzuetatik emango dira. Hala ere, zerbitzaria daturik gabe konektatzen bada (berrabiarazi ondoren gertatu ohi den bezala), gako batzuk bere alde banatuko dira. Izan ere, horrek esan nahi du saioaren datuak galtzea, ez baitago beste erreplika batera "joateko" modurik huts bat gertatuz gero.

Liburutegiko tresna estandarrak bideratzen dira batez ere horizontala eskalatzea: cachea tamaina erraldoietara handitzeko eta zerbitzari ezberdinetan ostatatutako kodetik sartzeko aukera ematen dute. Hala ere, gure egoeran, gordetako datuen kopuruak ez du hainbat gigabyte gainditzen, eta nodo baten edo biren errendimendua nahikoa da. Horren arabera, baliabide erregular erabilgarri batetik, memcached-en erabilgarritasuna bermatu ahal izango dute, gutxienez, cachearen instantzia bat lan-egoeran mantentzen duten bitartean. Hala ere, aukera hori ere ez zen aprobetxatu... Hemen proiektuan erabilitako markoaren antzinatasuna gogoratu behar dugu, horregatik ezin izan da aplikazioa zerbitzari multzo batekin funtzionatzea. Ez dezagun ahaztu saioko datuen galeraz ere: bezeroaren begiak kikildu egin ziren erabiltzaileen saioaren amaiera masibotik.

Egokiena, beharrezkoa da memcached sarrera erreplikatzea eta erreplikak saihestuz huts edo akatsen kasuan. Estrategia hau ezartzen lagundu digu mcrouter.

mcrouter

Hau Facebook-ek beren arazoak konpontzeko garatutako memcached bideratzailea da. Memcached testu-protokoloa onartzen du, eta horrek aukera ematen du eskalatu memcached instalazioak neurri zoroetaraino. mcrouter-en deskribapen zehatza hemen aurki daiteke iragarki hau. Besteez gain funtzionaltasun zabala behar duguna egin dezake:

  • erregistroa errepikatu;
  • akatsen bat gertatuz gero, taldeko beste zerbitzari batzuetara itzuli.

Ekin lanari!

mcrouter konfigurazioa

Goazen zuzenean konfiguraziora:

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

Zergatik hiru igerileku? Zergatik errepikatzen dira zerbitzariak? Ea nola funtzionatzen duen.

  • Konfigurazio honetan, mcrouter-ek eskaera bidaliko den bidea aukeratzen du eskaera komandoaren arabera. Motak esaten dio horri buruz OperationSelectorRoute.
  • GET eskaerak kudeatzailera doaz RandomRoute, zeinak ausaz aukeratzen duen multzo bat edo ibilbide bat array-objektuen artean children. Array honen elementu bakoitza kudeatzaile bat da MissFailoverRoute, igerilekuko zerbitzari bakoitzean errepikatzen duena datuekin erantzuna jaso arte, bezeroari itzultzen zaiona.
  • Bakarrik erabiliko bagenu MissFailoverRoute hiru zerbitzariko multzoarekin, eskaera guztiak lehenik memcached-en lehen instantziara iritsiko lirateke, eta gainerakoek eskaerak jasoko lituzkete daturik ez dagoenean. Horrelako planteamendu batek ekarriko luke gehiegizko karga zerrendako lehen zerbitzarian, beraz, hiru multzo sortzea erabaki zen helbideak beste sekuentzia batean eta ausaz aukeratzea.
  • Beste eskaera guztiak (eta hau erregistro bat da) erabiliz prozesatzen dira AllMajorityRoute. Kudeatzaile honek igerilekuko zerbitzari guztiei eskaerak bidaltzen dizkie eta gutxienez horietako N/2 + 1en erantzunen zain geratzen da. Erabileratik AllSyncRoute izan ere, idazketa-eragiketak alde batera utzi behar izan ziren, metodo honek erantzun positiboa eskatzen baitu guztiak taldeko zerbitzariak - bestela itzuliko da SERVER_ERROR. mcrouter-ek datuak eskuragarri dauden cacheetan gehituko dituen arren, deitzen duen PHP funtzioa errore bat itzuliko du eta oharra sortu. AllMajorityRoute ez da hain zorrotza eta unitateen erdia desafektatzeko aukera ematen du goian deskribatutako arazorik gabe.

Desabantaila nagusia eskema hau da, benetan cachean daturik ez badago, bezeroaren eskaera bakoitzerako, N memcached-erako eskaerak exekutatu egingo dira, hau da. guztiontzat zerbitzariak igerilekuan. Igerilekuetako zerbitzarien kopurua, adibidez, bitara murriztu dezakezu: biltegiratze fidagarritasuna sakrifikatu, b lortzen duguΠΎAbiadura handiagoa eta karga gutxiago falta diren gakoen eskaeren ondorioz.

NB: mcrouter ikasteko esteka erabilgarriak ere izan daitezke wikiko dokumentazioa ΠΈ proiektuaren gaiak (itxiak barne), hainbat konfigurazioko biltegi oso bat irudikatuz.

mcrouter eraikitzea eta martxan jartzea

Aplikazioak (eta memcachek berak) Kubernetesen funtzionatzen du guretzat; horren arabera, mcrouter ere hor dago. Izan ere edukiontzien muntaia erabiltzen dugu werf, honen konfigurazioa honela izango da:

NB: artikuluan emandako zerrendak biltegian argitaratzen dira 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)

... eta bota Helm taula. Interesgarriena - hemen konfigurazio-sorgailu bat baino ez dago erreplika kopurutik (Norbaitek bertsio zehatzagoa eta dotoreagoa badu - partekatu iruzkinetan):

{{- $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)

Proba-ingurunera zabaltzen gara eta egiaztatuko dugu:

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

Errorearen testuaren bilaketak ez du emaitzarik eman, ordea, kontsultak "mikrouter phpΒ» abangoardian proiektuaren konpondu gabeko arazo zaharrena zegoen - laguntza falta memcached protokolo bitarra.

NB: Memcached-en ASCII protokoloa bitarra baino motelagoa da, baita gakoen hashing koherenteak protokolo bitarrekin soilik funtzionatzen duten bitarteko arruntek. Baina horrek ez du arazorik sortzen kasu jakin baterako.

Poltsan dago: ASCII protokolora aldatzea besterik ez da geratzen eta dena funtzionatuko du.... Hala ere, kasu honetan, erantzunak bilatzeko ohitura php.net-en dokumentazioa txantxa anker bat egin zuen. Ez duzu erantzun zuzena aurkituko hor..., noski, amaierara joan ezean, atalaren nondik norakoak "Erabiltzaileak lagundutako oharrak" egia izango da eta bidegabe bozkatu duen erantzuna.

Bai, aukeraren izen zuzena da memcached.sess_binary_protocol. Desgaitu egin behar da, eta ondoren saioak lanean hasiko dira. PHP-rekin mcrouter duen edukiontzia ontzian jartzea besterik ez da geratzen!

Ondorioa

Horrela, azpiegitura-aldaketak soilik eginda, zeregina konpontzea lortu dugu: memcached akatsen tolerantziaren arazoa konpondu da, cache biltegiratzearen fidagarritasuna areagotu da. Aplikaziorako abantail nabariez gain, horrek maniobrarako tartea ematen zuen plataforman lanean: osagai guztiek erreserba dutenean, administratzailearen bizitza asko errazten da. Bai, metodo honek bere eragozpenak ditu, "makulu" itxura izan dezake, baina dirua aurrezten badu, arazoa lurperatzen badu eta berririk sortzen ez badu - zergatik ez?

PS

Irakurri ere gure blogean:

Iturria: www.habr.com

Gehitu iruzkin berria