Mei help fan mcrouter te skaal memcached horizontaal

Mei help fan mcrouter te skaal memcached horizontaal

It ûntwikkeljen fan projekten mei hege lading yn elke taal fereasket in spesjale oanpak en it brûken fan spesjale ark, mar as it giet om applikaasjes yn PHP, kin de situaasje sa fergriemd wurde dat jo ûntwikkelje moatte, bygelyks, eigen applikaasje tsjinner. Yn dizze notysje sille wy prate oer de bekende pine mei ferspraat sesje opslach en gegevens caching yn memcached en hoe't wy oplost dizze problemen yn ien "ward" projekt.

De held fan 'e gelegenheid is in PHP-applikaasje basearre op it symfony 2.3-ramt, dat hielendal net opnommen is yn' e bedriuwsplannen om te aktualisearjen. Neist frij standert sesje-opslach, makke dit projekt folslein gebrûk fan "alles caching" belied yn memcached: antwurden op fersiken nei de databank en API tsjinners, ferskate flaggen, slûzen foar syngronisaasje koade útfiering en folle mear. Yn sa'n situaasje wurdt in ôfbraak fan memcached fataal foar de wurking fan 'e applikaasje. Derneist liedt cacheferlies ta serieuze gefolgen: de DBMS begjint yn 'e naden te barsten, API-tsjinsten begjinne fersiken te ferbieden, ensfh. It stabilisearjen fan de situaasje kin tsientallen minuten duorje, en yn dizze tiid sil de tsjinst ferskriklik stadich wêze as folslein net beskikber.

Wy moasten leverje de mooglikheid om de applikaasje horizontaal te skaaljen mei in bytsje muoite, d.w.s. mei minimale feroarings oan 'e boarnekoade en folsleine funksjonaliteit bewarre bleaun. Meitsje de cache net allinich resistint foar mislearrings, mar besykje ek gegevensferlies derút te minimalisearjen.

Wat is der mis mei memcached sels?

Yn 't algemien stipet de memcached-útwreiding foar PHP ferdielde gegevens en sesje-opslach út' e fak. It meganisme foar konsekwint kaai-hashing lit jo gegevens lykwichtich fersprieden oer in protte servers, unyk adressearje fan elke spesifike kaai nei in spesifike server fan 'e groep, en ynboude failover-ark soargje foar hege beskikberens fan' e cachingtsjinst (mar, spitigernôch, gjin gegevens).

Dingen binne in bytsje better mei sesje opslach: jo kinne konfigurearje memcached.sess_number_of_replicas, as gefolch wêrfan de gegevens wurde opslein op ferskate servers tagelyk, en yn it gefal fan in mislearring fan ien memcached eksimplaar, de gegevens wurde oerdroegen fan oaren. As de tsjinner lykwols wer online komt sûnder gegevens (lykas gewoanlik bart nei in trochstart), wurde guon fan 'e kaaien yn syn foardiel opnij ferdield. Yn feite sil dit betsjutte ferlies fan sesjegegevens, om't d'r gjin manier is om nei in oare replika te "gean" yn gefal fan in miss.

Standert biblioteek ark binne benammen rjochte op horizontaal skaalfergrutting: se tastean jo de cache te fergrutsjen nei gigantyske maten en jouwe tagong ta it fan koade hosted op ferskate tsjinners. Lykwols, yn ús situaasje, it folume fan bewarre gegevens net mear as ferskate gigabytes, en de prestaasjes fan ien of twa knopen is genôch. Dêrnjonken kinne de iennichste brûkbere standert ark wêze om de beskikberens fan memcached te garandearjen, wylst op syn minst ien cache-eksimplaar yn wurkjende steat behâldt. It wie lykwols net mooglik om sels dizze kâns te profitearjen ... Hjir is it wurdich om de âldheid fan it ramt te ûnthâlden dat yn it projekt brûkt waard, wêrtroch it ûnmooglik wie om de applikaasje te krijen om te wurkjen mei in pool fan servers. Litte wy ek it ferlies fan sesjegegevens net ferjitte: it each fan 'e klant trille fan' e massale útlogging fan brûkers.

Ideal wie it ferplicht replikaasje fan records yn memcached en bypasse replika's yn gefal fan in flater of flater. Help ús dizze strategy út te fieren mcrouter.

mcrouter

Dit is in memcached router ûntwikkele troch Facebook om har problemen op te lossen. It stipet it memcached tekstprotokol, wat it mooglik makket skaal memcached ynstallaasjes ta gekke proporsjes. In detaillearre beskriuwing fan mcrouter is te finen yn dizze oankundiging. Ûnder oare brede funksjonaliteit it kin dwaan wat wy nedich binne:

  • replicate record;
  • weromfal nei oare tsjinners yn 'e groep as in flater optreedt.

Gean del nei saken!

mcrouter konfiguraasje

Ik gean direkt nei de konfiguraasje:

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

