Memcach-ыг хэвтээ байдлаар масштаблахын тулд mcrouter ашиглана

Memcach-ыг хэвтээ байдлаар масштаблахын тулд mcrouter ашиглана

Ямар ч хэл дээр өндөр ачаалалтай төслүүдийг боловсруулах нь тусгай арга барил, тусгай хэрэгсэл ашиглахыг шаарддаг боловч PHP-ийн хэрэглээний талаар ярихад нөхцөл байдал улам хүндэрч болзошгүй тул та жишээ нь: өөрийн програмын сервер. Энэ тэмдэглэлд бид memcached-д тархсан сесс хадгалах, өгөгдлийг кэшлэхтэй холбоотой танил зовлонгийн талаар ярих болно, мөн эдгээр асуудлыг нэг "тойргийн" төсөлд хэрхэн шийдсэн талаар ярих болно.

Энэ үйл явдлын баатар бол шинэчлэх бизнес төлөвлөгөөнд огт ороогүй symfony 2.3 framework дээр суурилсан PHP програм юм. Нэлээд стандарт сесс хадгалахаас гадна энэ төсөл бүрэн ашигласан "Бүхнийг кэшлэх" бодлого memcached-д: өгөгдлийн сан болон API серверүүдийн хүсэлтэд хариу өгөх, янз бүрийн тугнууд, кодын гүйцэтгэлийг синхрончлох түгжээ гэх мэт. Ийм нөхцөлд memcach-ийн эвдрэл нь програмын үйл ажиллагаанд сөргөөр нөлөөлнө. Нэмж дурдахад кэш алдагдах нь ноцтой үр дагаварт хүргэдэг: DBMS нь давхцаж эхэлдэг, API үйлчилгээнүүд хүсэлтийг хориглож эхэлдэг гэх мэт. Нөхцөл байдлыг тогтворжуулахад хэдэн арван минут шаардагдах бөгөөд энэ хугацаанд үйлчилгээ маш удаан эсвэл бүрэн боломжгүй болно.

Бид хангах шаардлагатай байсан бага хүчин чармайлтаар програмыг хэвтээ байдлаар томруулах чадвар, өөрөөр хэлбэл эх кодын хамгийн бага өөрчлөлттэй, бүрэн ажиллагаа нь хадгалагдсан. Кэшийг зөвхөн бүтэлгүйтэлд тэсвэртэй болгоод зогсохгүй өгөгдлийн алдагдлыг багасгахыг хичээ.

Memcached өөрөө юу нь болохгүй байна вэ?

Ерөнхийдөө PHP-д зориулсан memcached өргөтгөл нь тархсан өгөгдөл болон сессийн хадгалалтыг дэмждэг. Түлхүүрийг тогтмол хэшлэх механизм нь олон сервер дээр өгөгдлийг жигд байршуулах боломжийг олгодог бөгөөд тодорхой түлхүүр бүрийг бүлгийн тодорхой серверт өвөрмөц байдлаар хаяглаж, суулгасан бүтэлгүйтлийн хэрэгслүүд нь кэшийн үйлчилгээний өндөр хүртээмжийг баталгаажуулдаг (гэхдээ харамсалтай нь өгөгдөл алга).

Сеанс хадгалахад бүх зүйл арай дээрдсэн: та тохируулж болно memcached.sess_number_of_replicas, үүний үр дүнд өгөгдөл нэг дор хэд хэдэн сервер дээр хадгалагдах бөгөөд нэг memcach-д алдаа гарсан тохиолдолд өгөгдлийг бусдаас шилжүүлэх болно. Гэсэн хэдий ч, хэрэв сервер өгөгдөлгүйгээр онлайнаар буцаж ирвэл (дахин ачаалсны дараа ихэвчлэн тохиолддог) зарим түлхүүрүүд түүний талд дахин хуваарилагдах болно. Үнэндээ энэ нь гэсэн үг юм сессийн өгөгдөл алдагдах, алдсан тохиолдолд өөр хуулбар руу "явах" арга байхгүй тул.

