Gunakan mcrouter untuk mengukur memcached secara horizontal



Pengembangan proyek yang sangat dimuat dalam bahasa apa pun membutuhkan pendekatan khusus dan penggunaan alat khusus, tetapi ketika datang ke aplikasi dalam PHP, situasinya dapat memburuk sehingga Anda harus mengembangkan, misalnya, server aplikasi Anda sendiri . Dalam artikel ini kita akan berbicara tentang rasa sakit yang semua orang tahu dengan penyimpanan terdistribusi dari sesi dan data caching di memcached dan bagaimana kita memecahkan masalah ini dalam satu proyek "lingkungan".

Pahlawan hari ini adalah aplikasi PHP berdasarkan kerangka symfony 2.3, yang tidak termasuk dalam rencana bisnis sama sekali. Selain penyimpanan sesi yang sepenuhnya standar, proyek ini menggunakan caching kebijakan segala sesuatu dalam memcached dengan semua kekuatan mereka : jawaban untuk pertanyaan ke database dan server API, berbagai bendera, kunci untuk sinkronisasi eksekusi kode, dan banyak lagi. Dalam situasi ini, kegagalan memcached menjadi fatal bagi aplikasi untuk bekerja. Selain itu, hilangnya cache menyebabkan konsekuensi serius: DBMS mulai retak pada lapisan, layanan API - permintaan larangan, dll. Stabilisasi situasi dapat memakan waktu puluhan menit, dan saat ini layanan akan sangat melambat atau menjadi sepenuhnya tidak dapat diakses.

Kami perlu memberikan kemungkinan penskalaan horizontal aplikasi dengan darah kecil , mis. dengan sedikit perubahan pada kode sumber dan pelestarian fungsionalitas lengkap. Jadikan cache tidak hanya toleran terhadap kesalahan, tetapi juga cobalah untuk meminimalkan kehilangan data darinya.

Apa yang salah dengan memcached itu sendiri?


Secara umum, ekstensi memcached untuk PHP di luar kotak mendukung penyimpanan data dan sesi yang didistribusikan. Mekanisme hashing kunci yang konsisten memungkinkan Anda untuk menempatkan data secara merata di banyak server, dengan jelas menangani setiap kunci spesifik ke server tertentu dalam grup, dan alat bawaan failover menyediakan ketersediaan tinggi dari layanan caching (tetapi, sayangnya, bukan data ).

Dengan sesi penyimpanan, segalanya menjadi sedikit lebih baik: Anda dapat mengonfigurasi memcached.sess_number_of_replicas , sebagai hasilnya data akan disimpan ke beberapa server sekaligus, dan jika terjadi kegagalan satu instance memcached, data akan ditransfer dari yang lain. Namun, jika server kembali ke layanan tanpa data (seperti biasanya setelah restart), bagian dari kunci akan didistribusikan kembali sesuai keinginannya. Bahkan, ini akan berarti hilangnya data sesi , karena tidak ada cara untuk "pergi" ke replika lain jika terjadi kehilangan.

Alat pustaka standar terutama ditujukan untuk penskalaan horizontal : mereka memungkinkan Anda untuk meningkatkan cache ke ukuran raksasa dan menyediakan akses ke sana dari kode yang terletak di server yang berbeda. Namun, dalam situasi kami, jumlah data yang disimpan tidak melebihi beberapa gigabytes, dan kinerja satu atau dua node sudah cukup. Dengan demikian, dari cara biasa yang bermanfaat, mereka hanya bisa memastikan ketersediaan memcached sambil mempertahankan setidaknya satu instance cache dalam kondisi kerja. Namun, saya tidak berhasil mengambil keuntungan dari kesempatan ini ... Di sini kita harus mengingat kembali kekunoan dari kerangka kerja yang digunakan dalam proyek, yang membuatnya tidak mungkin untuk membuat aplikasi bekerja dengan kumpulan server. Kami juga tidak akan lupa tentang hilangnya data sesi: mata berkedut karena keluar secara massal dari pengguna di pelanggan.

Idealnya, replikasi catatan dalam replika memcached dan crawling jika terjadi kesalahan atau kesalahan. Mcrouter membantu kami menerapkan strategi ini.

mcrouter


Ini adalah router memcached yang dikembangkan oleh Facebook untuk menyelesaikan masalahnya. Ini mendukung protokol teks memcached, yang memungkinkan Anda untuk skala instalasi memcached ke ukuran gila. Penjelasan rinci tentang mcrouter dapat ditemukan di pengumuman ini . Di antara fungsionalitas luas lainnya , dapat apa yang kita butuhkan:

  • mereplikasi catatan;
  • membuat fallback ke server lain grup jika terjadi kesalahan.

Untuk penyebabnya!

Konfigurasi Mcrouter


Saya akan langsung ke konfigurasi:

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

Kenapa tiga kolam? Mengapa server diulang? Mari kita lihat cara kerjanya.

  • Dalam konfigurasi ini, mcrouter memilih lintasan di mana permintaan akan dikirim berdasarkan perintah permintaan. Tipe OperationSelectorRoute memberitahunya tentang hal ini.
  • MENDAPATKAN permintaan jatuh ke penangan RandomRoute , yang secara acak memilih kumpulan atau rute di antara objek dalam array children - children . Setiap elemen dari array ini, pada gilirannya, adalah handler MissFailoverRoute yang akan melewati setiap server di pool sampai menerima respons dengan data, yang akan dikembalikan ke klien.
  • Jika kami menggunakan secara eksklusif MissFailoverRoute dengan kumpulan tiga server, maka semua permintaan akan datang terlebih dahulu ke instance memcached pertama, dan sisanya akan menerima permintaan pada prinsip residual ketika tidak ada data. Pendekatan semacam itu akan menyebabkan kelebihan server pertama dalam daftar , sehingga diputuskan untuk menghasilkan tiga kumpulan dengan alamat dalam urutan yang berbeda dan memilihnya secara acak.
  • Semua permintaan lainnya (dan catatan ini) diproses menggunakan AllMajorityRoute . Handler ini mengirimkan permintaan ke semua server di kumpulan dan menunggu respons dari setidaknya N / 2 +1 dari mereka. Saya harus mengabaikan penggunaan AllSyncRoute untuk operasi penulisan, karena metode ini memerlukan respons positif dari semua server dalam grup - jika tidak, ia akan mengembalikan SERVER_ERROR . Meskipun mcrouter akan meletakkan data dalam cache yang dapat diakses, fungsi pemanggilan PHP akan mengembalikan kesalahan dan menghasilkan pemberitahuan. AllMajorityRoute tidak begitu ketat dan memungkinkan penonaktifan hingga setengah dari node tanpa masalah di atas.

Kerugian utama dari skema ini adalah jika benar-benar tidak ada data dalam cache, maka untuk setiap permintaan dari klien, N permintaan untuk memcached akan dieksekusi - ke semua server di kumpulan. Anda dapat mengurangi jumlah server di kumpulan, misalnya, menjadi dua: mengorbankan keandalan penyimpanan, kami akan mendapatkan lebih banyak kecepatan dan lebih sedikit pemuatan dari permintaan ke kunci yang hilang.

NB : Dokumentasi dalam wiki dan masalah-masalah proyek (termasuk yang tertutup), mewakili seluruh gudang berbagai konfigurasi, juga dapat menjadi tautan yang berguna untuk mempelajari komputer.

Bangun dan jalankan mcrouter


Aplikasi (dan memcached sendiri) bekerja untuk kita di Kubernetes - masing-masing, di tempat dan mcrouter yang sama. Untuk membangun wadah, kami menggunakan werf , konfigurasi yang akan terlihat seperti ini:

NB : Daftar dalam artikel ini diterbitkan dalam repositori 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.imtqy.com/mcrouter/debrepo/xenial/PUBLIC.KEY - name: Add mcrouter Repo apt_repository: repo: deb https://facebook.imtqy.com/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 )

... dan melempar grafik Helm . Dari yang menarik - hanya ada generator konfigurasi pada jumlah replika (jika seseorang memiliki opsi yang lebih ringkas dan elegan - bagikan di komentar) :

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

Kami meluncurkan ke lingkungan pengujian dan memeriksa:

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

Pencarian dalam teks tidak memberikan kesalahan, tetapi atas permintaan " mcrouter php " masalah proyek tertua yang tidak ditutup muncul di garis depan - kurangnya dukungan untuk protokol biner memcached.

NB : protokol ASCII memcached lebih lambat dari biner, serta sarana standar hashing kunci konsisten yang hanya bekerja dengan protokol biner. Tapi ini tidak menimbulkan masalah untuk kasus tertentu.

Masalahnya adalah di topi: itu tetap hanya untuk beralih ke protokol ASCII dan itu akan berhasil .... Namun, dalam hal ini, kebiasaan mencari jawaban dalam dokumentasi di php.net memainkan lelucon yang kejam. Anda tidak akan menemukan jawaban yang tepat di sana ... kecuali, tentu saja, Anda membuka jalan sampai akhir, di mana di bagian "Catatan pengguna menyumbang" akan ada jawaban yang benar dan dibombardir secara tidak pantas .

Ya, nama opsi yang benar adalah memcached.sess_binary_protocol . Itu harus dinonaktifkan, setelah itu sesi akan mulai berfungsi. Tetap hanya menempatkan wadah dengan mcrouter di pod dengan PHP!

Kesimpulan


Dengan demikian, dengan bantuan perubahan infrastruktur saja, kami dapat menyelesaikan masalah yang ditimbulkan: masalah dengan toleransi kesalahan memcached diselesaikan, keandalan penyimpanan cache meningkat. Selain keuntungan yang jelas untuk aplikasi, ini memberi ruang untuk bermanuver ketika bekerja pada platform: ketika semua komponen memiliki cadangan, kehidupan administrator sangat disederhanakan. Ya, metode ini juga memiliki kelemahan, mungkin terlihat seperti "penopang", tetapi jika menghemat uang, mengubur masalahnya dan tidak menyebabkan yang baru - mengapa tidak?

PS


Baca juga di blog kami:

Source: https://habr.com/ru/post/id457510/


All Articles