Benotzen mcrouter zu Skala memcached horizontal

Benotzen mcrouter zu Skala memcached horizontal

Héichbelaaschtungsprojeten an all Sprooch z'entwéckelen erfuerdert eng speziell Approche an d'Benotzung vu speziellen Tools, awer wann et ëm Applikatiounen an PHP kënnt, kann d'Situatioun esou verschlechtert ginn datt Dir z.B. eegene Applikatiounsserver. An dëser Notiz schwätze mir iwwer de vertraute Péng mat verdeelt Sessiounsspeicherung an Datencaching am memcached a wéi mir dës Probleemer an engem "Ward" Projet geléist hunn.

Den Held vun der Geleeënheet ass eng PHP Applikatioun baséiert op dem Symfony 2.3 Kader, deen guer net an de Geschäftspläng abegraff ass fir ze aktualiséieren. Zousätzlech zu zimmlech Standard Sessiounspäicherung huet dëse Projet voll genotzt "Alles cachen" Politik an memcached: Äntwerten op Ufroen op d'Datebank an API Serveren, verschidde Fändelen, Spären fir Synchroniséierung Code Ausféierung a vill méi. An esou enger Situatioun gëtt en Decompte vu memcached fatal fir d'Operatioun vun der Applikatioun. Zousätzlech féiert de Cache-Verloscht zu eeschte Konsequenzen: d'DBMS fänkt un d'Nähten ze platzen, API-Servicer fänken un Ufroen ze verbidden, asw. D'Stabiliséierung vun der Situatioun kann Zénger vu Minutten daueren, a während dëser Zäit wäert de Service schrecklech lues oder komplett net verfügbar sinn.

Mir hu misse bidden d'Kapazitéit fir d'Applikatioun horizontal mat wéineg Effort ze skaléieren, d.h. mat minimale Ännerunge vum Quellcode a voller Funktionalitéit erhale bleiwen. Maacht de Cache net nëmme resistent géint Feeler, awer probéiert och Datenverloscht dovunner ze minimiséieren.

Wat ass falsch mam memcached selwer?

Allgemeng ënnerstëtzt d'memcached Extensioun fir PHP verdeelt Daten a Sessiounspäicherung aus der Këscht. De Mechanismus fir konsequent Schlësselhashing erlaabt Iech Daten gläichméisseg op ville Serveren ze verdeelen, andeems Dir all spezifesche Schlëssel un e spezifesche Server aus der Grupp adresséiert, an agebaute Failover-Tools garantéieren eng héich Disponibilitéit vum Caching-Service (awer, leider, keng Donnéeën).

D'Saachen sinn e bësse besser mat Sessiounslagerung: Dir kënnt konfiguréieren memcached.sess_number_of_replicas, als Resultat vun deem d'Donnéeën op e puer Serveren gläichzäiteg gespäichert ginn, an am Fall vun engem Echec vun enger memcached Instanz, ginn d'Donnéeë vun aneren transferéiert. Wann de Server awer ouni Donnéeën online kënnt (wéi normalerweis geschitt no engem Neistart), ginn e puer vun de Schlësselen zu senger Gonschten ëmverdeelt. Tatsächlech wäert dat bedeiten Verloscht vun Sëtzungsdaten, well et ass kee Wee fir an eng aner Replica ze "goen" am Fall vun enger Miss.

Standard Bibliothéik Tools sinn haaptsächlech op horizontal Skaléieren: Si erlaben Iech de Cache op gigantesch Gréissten z'erhéijen an Zougang dozou ze bidden vum Code deen op verschiddene Serveren gehost gëtt. Wéi och ëmmer, an eiser Situatioun ass de Volume vun de gespäicherten Donnéeën net méi wéi e puer Gigabyte, an d'Performance vun engem oder zwee Wirbelen ass ganz genuch. Deementspriechend kënnen déi eenzeg nëtzlech Standardinstrumenter sinn d'Disponibilitéit vu memcached ze garantéieren wärend op d'mannst eng Cache-Instanz an engem funktionnéierenden Zoustand behalen. Et war awer net méiglech, och vun dëser Geleeënheet ze profitéieren ... Hei ass et derwäert ze erënneren un d'Antikitéit vum Kader, deen am Projet benotzt gouf, dofir war et onméiglech fir d'Applikatioun mat engem Pool vu Serveren ze schaffen. Loosst eis och net iwwer de Verloscht vun Sessiounsdaten vergiessen: d'Ae vum Client hu sech aus dem massive Logout vun de Benotzer gedréckt.

Ideal war et néideg Replikatioun vun records an memcached a Contournement Replika am Fall vun engem Feeler oder Feeler. Huet eis gehollef dës Strategie ëmzesetzen mcrouter.

mcrouter

Dëst ass e memcached Router entwéckelt vu Facebook fir seng Probleemer ze léisen. Et ënnerstëtzt de memcached Text Protokoll, wat erlaabt Skala memcached Installatiounen zu verréckten Proportiounen. Eng detailléiert Beschreiwung vum Mcrouter fannt Dir an dëser Ukënnegung. Ënnert anerem breet Funktionalitéit et kann maachen wat mir brauchen:

  • replizéieren Rekord;
  • Fallback op aner Serveren am Grupp wann e Feeler geschitt.

Kommt op de Geschäft!

mcrouter Konfiguratioun

Ech ginn direkt an d'Configuratioun:

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

