Sèvi ak mcrouter pou echèl memcached orizontal

Sèvi ak mcrouter pou echèl memcached orizontal

Devlope pwojè gwo chaj nan nenpòt lang mande pou yon apwòch espesyal ak itilizasyon zouti espesyal, men lè li rive aplikasyon nan PHP, sitiyasyon an ka vin tèlman agrave ke ou dwe devlope, pou egzanp, pwòp sèvè aplikasyon an. Nan nòt sa a nou pral pale sou doulè abitye ak depo sesyon distribye ak kachèt done nan memcached ak kijan nou rezoud pwoblèm sa yo nan yon pwojè "pawas".

Ewo okazyon an se yon aplikasyon PHP ki baze sou symfony 2.3 kad, ki pa ditou nan plan biznis yo mete ajou. Anplis depo sesyon byen estanda, pwojè sa a te fè tout itilizasyon politik "kache tout bagay". nan memcached: repons a demann nan baz done a ak sèvè API, drapo divès kalite, kadna pou senkronize ekzekisyon kòd ak plis ankò. Nan yon sitiyasyon konsa, yon pann nan memcached vin fatal nan operasyon an nan aplikasyon an. Anplis de sa, pèt kachèt mennen nan konsekans grav: DBMS a kòmanse pete nan kouti yo, sèvis API kòmanse entèdi demann, elatriye. Estabilize sitiyasyon an ka pran plizyè dizèn minit, epi pandan tan sa a sèvis la pral fò anpil dousman oswa konplètman pa disponib.

Nou te bezwen bay kapasite nan orizontal echèl aplikasyon an ak ti kras efò, i.e. ak chanjman minimòm nan kòd sous la ak tout fonksyonalite konsève. Fè kachèt la pa sèlman rezistan a echèk, men tou, eseye minimize pèt done ki soti nan li.

Ki sa ki mal ak memcached tèt li?

An jeneral, ekstansyon memcached pou PHP sipòte done distribye ak depo sesyon soti nan bwat la. Mekanis nan hachaj kle ki konsistan pèmèt ou mete done yo sou anpil sèvè, yon fason inik abòde chak kle espesifik nan yon sèvè espesifik nan gwoup la, ak zouti failover entegre asire gwo disponiblite nan sèvis la kachèt (men, malerezman, pa gen done).

Bagay yo yon ti kras pi bon ak depo sesyon: ou ka konfigirasyon memcached.sess_number_of_replicas, kòm yon rezilta ki done yo pral estoke sou plizyè serveurs nan yon fwa, ak nan evènman an nan yon echèk nan yon egzanp memcached, done yo pral transfere nan men lòt moun. Sepandan, si sèvè a tounen sou entènèt san done (tankou anjeneral k ap pase apre yon rekòmanse), kèk nan kle yo pral redistribiye an favè li. An reyalite sa a pral vle di pèt done sesyon yo, paske pa gen okenn fason pou "ale" nan yon lòt kopi nan ka ta gen yon miss.

Zouti bibliyotèk estanda yo vize sitou orizontal Eskalad: yo pèmèt ou ogmante kachèt la nan gwosè jigantèsk epi bay aksè a li nan kòd ki anime sou diferan sèvè. Sepandan, nan sitiyasyon nou an, volim done ki estoke pa depase plizyè jigokte, ak pèfòmans youn oubyen de nœuds ase. An konsekans, sèlman zouti estanda itil yo ta ka asire disponiblite a nan memcached pandan w ap kenbe omwen yon egzanp kachèt nan kondisyon travay. Sepandan, li pa t 'posib pwofite menm opòtinite sa a... Isit la li vo sonje antikite nan fondasyon an te itilize nan pwojè a, ki se poukisa li te enposib jwenn aplikasyon an nan travay ak yon pisin nan serveurs. Se pou nou tou pa bliye sou pèt la nan done sesyon: je kliyan an twitched soti nan dekonekte masiv itilizatè yo.

Idealman li te obligatwa replikasyon dosye nan memcached ak kontourne kopi an ka ta gen yon erè oswa erè. Te ede nou aplike estrateji sa a mcrouter.

mcrouter

Sa a se yon routeur memcached devlope pa Facebook pou rezoud pwoblèm li yo. Li sipòte pwotokòl tèks memcached, ki pèmèt echèl enstalasyon memcached nan pwopòsyon fou. Ou ka jwenn yon deskripsyon detaye sou mcrouter nan anons sa a. Pami lòt bagay lajè fonksyonalite li ka fè sa nou bezwen:

  • kopi dosye;
  • fè repli nan lòt serveurs nan gwoup la si yon erè rive.

Al travay!

konfigirasyon mcrouter

Mwen pral tou dwat nan konfigirasyon an:

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

