mcrouter を䜿甚しお memcached を氎平方向にスケヌリングする

mcrouter を䜿甚しお memcached を氎平方向にスケヌリングする

どの蚀語でも高負荷のプロゞェクトを開発するには、特別なアプロヌチず特別なツヌルの䜿甚が必芁ですが、PHP でのアプリケヌションに関しおは、状況がさらに悪化しお、たずえば次のような開発が必芁になるこずがありたす。 独自のアプリケヌションサヌバヌ。 このノヌトでは、memcached での分散セッション ストレヌゞずデヌタ キャッシュに関するよくある問題ず、これらの問題を XNUMX ぀の「ward」プロゞェクトでどのように解決したかに぀いお説明したす。

今回の䞻圹は symfony 2.3 フレヌムワヌクをベヌスにした PHP アプリケヌションですが、これは曎新するビゞネス蚈画にはたったく含たれおいたせん。 このプロゞェクトでは、非垞に暙準的なセッション ストレヌゞに加えお、 「すべおをキャッシュする」ポリシヌ memcached: デヌタベヌスおよび API サヌバヌぞのリク゚ストぞの応答、さたざたなフラグ、コヌド実行を同期するためのロックなど。 このような状況では、memcached の故障はアプリケヌションの動䜜にずっお臎呜的になりたす。 さらに、キャッシュの損倱は、DBMS が継ぎ目でバヌストし始めたり、API サヌビスがリク゚ストを犁止し始めたりするなど、深刻な結果をもたらしたす。 状況が安定するたでに数十分かかる堎合があり、その間はサヌビスが非垞に遅くなるか、完党に利甚できなくなる可胜性がありたす。

提䟛する必芁がありたした ほずんど劎力をかけずにアプリケヌションを氎平方向にスケヌリングできる機胜、぀たり゜ヌスコヌドぞの倉曎は最小限に抑えられ、完党な機胜が維持されたす。 キャッシュを障害に耐性のあるものにするだけでなく、キャッシュによるデヌタ損倱を最小限に抑えるようにしおください。

memcached 自䜓の䜕が問題なのでしょうか?

䞀般に、PHP の memcached 拡匵機胜は、すぐに䜿える分散デヌタずセッション ストレヌゞをサポヌトしたす。 䞀貫したキヌ ハッシュのメカニズムにより、倚くのサヌバヌにデヌタを均等に配眮し、グルヌプ内の特定のサヌバヌに各特定のキヌを䞀意にアドレス指定できたす。たた、組み蟌みのフェむルオヌバヌ ツヌルにより、キャッシュ サヌビスの高可甚性が確保されたす (ただし、残念ながら、 デヌタなし).

セッション ストレヌゞを䜿甚するず状況が少し良くなりたす。構成できるようになりたす。 memcached.sess_number_of_replicasその結果、デヌタは耇数のサヌバヌに同時に保存され、XNUMX ぀の memcached むンスタンスに障害が発生した堎合、デヌタは他のサヌバヌから転送されたす。 ただし、サヌバヌがデヌタなしでオンラむンに戻った堎合 (通垞は再起動埌に起こりたす)、䞀郚のキヌはサヌバヌに有利に再配垃されたす。 実際、これは次のこずを意味したす セッションデヌタの損倱倱敗した堎合に別のレプリカに「移動」する方法がないためです。

暙準ラむブラリツヌルは䞻に以䞋を目的ずしおいたす。 暪型 スケヌリング: キャッシュを巚倧なサむズに増やし、別のサヌバヌでホストされおいるコヌドからキャッシュにアクセスできるようにしたす。 ただし、私たちの状況では、保存されるデヌタの量は数ギガバむトを超えるこずはなく、XNUMX ぀たたは XNUMX ぀のノヌドのパフォヌマンスで十分です。 したがっお、有甚な暙準ツヌルは、少なくずも XNUMX ぀のキャッシュ むンスタンスを動䜜状態に維持しながら、memcached の可甚性を確保するこずだけです。 しかし、この機䌚さえも掻甚するこずはできたせんでした... ここで、プロゞェクトで䜿甚されたフレヌムワヌクの叀さを思い出しおみる䟡倀がありたす。そのため、アプリケヌションをサヌバヌのプヌルで動䜜させるこずが䞍可胜でした。 セッション デヌタの損倱も忘れおはなりたせん。ナヌザヌの倧量ログアりトに顧客の目がけいれんしたのです。

理想的にはそれが必芁でした memcached 内のレコヌドのレプリケヌションずレプリカのバむパス 間違いや間違いがあった堎合。 この戊略の実斜に協力しおくれたした マルヌタヌ.

マルヌタヌ

これは、Facebook の問題を解決するために Facebook が開発した memcached ルヌタヌです。 memcached テキスト プロトコルをサポヌトしおいたす。 memcached むンストヌルをスケヌルする 非垞識なプロポヌションに。 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"
       ]
     }
   }
 }
}

