Memkeshni gorizontal ravishda o'lchash uchun mcrouter-dan foydalanish

Memkeshni gorizontal ravishda o'lchash uchun mcrouter-dan foydalanish

Har qanday tilda yuqori yuklangan loyihalarni ishlab chiqish alohida yondashuv va maxsus vositalardan foydalanishni talab qiladi, ammo PHP-dagi ilovalar haqida gap ketganda, vaziyat shu qadar og'irlashishi mumkinki, masalan, shaxsiy dastur serveri. Ushbu eslatmada biz memcached-da taqsimlangan seansni saqlash va ma'lumotlarni keshlash bilan bog'liq tanish og'riqlar va biz ushbu muammolarni bitta "bo'lim" loyihasida qanday hal qilganimiz haqida gaplashamiz.

Voqeaning qahramoni - symfony 2.3 ramkasiga asoslangan PHP ilovasi bo'lib, uni yangilash biznes rejalariga umuman kiritilmagan. Oddiy seans xotirasidan tashqari, ushbu loyiha to'liq foydalandi "hamma narsani keshlash" siyosati memcached-da: ma'lumotlar bazasi va API serverlariga so'rovlarga javoblar, turli bayroqlar, kod bajarilishini sinxronlashtirish uchun qulflar va boshqalar. Bunday holatda, memcachedning buzilishi dasturning ishlashi uchun halokatli bo'ladi. Bundan tashqari, keshni yo'qotish jiddiy oqibatlarga olib keladi: DBMS tikuvlarda yorilib keta boshlaydi, API xizmatlari so'rovlarni taqiqlashni boshlaydi va hokazo. Vaziyatni barqarorlashtirish o'nlab daqiqalarni olishi mumkin va bu vaqt ichida xizmat juda sekin yoki butunlay mavjud bo'lmaydi.

ta'minlashimiz kerak edi kam harakat bilan dasturni gorizontal ravishda o'lchash qobiliyati, ya'ni. manba kodiga minimal o'zgarishlar kiritilgan va to'liq funksionallik saqlanib qolgan. Keshni nafaqat nosozliklarga chidamli qilib qo'ying, balki undan ma'lumotlar yo'qotilishini minimallashtirishga harakat qiling.

Memcached-ning o'zi nimada?

Umuman olganda, PHP uchun memcached kengaytmasi tarqatilgan ma'lumotlarni va qutidan tashqarida seansni saqlashni qo'llab-quvvatlaydi. Kalitlarni izchil xeshlash mexanizmi sizga ma'lumotlarni ko'plab serverlarda bir tekisda tarqatish imkonini beradi, har bir alohida kalitni guruhdan ma'lum bir serverga noyob tarzda yo'naltiradi va o'rnatilgan almashtirish vositalari keshlash xizmatining yuqori mavjudligini ta'minlaydi (lekin, afsuski, ma'lumotlar yo'q).

Seansni saqlash bilan ishlar biroz yaxshiroq: siz sozlashingiz mumkin memcached.sess_number_of_replicas, buning natijasida ma'lumotlar bir vaqtning o'zida bir nechta serverlarda saqlanadi va bitta memkeshli nusxada ishlamay qolganda, ma'lumotlar boshqalardan uzatiladi. Biroq, agar server ma'lumotsiz onlayn bo'lsa (odatda qayta ishga tushirilgandan keyin sodir bo'ladi), ba'zi kalitlar uning foydasiga qayta taqsimlanadi. Aslida bu degani bo'ladi sessiya ma'lumotlarining yo'qolishi, chunki o'tkazib yuborilgan taqdirda boshqa nusxaga "o'tish" mumkin emas.