Wêrom trije swimbaden? Wêrom wurde tsjinners werhelle? Litte wy útfine hoe't it wurket.

  • Yn dizze konfiguraasje selekteart mcrouter it paad wêrnei't it fersyk ferstjoerd wurdt op basis fan it fersykkommando. De keardel fertelt him dit OperationSelectorRoute.
  • GET-oanfragen geane nei de handler RandomRoutedy't willekeurich selektearret in pool of rûte ûnder array objekten children. Elk elemint fan dizze array is op syn beurt in handler MissFailoverRoute.
  • As wy allinnich brûkt MissFailoverRoute mei in pool fan trije tsjinners, dan soe alle fersiken komme earst nei de earste memcached eksimplaar, en de rest soe ûntfange fersiken op in oerbleaune basis as der gjin gegevens. Sa'n oanpak soe liede ta oerstallige lading op de earste tsjinner yn de list, Sa waard besletten om trije pools te generearjen mei adressen yn ferskate sekwinsjes en se willekeurich te selektearjen.
  • Alle oare oanfragen (en dit is in rekord) wurde ferwurke mei AllMajorityRoute. Dizze handler stjoert fersiken nei alle tsjinners yn it swimbad en wachtet op antwurden fan op syn minst N/2 + 1 fan harren. Fan gebrûk AllSyncRoute foar skriuwen operaasjes moasten wurde ferlitten, sûnt dizze metoade fereasket in positive reaksje fan всех tsjinners yn 'e groep - oars komt it werom SERVER_ERROR. Hoewol mcrouter de gegevens sil tafoegje oan beskikbere caches, de PHP-funksje oproppe sil in flater weromjaan en sil generearje notice. AllMajorityRoute is net sa strang en makket it mooglik om oant de helte fan 'e ienheden sûnder de hjirboppe beskreaune problemen út tsjinst te nimmen.

Main neidiel Dit skema is dat as d'r echt gjin gegevens yn 'e cache binne, dan foar elk fersyk fan 'e kliïnt N fersiken nei memcached wirklik wurde útfierd - om foar allegear tsjinners yn it swimbad. Wy kinne it oantal servers yn swimbaden ferminderje, bygelyks nei twa: it offerjen fan opslachbetrouberens, wy krijeоhegere snelheid en minder lading fan fersiken nei ûntbrekkende kaaien.

NB: Jo kinne ek nuttige keppelings fine foar it learen fan mcrouter dokumintaasje op wiki и projekt saken (ynklusyf sluten), dy't in hiele pakhûs fan ferskate konfiguraasjes fertsjintwurdigje.

Bouwe en útfiere mcrouter

Us applikaasje (en memcached sels) rint yn Kubernetes - dus is mcrouter dêr ek leit. Foar container gearkomste Wy brûke werf, de konfiguraasje wêrfoar der sa útsjen sil:

NB: De listings jûn yn it artikel wurde publisearre yn de repository 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)

... en sketse it út Helm chart. It nijsgjirrige is dat d'r allinich in konfiguraasjegenerator is basearre op it oantal replika's (as immen in mear lakonike en elegante opsje hat, diel it dan yn 'e kommentaren):

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

Wy rôlje it út yn 'e testomjouwing en kontrolearje:

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

It sykjen nei de tekst fan 'e flater joech gjin resultaten, mar foar de query "mcrouter php"Yn 'e foargrûn wie it âldste ûnoploste probleem fan it projekt - gebrek oan stipe memcached binêre protokol.

NB: It ASCII-protokol yn memcached is stadiger as it binêre, en standertmiddels fan konsekwint kaai-hashing wurkje allinich mei it binêre protokol. Mar dit makket gjin problemen foar in spesifyk gefal.

De trúk is yn 'e tas: alles wat jo hoege te dwaan is oerskeakelje nei it ASCII-protokol en alles sil wurkje .... Lykwols, yn dit gefal, de gewoante fan it sykjen nei antwurden yn dokumintaasje op php.net spile in wrede grap. Jo sille net fine it goede antwurd dêr ... útsein as jo, fansels, scroll nei it ein, wêr yn 'e seksje "Notysjes bydroegen troch brûker" sil wêze trou en unfair downvoted antwurd.

Ja, de juste opsjenamme is memcached.sess_binary_protocol. It moat útskeakele wurde, wêrnei't de sesjes begjinne te wurkjen. Alles wat oerbliuwt is de kontener mei mcrouter yn in pod te setten mei PHP!

konklúzje

Sa koene wy ​​​​mei gewoan ynfrastruktuerferoarings it probleem oplosse: it probleem mei memcached fouttolerânsje is oplost, en de betrouberens fan cache-opslach is ferhege. Neist de foar de hân lizzende foardielen foar de applikaasje joech dit romte foar manoeuvre by it wurkjen op it platfoarm: as alle komponinten in reserve hawwe, wurdt it libben fan 'e behearder sterk ferienfâldige. Ja, dizze metoade hat ek syn neidielen, it kin lykje as in "kruk", mar as it jild besparret, it probleem begravet en gjin nije feroarsaket - wêrom net?

PS

Lês ek op ús blog:

Boarne: www.habr.com

Add a comment