Poukisa twa pisin? Poukisa sèvè yo repete? Ann chèche konnen ki jan li fonksyone.

  • Nan konfigirasyon sa a, mcrouter chwazi chemen kote yo pral voye demann lan dapre lòd demann lan. Nèg la di l sa OperationSelectorRoute.
  • GET demann ale nan moun k ap okipe a RandomRouteki chwazi owaza yon pisin oswa yon wout nan mitan objè etalaj children. Chak eleman nan etalaj sa a se nan vire yon moun kap okipe MissFailoverRoute, ki pral ale nan chak sèvè nan pisin lan jiskaske li resevwa yon repons ak done, ki pral retounen nan kliyan an.
  • Si nou itilize sèlman MissFailoverRoute ak yon pisin nan twa serveurs, Lè sa a, tout demann yo ta vini an premye nan premye egzanp memcached la, ak rès la ta resevwa demann sou yon baz rezidyèl lè pa gen okenn done. Yon apwòch konsa ta mennen a chaj twòp sou premye sèvè nan lis la, Se konsa, li te deside jenere twa pisin ak adrès nan sekans diferan epi chwazi yo owaza.
  • Tout lòt demann (epi sa a se yon dosye) yo trete lè l sèvi avèk AllMajorityRoute. Moun k ap okipe sa a voye demann bay tout sèvè ki nan pisin lan epi li tann repons omwen N/2 + 1 ladan yo. Soti nan itilize AllSyncRoute pou operasyon ekri yo te dwe abandone, depi metòd sa a mande pou yon repons pozitif soti nan nan tout sèvè nan gwoup la - otreman li pral retounen SERVER_ERROR. Malgre ke mcrouter pral ajoute done yo nan kachèt ki disponib, fonksyon PHP apèl la pral retounen yon erè epi yo pral jenere avi. AllMajorityRoute se pa tèlman strik epi li pèmèt jiska mwatye nan inite yo dwe retire nan sèvis san pwoblèm ki dekri pi wo a.

Dezavantaj prensipal la Konplo sa a se ke si reyèlman pa gen okenn done nan kachèt la, Lè sa a, pou chak demann nan men kliyan an N demann memcached pral aktyèlman egzekite - a tout sèvè nan pisin lan. Nou ka redwi kantite sèvè nan pisin, pou egzanp, a de: sakrifye fyab depo, nou jwennоpi gwo vitès ak mwens chaj soti nan demann nan kle ki manke.

NB: Ou ka jwenn tou lyen itil pou aprann mcrouter dokiman sou wiki и pwoblèm pwojè yo (ki gen ladan yo fèmen), ki reprezante yon depo antye nan konfigirasyon divès kalite.

Bati ak kouri mcrouter

Aplikasyon nou an (ak memcached tèt li) kouri nan Kubernetes - kòmsadwa, mcrouter tou sitiye la. Pou asanble veso nou itilize werf, konfigirasyon an pou ki pral sanble sa a:

NB: Lis yo bay nan atik la yo pibliye nan depo a 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)

... epi trase li Tablo Helm. Bagay la enteresan an se ke gen sèlman yon dèlko konfigirasyon ki baze sou kantite kopi (si yon moun gen yon opsyon ki pi lakonik ak elegant, pataje li nan kòmantè yo):

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

Nou woule li nan anviwònman tès la epi tcheke:

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

Chèche tèks erè a pa bay okenn rezilta, men rechèch la "mcrouter php"Nan forefront an te pi ansyen pwoblèm ki pako rezoud nan pwojè a - mank de sipò memcached pwotokòl binè.

NB: Pwotokòl ASCII nan memcached pi dousman pase yon sèl binè, ak mwayen estanda nan hachaj kle konsistan sèlman travay ak pwotokòl la binè. Men, sa a pa kreye pwoblèm pou yon ka espesifik.

Trick la se nan sak la: tout sa ou dwe fè se chanje nan pwotokòl la ASCII ak tout bagay pral travay.... Sepandan, nan ka sa a, abitid pou chèche repons nan dokiman sou php.net te jwe yon blag mechan. Ou p ap jwenn repons ki kòrèk la... sof si, nan kou, ou woule nan fen a, kote nan seksyon an. "Itilizatè kontribye nòt" pral fidèl ak repons enjisteman enjis.

Wi, non opsyon ki kòrèk la se memcached.sess_binary_protocol. Li dwe enfim, apre sa sesyon yo ap kòmanse travay. Tout sa ki rete se mete veso a ak mcrouter nan yon gous ak PHP!

Konklizyon

Se konsa, ak jis chanjman enfrastriktirèl nou te kapab rezoud pwoblèm nan: pwoblèm nan ak tolerans fay memcached yo te rezoud, ak fyab nan depo kachèt yo te ogmante. Anplis de avantaj evidan pou aplikasyon an, sa a te bay plas pou manevwe lè w ap travay sou platfòm la: lè tout eleman gen yon rezèv, lavi administratè a anpil senplifye. Wi, metòd sa a tou gen dezavantaj li yo, li ka sanble ak yon "beki", men si li ekonomize lajan, antere pwoblèm nan epi li pa lakòz nouvo - poukisa pa?

PS

Li tou sou blog nou an:

Sous: www.habr.com

Add nouvo kòmantè