Standart kutubxona vositalari asosan quyidagilarga qaratilgan gorizontal masshtablash: ular sizga keshni ulkan o'lchamlarga oshirishga imkon beradi va unga turli serverlarda joylashtirilgan koddan kirishni ta'minlaydi. Biroq, bizning vaziyatimizda saqlangan ma'lumotlarning hajmi bir necha gigabaytdan oshmaydi va bir yoki ikkita tugunning ishlashi etarli. Shunga ko'ra, yagona foydali standart vositalar ish holatida kamida bitta kesh nusxasini saqlab, memcached mavjudligini ta'minlash bo'lishi mumkin. Biroq, bu imkoniyatdan ham foydalanishning iloji bo'lmadi... Shu o'rinda loyihada qo'llanilgan ramkaning qadimiyligini eslatib o'tish joiz, shu sababli dasturni serverlar puli bilan ishlashga olib bo'lmaydi. Seans ma'lumotlarining yo'qolishini ham unutmasligimiz kerak: foydalanuvchilarning ommaviy chiqishlaridan mijozning ko'zlari chayqalib ketdi.

Ideal holda, bu talab qilingan memkeshlangan va chetlab o'tuvchi replikalardagi yozuvlarni takrorlash xato yoki xato bo'lgan taqdirda. Ushbu strategiyani amalga oshirishga yordam berdi mcrouter.

mcrouter

Bu Facebook tomonidan o'z muammolarini hal qilish uchun ishlab chiqilgan memcached router. Bu ruxsat beruvchi memcached matn protokolini qo'llab-quvvatlaydi memcachli o'rnatishlarni o'lchash aqldan ozgan nisbatlarga. Mcrouterning batafsil tavsifi bilan tanishishingiz mumkin ushbu e'lon. Boshqa narsalar qatorida keng funksionallik u bizga kerak bo'lgan narsani qila oladi:

  • yozuvni takrorlash;
  • xatolik yuzaga kelsa, guruhdagi boshqa serverlarga qayting.

Ishga bor!

mcrouter konfiguratsiyasi

Men to'g'ridan-to'g'ri konfiguratsiyaga o'taman:

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

Nega uchta basseyn? Nima uchun serverlar takrorlanadi? Keling, bu qanday ishlashini aniqlaymiz.

  • Ushbu konfiguratsiyada mcrouter so'rov buyrug'i asosida so'rov yuboriladigan yo'lni tanlaydi. Buni yigit unga aytadi OperationSelectorRoute.
  • GET so'rovlari ishlov beruvchiga o'tadi RandomRoutemassiv ob'ektlari orasidan tasodifiy hovuz yoki marshrutni tanlaydi children. Ushbu massivning har bir elementi o'z navbatida ishlov beruvchi hisoblanadi MissFailoverRoute, u mijozga qaytariladigan ma'lumotlar bilan javob olguncha hovuzdagi har bir server orqali o'tadi.
  • Agar biz faqat foydalansak MissFailoverRoute uchta serverdan iborat pul bilan, keyin barcha so'rovlar birinchi bo'lib birinchi memkeshlangan nusxaga keladi, qolganlari esa ma'lumotlar bo'lmaganda qoldiq asosida so'rovlarni qabul qiladi. Bunday yondashuv sabab bo'ladi ro'yxatdagi birinchi serverda ortiqcha yuk, shuning uchun turli xil ketma-ketlikdagi manzillar bilan uchta hovuz yaratish va ularni tasodifiy tanlashga qaror qilindi.
  • Boshqa barcha so'rovlar (va bu rekord) yordamida qayta ishlanadi AllMajorityRoute. Ushbu ishlov beruvchi hovuzdagi barcha serverlarga so'rovlar yuboradi va ulardan kamida N/2 + 1 javobini kutadi. Foydalanishdan AllSyncRoute yozish uchun operatsiyalardan voz kechish kerak edi, chunki bu usul ijobiy javobni talab qiladi всех guruhdagi serverlar - aks holda u qaytadi SERVER_ERROR. mcrouter ma'lumotlarni mavjud keshlarga qo'shishiga qaramasdan, chaqiruvchi PHP funktsiyasi xato qaytaradi va bildirishnoma hosil qiladi. AllMajorityRoute unchalik qattiq emas va yuqorida tavsiflangan muammolarsiz birliklarning yarmigacha xizmatdan chiqarilishiga imkon beradi.