Firwat dräi Pools? Firwat ginn Serveren widderholl? Loosst eis erausfannen wéi et funktionnéiert.

  • An dëser Konfiguratioun wielt mcrouter de Wee op deen d'Ufro geschéckt gëtt baséiert op dem Ufro Kommando. De Guy seet him dëst OperationSelectorRoute.
  • GET Ufroe ginn un den Handler RandomRoutedéi zoufälleg e Pool oder Streck tëscht Arrayobjekter auswielt children. All Element vun dëser Array ass am Tour en Handler MissFailoverRoute, deen duerch all Server am Pool geet bis en eng Äntwert mat Daten kritt, déi dem Client zréckginn.
  • Wa mir exklusiv benotzt MissFailoverRoute mat engem Pool vun dräi Serveren, dann all Ufroe kommen éischt un déi éischt memcached Instanz, an de Rescht géif Demanden op engem Reschtoffall kréien wann et keng Donnéeën ass. Esou eng Approche géif féieren exzessiv Laascht op den éischte Server an der Lëscht, Also gouf decidéiert dräi Poole mat Adressen a verschiddene Sequenzen ze generéieren an se zoufälleg auswielen.
  • All aner Ufroen (an dëst ass e Rekord) gi veraarbecht mat AllMajorityRoute. Dëse Handler schéckt Ufroen un all Server am Pool a waart op Äntwerte vun op d'mannst N / 2 + 1 vun hinnen. Vum Gebrauch AllSyncRoute fir schreiwen Operatiounen huet misse opginn, well dës Method eng positiv Äntwert verlaangt vun всех Serveren am Grupp - soss gëtt et zréck SERVER_ERROR. Och wann mcrouter d'Donnéeën op verfügbare Cache addéiere wäert, ass d'Uruff PHP Funktioun wäert e Feeler zréckginn a wäert Notiz generéieren. AllMajorityRoute ass net sou strikt an erlaabt bis zu der Halschent vun den Unitéiten ouni d'Problemer uewen beschriwwen aus dem Déngscht geholl ginn.

Main Nodeel Dëst Schema ass datt wann et wierklech keng Daten am Cache ass, da fir all Ufro vum Client N Ufroe fir memcached tatsächlech ausgefouert ginn - fir un all Serveren am Pool. Mir kënnen d'Zuel vun de Serveren an de Poole reduzéieren, zum Beispill, op zwee: Affer vun der Späicherverlässegkeet, mir kréienоméi héich Geschwindegkeet a manner Belaaschtung vun Ufroen op fehlend Schlësselen.

NB: Dir kënnt och nëtzlech Linken fannen fir mcrouter ze léieren Dokumentatioun op Wiki и Projet Problemer (och zougemaach), representéiert e ganze Lagerhaus vu verschiddene Konfiguratiounen.

Bauen a lafen Mcrouter

Eis Applikatioun (a selwer memcached) leeft a Kubernetes - deementspriechend ass mcrouter och do. Fir Container Assemblée mir benotzen werf, d'Konfiguratioun fir déi esou ausgesäit:

NB: D'Lëschten, déi am Artikel ginn, ginn am Repository publizéiert 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)

... a skizzéiert et Helm Chart. Déi interessant Saach ass datt et nëmmen e Konfiguratiounsgenerator baséiert op der Unzuel vun de Repliken (wann iergendeen eng méi lakonesch an elegant Optioun huet, deelt se an de Kommentarer):

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

Mir rullen et an d'Testëmfeld eraus a kontrolléieren:

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

Sich no den Text vum Feeler huet keng Resultater ginn, awer fir d'Ufro "mcrouter php"Am Virdergrond war den eelste ongeléiste Problem vum Projet - Mangel un Ënnerstëtzung memcached binäre Protokoll.

NB: Den ASCII Protokoll am memcached ass méi lues wéi de binäre, a Standardmëttel fir konsequent Schlësselhashing funktionnéieren nëmme mam binäre Protokoll. Mä dëst schaaft keng Problemer fir e spezifesche Fall.

Den Trick ass an der Täsch: alles wat Dir maache musst ass op den ASCII Protokoll ze wiesselen an alles funktionnéiert ... Wéi och ëmmer, an dësem Fall ass d'Gewunnecht no Äntwerten ze sichen Dokumentatioun op php.net e grausame Witz gespillt. Dir fannt do net déi richteg Äntwert ... ausser Dir scrollt natierlech bis zum Schluss, wou an der Rubrik "Benotzer bäigedroen Notizen" wäert trei an ongerecht ofgestëmmt Äntwert.

Jo, de richtege Optiounsnumm ass memcached.sess_binary_protocol. Et muss behënnert ginn, duerno fänken d'Sessiounen un ze schaffen. Alles wat bleift ass de Container mat mcrouter an e Pod mat PHP ze setzen!

Konklusioun

Also, mat just infrastrukturellen Ännerungen konnten mir de Problem léisen: d'Thema mat memcached Feeler Toleranz ass geléist ginn, an d'Zouverlässegkeet vun der Cache Späichere gouf erhéicht. Zousätzlech zu den offensichtleche Virdeeler fir d'Applikatioun, huet dëst Spillraum beim Aarbecht op der Plattform ginn: wann all Komponenten eng Reserve hunn, ass d'Liewen vum Administrateur staark vereinfacht. Jo, dës Method huet och seng Nodeeler, et kann ausgesinn wéi eng "Krëpp", awer wann et Sue spuert, de Problem begruewen an net nei verursaacht - firwat net?

PS

Liest och op eisem Blog:

Source: will.com

Setzt e Commentaire