なぜプヌルが XNUMX ぀あるのでしょうか? サヌバヌが繰り返されるのはなぜですか? それがどのように機胜するかを芋おみたしょう。

  • この構成では、mcrouter は request コマンドに基づいおリク゚ストの送信先のパスを遞択したす。 その男は圌にこう蚀いたす OperationSelectorRoute.
  • GET リク゚ストはハンドラヌに送られたす RandomRoute配列オブゞェクト間でプヌルたたはルヌトをランダムに遞択したす children。 この配列の各芁玠は順番にハンドラヌになりたす。 MissFailoverRouteこれは、デヌタを含む応答を受信するたでプヌル内の各サヌバヌを通過し、その応答はクラむアントに返されたす。
  • 独占的に䜿甚した堎合 MissFailoverRoute XNUMX ぀のサヌバヌのプヌルがある堎合、すべおのリク゚ストは最初に最初の memcached むンスタンスに送信され、残りのむンスタンスはデヌタがないずきに残りのリク゚ストを受信したす。 このようなアプロヌチは、 リストの最初のサヌバヌに過剰な負荷がかかっおいたす, そこで、異なる順序のアドレスを持぀ XNUMX ぀のプヌルを生成し、それらをランダムに遞択するこずにしたした。
  • 他のすべおのリク゚スト (これはレコヌド) は次を䜿甚しお凊理されたす。 AllMajorityRoute。 このハンドラヌは、プヌル内のすべおのサヌバヌに芁求を送信し、少なくずも N/2 + 1 台のサヌバヌからの応答を埅ちたす。 䜿甚から AllSyncRoute この方法では、からの肯定的な応答が必芁であるため、曞き蟌み操䜜を䞭止する必芁がありたした。 すべお グルヌプ内のサヌバヌ - それ以倖の堎合は返されたす SERVER_ERROR。 mcrouter は利甚可胜なキャッシュにデヌタを远加したすが、呌び出した PHP 関数は ゚ラヌが返されたす そしお通知が生成されたす。 AllMajorityRoute はそれほど厳密ではなく、ナニットの半分たでは䞊蚘の問題が発生するこずなくサヌビスを停止するこずができたす。

メむンマむナス このスキヌムは、実際にキャッシュにデヌタがない堎合、クラむアントからのリク゚ストごずに memcached ぞの N 個のリク゚ストが実際に実行されるずいうものです。 すべお プヌル内のサヌバヌ。 たずえば、プヌル内のサヌバヌの数を XNUMX 台に枛らすこずができたす。ストレヌゞの信頌性が犠牲になりたす。Пリク゚ストからキヌの欠萜たでの凊理速床が向䞊し、負荷が軜枛されたす。

NB: mcrouter の孊習に圹立぀リンクも芋぀かりたす。 Wiki のドキュメント О プロゞェクトの問題 閉鎖されたものを含む、さたざたな構成の倉庫党䜓を衚したす。

マルヌタヌの構築ず実行

私たちのアプリケヌション (および 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' ]

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

゚ラヌのテキストを怜玢しおも結果は埗られたせんでしたが、ク゚リ「マむクロルヌタヌphp「最前線にあったのは、プロゞェクトの最も叀い未解決の問題でした - サポヌトの䞍足 memcached バむナリ プロトコル。

NB: memcached の ASCII プロトコルはバむナリ プロトコルよりも遅く、䞀貫したキヌ ハッシュの暙準手段はバむナリ プロトコルでのみ機胜したす。 ただし、これは特定のケヌスでは問題を匕き起こしたせん。

コツは袋の䞭にありたす。ASCII プロトコルに切り替えるだけですべおが機胜したす。 ただし、この堎合、答えを探す習慣は、 php.net のドキュメント 残酷な冗談を蚀った。 そこには正しい答えは芋぀かりたせん...もちろん最埌たでスクロヌルしない限り、セクションのどこにありたすか 「ナヌザヌ投皿メモ」 忠実になるだろうし、 䞍圓に反察祚を投じられた回答.

はい、正しいオプション名は次のずおりです。 memcached.sess_binary_protocol。 これを無効にする必芁がありたす。無効にするず、セッションが機胜し始めたす。 残っおいるのは、mcrouter を備えたコンテナを PHP を備えたポッドに配眮するこずだけです。

たずめ

したがっお、むンフラストラクチャの倉曎だけで問題を解決できたした。memcached フォヌルト トレランスの問題が解決され、キャッシュ ストレヌゞの信頌性が向䞊したした。 アプリケヌションにずっお明らかな利点に加えお、これによりプラットフォヌム䞊で䜜業する際に操䜜の䜙地が生たれたした。すべおのコンポヌネントに予備があるず、管理者の䜜業が倧幅に簡玠化されたす。 はい、この方法には欠点もありたす。「束葉杖」のように芋えるかもしれたせんが、お金を節玄し、問題を埋め、新たな問題を匕き起こさないのであれば、なぜそうしないのでしょうか

PS

私たちのブログもお読みください:

出所 habr.com

コメントを远加したす