Asosiy kamchilik Ushbu sxema shundan iboratki, agar keshda haqiqatan ham ma'lumot bo'lmasa, mijozning har bir so'rovi uchun memcached uchun N so'rovi amalda bajariladi - to hamma uchun hovuzdagi serverlar. Biz hovuzlardagi serverlar sonini, masalan, ikkitaga kamaytirishimiz mumkin: saqlash ishonchliligini qurbon qilib, biz olamizоyuqori tezlik va so'rovlardan etishmayotgan kalitlarga kamroq yuk.

NB: mcrouterni o'rganish uchun foydali havolalarni ham topishingiz mumkin vikidagi hujjatlar и loyiha masalalari (shu jumladan yopiq), turli xil konfiguratsiyalarning butun omborini ifodalaydi.

mcrouter qurish va ishga tushirish

Bizning ilovamiz (va o'zi memcached) Kubernetesda ishlaydi - shunga ko'ra, mcrouter ham u erda joylashgan. Uchun konteyner yig'ish foydalanamiz werf, konfiguratsiyasi quyidagicha ko'rinadi:

NB: Maqolada keltirilgan ro'yxatlar omborda nashr etilgan 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)

... va uni eskiz qiling Rulda diagrammasi. Qizig'i shundaki, faqat replikalar soniga asoslangan konfiguratsiya generatori mavjud (agar kimdir yanada ixcham va oqlangan variantga ega bo'lsa, uni sharhlarda baham ko'ring):

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

Biz uni sinov muhitiga chiqaramiz va tekshiramiz:

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

Xato matnini qidirish hech qanday natija bermadi, ammo so'rov uchun "microuter php"Birinchi o'rinda loyihaning eng qadimgi hal qilinmagan muammosi - qo'llab-quvvatlashning etishmasligi memcached ikkilik protokoli.

NB: Memcached-dagi ASCII protokoli ikkilikdan sekinroq va kalitlarni izchil xeshlashning standart vositalari faqat ikkilik protokol bilan ishlaydi. Ammo bu muayyan holat uchun muammo tug'dirmaydi.

Ayyorlik sumkada: faqat ASCII protokoliga o'tish kifoya va hamma narsa ishlaydi.... Biroq, bu holda, javoblarni qidirish odati php.net da hujjatlar shafqatsiz hazil o'ynadi. Siz u erda to'g'ri javobni topa olmaysiz ... agar, albatta, bo'limning oxiriga o'tmasangiz "Foydalanuvchi qo'shgan qaydlar" sodiq bo'ladi va adolatsiz past ovoz berilgan javob.

Ha, to'g'ri variant nomi memcached.sess_binary_protocol. Uni o'chirib qo'yish kerak, shundan so'ng seanslar ishlay boshlaydi. Faqat mcrouterli konteynerni PHP bilan podkaga qo'yish qoladi!

xulosa

Shunday qilib, faqat infratuzilmaviy o'zgarishlar bilan biz muammoni hal qila oldik: memcached nosozliklarga chidamlilik muammosi hal qilindi va kesh saqlashning ishonchliligi oshirildi. Ilova uchun aniq afzalliklarga qo'shimcha ravishda, bu platformada ishlashda manevr qilish uchun joy berdi: agar barcha komponentlar zaxiraga ega bo'lsa, administratorning ishlash muddati ancha soddalashtiriladi. Ha, bu usulning ham kamchiliklari bor, u "tayoqcha" kabi ko'rinishi mumkin, ammo agar u pulni tejasa, muammoni ko'msa va yangilarini keltirib chiqarmasa - nega?

PS

Shuningdek, bizning blogimizda o'qing:

Manba: www.habr.com

a Izoh qo'shish