Ямар ч хэл дээр өндөр ачаалалтай төслүүдийг боловсруулах нь тусгай арга барил, тусгай хэрэгсэл ашиглахыг шаарддаг боловч PHP-ийн хэрэглээний талаар ярихад нөхцөл байдал улам хүндэрч болзошгүй тул та жишээ нь:
Энэ үйл явдлын баатар бол шинэчлэх бизнес төлөвлөгөөнд огт ороогүй symfony 2.3 framework дээр суурилсан PHP програм юм. Нэлээд стандарт сесс хадгалахаас гадна энэ төсөл бүрэн ашигласан "Бүхнийг кэшлэх" бодлого memcached-д: өгөгдлийн сан болон API серверүүдийн хүсэлтэд хариу өгөх, янз бүрийн тугнууд, кодын гүйцэтгэлийг синхрончлох түгжээ гэх мэт. Ийм нөхцөлд memcach-ийн эвдрэл нь програмын үйл ажиллагаанд сөргөөр нөлөөлнө. Нэмж дурдахад кэш алдагдах нь ноцтой үр дагаварт хүргэдэг: DBMS нь давхцаж эхэлдэг, API үйлчилгээнүүд хүсэлтийг хориглож эхэлдэг гэх мэт. Нөхцөл байдлыг тогтворжуулахад хэдэн арван минут шаардагдах бөгөөд энэ хугацаанд үйлчилгээ маш удаан эсвэл бүрэн боломжгүй болно.
Бид хангах шаардлагатай байсан бага хүчин чармайлтаар програмыг хэвтээ байдлаар томруулах чадвар, өөрөөр хэлбэл эх кодын хамгийн бага өөрчлөлттэй, бүрэн ажиллагаа нь хадгалагдсан. Кэшийг зөвхөн бүтэлгүйтэлд тэсвэртэй болгоод зогсохгүй өгөгдлийн алдагдлыг багасгахыг хичээ.
Memcached өөрөө юу нь болохгүй байна вэ?
Ерөнхийдөө PHP-д зориулсан memcached өргөтгөл нь тархсан өгөгдөл болон сессийн хадгалалтыг дэмждэг. Түлхүүрийг тогтмол хэшлэх механизм нь олон сервер дээр өгөгдлийг жигд байршуулах боломжийг олгодог бөгөөд тодорхой түлхүүр бүрийг бүлгийн тодорхой серверт өвөрмөц байдлаар хаяглаж, суулгасан бүтэлгүйтлийн хэрэгслүүд нь кэшийн үйлчилгээний өндөр хүртээмжийг баталгаажуулдаг (гэхдээ харамсалтай нь өгөгдөл алга).
Сеанс хадгалахад бүх зүйл арай дээрдсэн: та тохируулж болно memcached.sess_number_of_replicas
, үүний үр дүнд өгөгдөл нэг дор хэд хэдэн сервер дээр хадгалагдах бөгөөд нэг memcach-д алдаа гарсан тохиолдолд өгөгдлийг бусдаас шилжүүлэх болно. Гэсэн хэдий ч, хэрэв сервер өгөгдөлгүйгээр онлайнаар буцаж ирвэл (дахин ачаалсны дараа ихэвчлэн тохиолддог) зарим түлхүүрүүд түүний талд дахин хуваарилагдах болно. Үнэндээ энэ нь гэсэн үг юм сессийн өгөгдөл алдагдах, алдсан тохиолдолд өөр хуулбар руу "явах" арга байхгүй тул.
Номын сангийн стандарт хэрэгслүүд нь голчлон чиглэгддэг хэвтээ масштаблах: тэдгээр нь кэшийг асар том хэмжээтэй болгож, өөр өөр сервер дээр байрлуулсан кодоос нэвтрэх боломжийг олгодог. Гэсэн хэдий ч манай нөхцөлд хадгалагдсан өгөгдлийн хэмжээ хэд хэдэн гигабайтаас хэтрэхгүй бөгөөд нэг эсвэл хоёр зангилааны гүйцэтгэл хангалттай байдаг. Үүний дагуу хамгийн хэрэгтэй стандарт хэрэгсэл бол ядаж нэг кэшийн жишээг ажиллах нөхцөлд байлгахын зэрэгцээ memcach-ийн бэлэн байдлыг хангах явдал юм. Гэсэн хэдий ч энэ боломжийг ашиглах боломжгүй байсан ... Энд төсөлд ашиглагдаж байсан фрэймворкийн эртний үеийг эргэн санах нь зүйтэй бөгөөд иймээс програмыг серверийн сантай ажиллах боломжгүй байсан. Сеансын өгөгдөл алдагдах тухай мартаж болохгүй: хэрэглэгчид их хэмжээний гарснаас болж үйлчлүүлэгчийн нүд анивчсан.
Хамгийн тохиромжтой нь шаардлагатай байсан memcach-д хадгалагдсан бичлэгүүдийг хуулбарлах болон тойрч гарах хуулбарууд алдаа эсвэл алдаа гарсан тохиолдолд. Энэ стратегийг хэрэгжүүлэхэд бидэнд тусалсан
mcrouter
Энэ бол Facebook-ийн асуудлаа шийдвэрлэх зорилгоор боловсруулсан memcach-тэй чиглүүлэгч юм. Энэ нь memcached текст протоколыг дэмждэг бөгөөд үүнийг зөвшөөрдөг memcach-д суулгасан суулгацуудыг масштаблах галзуу харьцаа руу. Mcrouter-ийн дэлгэрэнгүй тайлбарыг эндээс олж болно
- бичлэгийг хуулбарлах;
- алдаа гарвал бүлгийн бусад серверүүд рүү буцах.
Бизнесийн хувьд!
mcrouter тохиргоо
Би тохиргоо руу шууд очно:
{
"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"
]
}
}
}
}
Яагаад гурван усан сан гэж? Яагаад серверүүд давтагддаг вэ? Энэ нь хэрхэн ажилладагийг олж мэдье.
- Энэ тохиргоонд mcrouter нь хүсэлтийн команд дээр үндэслэн хүсэлтийг илгээх замыг сонгоно. Тэр залуу түүнд ингэж хэлдэг
OperationSelectorRoute
. - GET хүсэлтүүд зохицуулагч руу очно
RandomRoute
массив объектуудын дундах сан эсвэл маршрутыг санамсаргүй байдлаар сонгоноchildren
. Энэ массивын элемент бүр нь эргээд зохицуулагч юмMissFailoverRoute
, энэ нь сан дахь сервер бүрээр дамжуулан өгөгдөл бүхий хариу хүлээн авах хүртэл явагдах бөгөөд үүнийг үйлчлүүлэгчид буцааж өгөх болно. - Хэрэв бид зөвхөн ашигласан бол
MissFailoverRoute
Гурван серверийн сантай бол бүх хүсэлтүүд хамгийн түрүүнд анхны мемкачлагдсан инстантад ирэх ба бусад нь өгөгдөл байхгүй үед үлдэгдэл үндсэн дээр хүсэлтийг хүлээн авах болно. Ийм хандлага нь үүнд хүргэнэ жагсаалтын эхний сервер дээр хэт их ачаалал, тиймээс өөр өөр дарааллаар хаягтай гурван сан үүсгэж, санамсаргүй байдлаар сонгохоор шийдсэн. - Бусад бүх хүсэлтийг (мөн энэ нь бичлэг) ашиглан боловсруулдаг
AllMajorityRoute
. Энэ зохицуулагч нь сан дахь бүх серверүүд рүү хүсэлт илгээж, хамгийн багадаа N/2 + 1-ээс хариу хүлээж байна. АшиглахаасAllSyncRoute
Энэ арга нь эерэг хариу өгөхийг шаарддаг тул бичих үйлдлүүдийг орхих шаардлагатай болсон всех бүлгийн серверүүд - эс тэгвээс буцаж ирэх болноSERVER_ERROR
. Хэдийгээр mcrouter нь боломжтой кэшүүдэд өгөгдлийг нэмэх боловч дуудаж буй PHP функц алдаа буцаах болно мөн мэдэгдэл үүсгэх болно.AllMajorityRoute
нь тийм ч хатуу биш бөгөөд дээр дурдсан асуудалгүйгээр нэгжийн тал хүртэлх хэсгийг ашиглалтаас гаргах боломжийг олгодог.
Гол сул тал Энэ схем нь хэрэв кэшэд үнэхээр өгөгдөл байхгүй бол үйлчлүүлэгчээс ирсэн хүсэлт бүрийн хувьд N memcached хүсэлтийг гүйцэтгэх болно. бүх хүмүүст усан сан дахь серверүүд. Бид усан сан дахь серверийн тоог хоёр болгон бууруулж болно: хадгалах найдвартай байдлыг золиослон, бидоилүү өндөр хурдтай, дутуу түлхүүрүүдийн хүсэлтээс бага ачаалал.
NB: Мөн та mcrouter сурахад хэрэгтэй холбоосуудыг олж болно
mcrouter барьж, ажиллуулж байна
Манай програм (мөн өөрөө memcached) нь Kubernetes дээр ажилладаг - үүний дагуу mcrouter бас тэнд байрладаг. Учир нь савны угсралт Бидний хэрэглэдэг
NB: Нийтлэлд өгөгдсөн жагсаалтыг хадгалах газарт нийтэлсэн болно
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' ]
... мөн ноорог зур Жолооны диаграм. Сонирхолтой нь зөвхөн хуулбарын тоонд суурилсан тохиргоо үүсгэгч байдаг (хэрэв хэн нэгэнд илүү товч бөгөөд дэгжин сонголт байвал сэтгэгдэл дээр хуваалцаарай):
{{- $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 }}
}
}
}
}
Бид үүнийг туршилтын орчинд гаргаж, шалгана:
# 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 >
Алдааны текстийг хайхад ямар ч үр дүн гарсангүй, гэхдээ "
NB: Memcached дахь ASCII протокол нь хоёртын протоколоос удаан бөгөөд түлхүүрүүдийг тогтмол хэшлэх стандарт хэрэгсэл нь зөвхөн хоёртын протоколтой ажилладаг. Гэхдээ энэ нь тодорхой тохиолдолд асуудал үүсгэдэггүй.
Трик нь цүнхэнд байгаа: таны хийх ёстой зүйл бол ASCII протокол руу шилжих бөгөөд бүх зүйл ажиллах болно.... Гэсэн хэдий ч, энэ тохиолдолд хариулт хайх зуршил
Тиймээ, зөв сонголтын нэр memcached.sess_binary_protocol
. Үүнийг идэвхгүй болгох ёстой бөгөөд үүний дараа сессүүд ажиллаж эхэлнэ. Үлдсэн зүйл бол mcrouter-тай савыг PHP-тэй pod руу хийх явдал юм!
дүгнэлт
Тиймээс зөвхөн дэд бүтцийн өөрчлөлтөөр бид асуудлыг шийдэж чадсан: memcach-ийн алдааг тэсвэрлэх чадварын асуудал шийдэгдэж, кэш хадгалах найдвартай байдал нэмэгдсэн. Хэрэглээний тодорхой давуу талуудаас гадна энэ нь платформ дээр ажиллахдаа маневр хийх боломжийг олгосон: бүх бүрэлдэхүүн хэсгүүд нөөцтэй байх үед администраторын амьдрал ихээхэн хялбаршдаг. Тийм ээ, энэ арга нь бас сул талуудтай, энэ нь "таяг" шиг харагдаж магадгүй, гэхдээ хэрэв энэ нь мөнгө хэмнэж, асуудлыг булж, шинэ зүйл үүсгэхгүй бол яагаад болохгүй гэж?
PS
Мөн манай блог дээрээс уншина уу:
- "Dapp-тай дасгал хий" (жишээ нь symfony-demo ашиглах):
1-р хэсэг (энгийн програм бүтээх) и2-р хэсэг (Helm ашиглан Docker зургийг Kubernetes-д байрлуулах) ; - «
Кубернетестэй хийсэн амьдралаас: HTTP сервер Испаничуудад хэрхэн таалагдаагүй вэ? ".
Эх сурвалж: www.habr.com