Номын сангийн стандарт хэрэгслүүд нь голчлон чиглэгддэг хэвтээ масштаблах: тэдгээр нь кэшийг асар том хэмжээтэй болгож, өөр өөр сервер дээр байрлуулсан кодоос нэвтрэх боломжийг олгодог. Гэсэн хэдий ч манай нөхцөлд хадгалагдсан өгөгдлийн хэмжээ хэд хэдэн гигабайтаас хэтрэхгүй бөгөөд нэг эсвэл хоёр зангилааны гүйцэтгэл хангалттай байдаг. Үүний дагуу хамгийн хэрэгтэй стандарт хэрэгсэл бол ядаж нэг кэшийн жишээг ажиллах нөхцөлд байлгахын зэрэгцээ memcach-ийн бэлэн байдлыг хангах явдал юм. Гэсэн хэдий ч энэ боломжийг ашиглах боломжгүй байсан ... Энд төсөлд ашиглагдаж байсан фрэймворкийн эртний үеийг эргэн санах нь зүйтэй бөгөөд иймээс програмыг серверийн сантай ажиллах боломжгүй байсан. Сеансын өгөгдөл алдагдах тухай мартаж болохгүй: хэрэглэгчид их хэмжээний гарснаас болж үйлчлүүлэгчийн нүд анивчсан.

Хамгийн тохиромжтой нь шаардлагатай байсан memcach-д хадгалагдсан бичлэгүүдийг хуулбарлах болон тойрч гарах хуулбарууд алдаа эсвэл алдаа гарсан тохиолдолд. Энэ стратегийг хэрэгжүүлэхэд бидэнд тусалсан mcrouter.

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: Нийтлэлд өгөгдсөн жагсаалтыг хадгалах газарт нийтэлсэн болно 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)

... мөн ноорог зур Жолооны диаграм. Сонирхолтой нь зөвхөн хуулбарын тоонд суурилсан тохиргоо үүсгэгч байдаг (хэрэв хэн нэгэнд илүү товч бөгөөд дэгжин сонголт байвал сэтгэгдэл дээр хуваалцаарай):

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

Бид үүнийг туршилтын орчинд гаргаж, шалгана:

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

Алдааны текстийг хайхад ямар ч үр дүн гарсангүй, гэхдээ "mcrouter php"Төслийн хамгийн эртний шийдэгдээгүй асуудал бол тэргүүн эгнээнд байсан. дэмжлэг дутмаг memcached хоёртын протокол.

NB: Memcached дахь ASCII протокол нь хоёртын протоколоос удаан бөгөөд түлхүүрүүдийг тогтмол хэшлэх стандарт хэрэгсэл нь зөвхөн хоёртын протоколтой ажилладаг. Гэхдээ энэ нь тодорхой тохиолдолд асуудал үүсгэдэггүй.

Трик нь цүнхэнд байгаа: таны хийх ёстой зүйл бол ASCII протокол руу шилжих бөгөөд бүх зүйл ажиллах болно.... Гэсэн хэдий ч, энэ тохиолдолд хариулт хайх зуршил php.net дээрх баримт бичиг харгис онигоо тоглов. Мэдээжийн хэрэг та хэсгийн төгсгөл хүртэл гүйлгэж харахгүй бол та зөв хариултыг тэндээс олохгүй "Хэрэглэгчийн оруулсан тэмдэглэл" итгэлтэй байх болно шударга бус санал өгсөн хариулт.

Тиймээ, зөв ​​сонголтын нэр memcached.sess_binary_protocol. Үүнийг идэвхгүй болгох ёстой бөгөөд үүний дараа сессүүд ажиллаж эхэлнэ. Үлдсэн зүйл бол mcrouter-тай савыг PHP-тэй pod руу хийх явдал юм!

дүгнэлт

Тиймээс зөвхөн дэд бүтцийн өөрчлөлтөөр бид асуудлыг шийдэж чадсан: memcach-ийн алдааг тэсвэрлэх чадварын асуудал шийдэгдэж, кэш хадгалах найдвартай байдал нэмэгдсэн. Хэрэглээний тодорхой давуу талуудаас гадна энэ нь платформ дээр ажиллахдаа маневр хийх боломжийг олгосон: бүх бүрэлдэхүүн хэсгүүд нөөцтэй байх үед администраторын амьдрал ихээхэн хялбаршдаг. Тийм ээ, энэ арга нь бас сул талуудтай, энэ нь "таяг" шиг харагдаж магадгүй, гэхдээ хэрэв энэ нь мөнгө хэмнэж, асуудлыг булж, шинэ зүйл үүсгэхгүй бол яагаад болохгүй гэж?

PS

Мөн манай блог дээрээс уншина уу:

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх