
Lai izstrÄdÄtu augstas slodzes projektus jebkurÄ valodÄ, ir nepiecieÅ”ama Ä«paÅ”a pieeja un Ä«paÅ”u rÄ«ku izmantoÅ”ana, taÄu, runÄjot par lietojumprogrammÄm PHP, situÄcija var kļūt tik saasinÄta, ka jums ir jÄizstrÄdÄ, piemÄram, . Å ajÄ piezÄ«mÄ mÄs runÄsim par pazÄ«stamajÄm grÅ«tÄ«bÄm saistÄ«bÄ ar sadalÄ«to sesiju krÄtuvi un datu keÅ”atmiÅu memcached programmÄ un to, kÄ mÄs Ŕīs problÄmas atrisinÄjÄm vienÄ āpalÄtasā projektÄ.
Notikuma varonis ir PHP lietojumprogramma, kas balstÄ«ta uz symfony 2.3 ietvaru, kas nemaz nav iekļauta atjauninÄmajos biznesa plÄnos. Papildus diezgan standarta sesiju krÄtuvei Å”is projekts pilnÄ«bÄ izmantoja "visa saglabÄÅ”ana keÅ”atmiÅÄ". in memcached: atbildes uz pieprasÄ«jumiem datu bÄzei un API serveriem, dažÄdi karodziÅi, slÄdzenes koda izpildes sinhronizÄÅ”anai un daudz kas cits. Å ÄdÄ situÄcijÄ memcached bojÄjums kļūst liktenÄ«gs lietojumprogrammas darbÄ«bai. TurklÄt keÅ”atmiÅas zudums rada nopietnas sekas: DBVS sÄk plÄ«st pie Å”uvÄm, API pakalpojumi sÄk aizliegt pieprasÄ«jumus utt. SituÄcijas stabilizÄÅ”ana var aizÅemt desmitiem minūŔu, un Å”ajÄ laikÄ pakalpojums bÅ«s Å”ausmÄ«gi lÄns vai pilnÄ«bÄ nepieejams.
Mums vajadzÄja nodroÅ”inÄt spÄja horizontÄli mÄrogot lietojumprogrammu ar nelielu piepÅ«li, t.i. ar minimÄlÄm izmaiÅÄm avota kodÄ un saglabÄtu visu funkcionalitÄti. Padariet keÅ”atmiÅu ne tikai izturÄ«gu pret kļūmÄm, bet arÄ« mÄÄ£iniet samazinÄt datu zudumus no tÄs.
Kas vainas paŔam memcached?
KopumÄ PHP atmiÅai saglabÄtais paplaÅ”inÄjums atbalsta izkliedÄtu datu un sesiju krÄtuvi. Konsekventas atslÄgu jaukÅ”anas mehÄnisms ļauj vienmÄrÄ«gi izvietot datus daudzos serveros, unikÄli adresÄjot katru konkrÄto atslÄgu konkrÄtam serverim no grupas, un iebÅ«vÄtie kļūmjpÄrlÄces rÄ«ki nodroÅ”ina augstu keÅ”atmiÅas pakalpojuma pieejamÄ«bu (bet diemžÄl nav datu).
Lietas ir nedaudz labÄkas ar sesiju krÄtuvi: varat konfigurÄt memcached.sess_number_of_replicas, kÄ rezultÄtÄ dati tiks glabÄti vairÄkos serveros vienlaikus, un vienas atmiÅÄ saglabÄtas instances atteices gadÄ«jumÄ dati tiks pÄrsÅ«tÄ«ti no citiem. TomÄr, ja serveris atgriežas tieÅ”saistÄ bez datiem (kÄ tas parasti notiek pÄc restartÄÅ”anas), dažas atslÄgas tiks pÄrdalÄ«tas tÄ labÄ. PatiesÄ«bÄ tas nozÄ«mÄs sesijas datu zudums, jo garÄm palaiÅ”anas gadÄ«jumÄ nav iespÄjas āietā uz citu repliku.
Standarta bibliotÄkas rÄ«ki galvenokÄrt ir paredzÄti horizontÄli mÄrogoÅ”ana: tie ļauj palielinÄt keÅ”atmiÅu lÄ«dz milzÄ«giem izmÄriem un nodroÅ”ina piekļuvi tai no koda, kas mitinÄts dažÄdos serveros. TaÄu mÅ«su situÄcijÄ saglabÄto datu apjoms nepÄrsniedz vairÄkus gigabaitus, un viena vai divu mezglu veiktspÄja ir pilnÄ«gi pietiekama. AttiecÄ«gi vienÄ«gie noderÄ«gie standarta rÄ«ki varÄtu bÅ«t memcached pieejamÄ«bas nodroÅ”inÄÅ”ana, vienlaikus uzturot vismaz vienu keÅ”atmiÅas gadÄ«jumu darba stÄvoklÄ«. TaÄu izmantot pat Å”o iespÄju neizdevÄs... Te der atgÄdinÄt projektÄ izmantotÄ karkasa senatni, kÄdÄļ nebija iespÄjams dabÅ«t aplikÄciju darbam ar serveru pÅ«lu. NeaizmirsÄ«sim arÄ« par sesijas datu zudumu: klienta acis sarÄvÄs no masveida lietotÄju atteikÅ”anÄs.
IdeÄlÄ gadÄ«jumÄ tas bija vajadzÄ«gs Ierakstu replikÄcija atmiÅÄ saglabÄtajÄs un apietajÄs replikÄs kļūdas vai kļūdas gadÄ«jumÄ. PalÄ«dzÄja mums Ä«stenot Å”o stratÄÄ£iju .
mcrouter
Å is ir keÅ”atmiÅÄ saglabÄts marÅ”rutÄtÄjs, ko Facebook izstrÄdÄjis, lai atrisinÄtu savas problÄmas. Tas atbalsta memcached teksta protokolu, kas ļauj mÄroga memcached instalÄcijas neprÄtÄ«gÄs proporcijÄs. DetalizÄtu mcrouter aprakstu var atrast . Cita starpÄ tas var darÄ«t to, kas mums nepiecieÅ”ams:
- replicÄt ierakstu;
- Ja rodas kļūda, atgriezieties pie citiem grupas serveriem.
SÄciet Ä·erties pie lietas!
mcrouter konfigurÄcija
Es pÄrieÅ”u tieÅ”i uz konfigurÄciju:
{
"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"
]
}
}
}
}KÄpÄc trÄ«s baseini? KÄpÄc serveri tiek atkÄrtoti? IzdomÄsim, kÄ tas darbojas.
- Å ajÄ konfigurÄcijÄ mcrouter atlasa ceļu, uz kuru tiks nosÅ«tÄ«ts pieprasÄ«jums, pamatojoties uz pieprasÄ«juma komandu. Puisis viÅam to stÄsta
OperationSelectorRoute. - GET pieprasÄ«jumi tiek nosÅ«tÄ«ti apstrÄdÄtÄjam
RandomRoutekas nejauÅ”i izvÄlas kopu vai marÅ”rutu starp masÄ«va objektiemchildren. Katrs Ŕī masÄ«va elements savukÄrt ir apstrÄdÄtÄjsMissFailoverRoute, kas iet caur katru pÅ«la serveri, lÄ«dz saÅems atbildi ar datiem, kas tiks atgriezti klientam. - Ja mÄs izmantotu tikai
MissFailoverRoutear trÄ«s serveru kopu, tad visi pieprasÄ«jumi vispirms tiktu saÅemti pirmajai keÅ”atmiÅÄ saglabÄtajai instancei, bet pÄrÄjie saÅemtu pieprasÄ«jumus pÄc atlikuma, kad nav datu. Å Äda pieeja novestu pie pÄrmÄrÄ«ga slodze sarakstÄ pirmajÄ serverÄ«, tÄpÄc tika nolemts Ä£enerÄt trÄ«s pÅ«lus ar adresÄm dažÄdÄs secÄ«bÄs un atlasÄ«t tos nejauÅ”i. - Visi pÄrÄjie pieprasÄ«jumi (un tas ir ieraksts) tiek apstrÄdÄti, izmantojot
AllMajorityRoute. Å is apdarinÄtÄjs nosÅ«ta pieprasÄ«jumus visiem pÅ«la serveriem un gaida atbildes no vismaz N/2 + 1 no tiem. No lietoÅ”anasAllSyncRouterakstīŔanas operÄcijÄm bija jÄatsakÄs, jo Ŕī metode prasa pozitÄ«vu atbildi no viss serveriem grupÄ - pretÄjÄ gadÄ«jumÄ tas atgriezÄ«siesSERVER_ERROR. Lai gan mcrouter pievienos datus pieejamajÄm keÅ”atmiÅÄm, izsaucoÅ”Ä PHP funkcija atgriezÄ«s kļūdu un radÄ«s paziÅojumu.AllMajorityRoutenav tik stingra un ļauj lÄ«dz pat pusei vienÄ«bu izÅemt no ekspluatÄcijas bez iepriekÅ” aprakstÄ«tajÄm problÄmÄm.
Galvenais trÅ«kums Å Ä« shÄma ir tÄda, ka, ja keÅ”atmiÅÄ patieÅ”Äm nav datu, tad katram klienta pieprasÄ«jumam faktiski tiks izpildÄ«ti N pieprasÄ«jumi uz memcached - uz viss serveri baseinÄ. MÄs varam samazinÄt serveru skaitu baseinos, piemÄram, lÄ«dz diviem: upurÄjot krÄtuves uzticamÄ«bu, mÄs iegÅ«stamоlielÄks Ätrums un mazÄka slodze no pieprasÄ«jumiem lÄ«dz trÅ«kstoÅ”Äm atslÄgÄm.
NB: JÅ«s varat atrast arÄ« noderÄ«gas saites mcrouter apguvei Šø (ieskaitot slÄgtos), kas pÄrstÄv veselu dažÄdu konfigurÄciju noliktavu.
Mcrouter izveide un darbinÄÅ”ana
MÅ«su aplikÄcija (un pati memcached) darbojas Kubernetes - attiecÄ«gi tur atrodas arÄ« mcrouter. PriekÅ” konteineru montÄža mÄs izmantojam , kuras konfigurÄcija izskatÄ«sies Å”Ädi:
NB: RakstÄ sniegtie saraksti tiek publicÄti repozitorijÄ .
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' ]()
... un ieskicÄt to StÅ«res diagramma. Interesanti ir tas, ka ir tikai konfigurÄcijas Ä£enerators, kas balstÄ«ts uz kopiju skaitu (ja kÄdam ir kÄds lakoniskÄks un elegantÄks variants, padalieties komentÄros):
{{- $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 }}
}
}
}
}()
MÄs to izlaižam testa vidÄ un pÄrbaudÄm:
# 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 >Kļūdas teksta meklÄÅ”ana nedeva nekÄdus rezultÄtus, bet vaicÄjums ā"PriekÅ”galÄ bija vecÄkÄ neatrisinÄtÄ projekta problÄma - Memcached binÄrais protokols.
NB: Memcached ASCII protokols ir lÄnÄks nekÄ binÄrais, un standarta lÄ«dzekļi konsekventai atslÄgu jaukÅ”anai darbojas tikai ar binÄro protokolu. Bet tas nerada problÄmas konkrÄtam gadÄ«jumam.
Triks ir maisÄ: atliek tikai pÄrslÄgties uz ASCII protokolu un viss darbosies.... TomÄr Å”ajÄ gadÄ«jumÄ ieradums meklÄt atbildes izspÄlÄja nežÄlÄ«gu joku. Pareizo atbildi tur neatradÄ«si... ja vien, protams, neritinÄsi lÄ«dz beigÄm, kur sadaÄ¼Ä "LietotÄju iesniegtÄs piezÄ«mes" bÅ«s uzticÄ«gs un .
JÄ, pareizais opcijas nosaukums ir memcached.sess_binary_protocol. Tas ir jÄatspÄjo, pÄc tam sesijas sÄks darboties. Atliek tikai ievietot konteineru ar mcrouter podÄ ar PHP!
SecinÄjums
TÄdÄjÄdi tikai ar infrastruktÅ«ras izmaiÅÄm mÄs spÄjÄm atrisinÄt problÄmu: problÄma ar atmiÅÄ saglabÄto kļūdu toleranci ir atrisinÄta, un ir palielinÄta keÅ”atmiÅas glabÄÅ”anas uzticamÄ«ba. Papildus acÄ«mredzamajÄm lietojumprogrammas priekÅ”rocÄ«bÄm tas deva manevrÄÅ”anas iespÄju, strÄdÄjot pie platformas: ja visiem komponentiem ir rezerve, administratora dzÄ«ve ir ievÄrojami vienkÄrÅ”ota. JÄ, Å”ai metodei ir arÄ« savi trÅ«kumi, tÄ var izskatÄ«ties pÄc ākruÄ·aā, bet, ja tÄ ietaupa naudu, aprok problÄmu un nerada jaunas - kÄpÄc gan ne?
PS
Lasi arÄ« mÅ«su emuÄrÄ:
- "Prakse ar dapp" (kÄ piemÄru izmantojot symfony-demo): Šø ;
- Ā«'.
Avots: www.habr.com
