क्षैतिज रूप से याद किए गए पैमाने पर mcrouter का उपयोग करें



किसी भी भाषा में अत्यधिक भरी हुई परियोजनाओं के विकास के लिए एक विशेष दृष्टिकोण और विशेष उपकरणों के उपयोग की आवश्यकता होती है, लेकिन जब PHP में अनुप्रयोगों की बात आती है, तो स्थिति इतनी बिगड़ सकती है कि आपको विकसित करना होगा, उदाहरण के लिए, अपना स्वयं का एप्लिकेशन सर्वर । इस लेख में हम उस दर्द के बारे में बात करेंगे जो हर कोई जानता है कि सत्रों और कैशिंग डेटा के वितरित भंडारण के साथ मेमचैड में और हमने इन समस्याओं को एक "वार्ड" परियोजना में कैसे हल किया।

इस अवसर का नायक एक PHP अनुप्रयोग है जो सिम्फनी 2.3 फ्रेमवर्क पर आधारित है, जो व्यवसाय योजनाओं में बिल्कुल भी शामिल नहीं है। पूरी तरह से मानक सत्र के भंडारण के अलावा, परियोजना ने पूरी तरह से मेमचैच में सब कुछ के कैशिंग का उपयोग किया: डेटाबेस और एपीआई सर्वरों के लिए प्रतिक्रियाओं का जवाब, विभिन्न झंडे, कोड निष्पादन को सिंक्रनाइज़ करने के लिए ताले, और बहुत कुछ। इस स्थिति में, आवेदन के लिए कार्य करने के लिए निर्धारित विफलता घातक हो जाती है। इसके अलावा, कैश की हानि के गंभीर परिणाम होते हैं: DBMS तेजी से दरार करना शुरू कर देता है, एपीआई सेवाएं - प्रतिबंध अनुरोध, आदि। स्थिति के स्थिरीकरण में दसियों मिनट लग सकते हैं, और इस समय यह सेवा बहुत धीमी हो जाएगी या पूरी तरह से दुर्गम हो जाएगी।

हमें छोटे रक्त के साथ आवेदन के क्षैतिज स्केलिंग की संभावना प्रदान करने की आवश्यकता थी, अर्थात्। स्रोत कोड में न्यूनतम परिवर्तन और कार्यक्षमता का पूर्ण संरक्षण। कैश को न केवल दोष सहिष्णु बनाओ, बल्कि इससे डेटा हानि को कम करने का भी प्रयास करें।

मेमेकैच्ड में क्या गलत है?


सामान्य तौर पर, बॉक्स के बाहर PHP के लिए मेमकाटेड एक्सटेंशन डेटा और सत्रों के वितरित भंडारण का समर्थन करता है। सुसंगत कुंजी हैशिंग तंत्र आपको कई सर्वरों पर डेटा को समान रूप से रखने की अनुमति देता है, समूह में प्रत्येक विशिष्ट कुंजी को विशिष्ट रूप से संबोधित करते हुए, और फ़ेलओवर के अंतर्निहित टूल कैशिंग सेवा की उच्च उपलब्धता प्रदान करते हैं (लेकिन, दुर्भाग्य से, डेटा नहीं )।

भंडारण सत्र के साथ, चीजें थोड़ी बेहतर होती हैं: आप memcached.sess_number_of_replicas को कॉन्फ़िगर कर सकते हैं, जिसके परिणामस्वरूप डेटा को एक साथ कई सर्वरों में सहेजा जाएगा, और एक यादगार उदाहरण की विफलता के मामले में, डेटा दूसरों से स्थानांतरित किया जाएगा। हालांकि, यदि सर्वर डेटा के बिना सेवा में लौटता है (जैसा कि आमतौर पर पुनरारंभ होने के बाद मामला होता है), कुंजी का हिस्सा इसके पक्ष में पुनर्वितरित किया जाएगा। वास्तव में, इसका अर्थ सत्र डेटा का नुकसान होगा, क्योंकि मिस के मामले में किसी अन्य प्रतिकृति के लिए "जाने" का कोई रास्ता नहीं है।

मानक पुस्तकालय उपकरण मुख्य रूप से क्षैतिज स्केलिंग के उद्देश्य से होते हैं: वे आपको कैश को विशाल आकार में बढ़ाने और विभिन्न सर्वरों पर स्थित कोड से इसकी पहुंच प्रदान करने की अनुमति देते हैं। हालांकि, हमारी स्थिति में, संग्रहीत डेटा की मात्रा कई गीगाबाइट से अधिक नहीं है, और एक या दो नोड्स का प्रदर्शन काफी पर्याप्त है। तदनुसार, एक उपयोगी नियमित साधनों से, वे केवल काम की स्थिति में कम से कम एक कैश उदाहरण को बनाए रखते हुए मेमेक की उपलब्धता सुनिश्चित कर सकते हैं। हालाँकि, मैं इस अवसर का लाभ उठाने में सफल नहीं हुआ ... यहाँ हमें परियोजना में प्रयुक्त ढांचे की प्राचीनता को याद करना चाहिए, जिससे सर्वर पूल के साथ काम करने के लिए आवेदन प्राप्त करना असंभव हो गया। हम सत्र डेटा के नुकसान के बारे में भी नहीं भूलेंगे: ग्राहक पर उपयोगकर्ताओं के बाहर बड़े पैमाने पर लॉगिंग से आई आंख।

आदर्श रूप में, किसी मिस या त्रुटि की स्थिति में एक रिकॉर्ड किए गए प्रतिकृति और रेंगने वाले प्रतिकृतियों की प्रतिकृति की आवश्यकता थी। Mcrouter ने हमें इस रणनीति को लागू करने में मदद की।

mcrouter


यह फेसबुक द्वारा विकसित एक मेमकार्डेड राउटर है जो अपनी समस्याओं को हल करने के लिए है। यह मेम्केड टेक्स्ट प्रोटोकॉल का समर्थन करता है, जो आपको पागल आकार में मेमॉच्ड इंस्टॉलेशन को स्केल करने की अनुमति देता है। इस घोषणा में 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 SelectorRoute उसे इस बारे में बताता है।
  • GET अनुरोध, RandomRoute हैंडलर में आते हैं, जो children एरे में ऑब्जेक्ट्स के बीच एक पूल या रूट का चयन करता है। इस सरणी के प्रत्येक तत्व, बदले में, एक MissFailoverRoute हैंडलर है जो पूल में प्रत्येक सर्वर से गुजरता है जब तक कि उसे डेटा के साथ प्रतिक्रिया नहीं मिलती है, जिसे क्लाइंट को वापस कर दिया जाएगा।
  • यदि हम तीन सर्वरों के एक पूल के साथ विशेष रूप से MissFailoverRoute उपयोग करते हैं, तो सभी अनुरोध पहले मेमेकैस्ट उदाहरण के लिए आएंगे, और बाकी डेटा नहीं होने पर अवशिष्ट सिद्धांत पर अनुरोध प्राप्त करेंगे। इस तरह के दृष्टिकोण से सूची में पहले सर्वर का अधिभार होगा, इसलिए एक अलग अनुक्रम में पते के साथ तीन पूल बनाने और उन्हें यादृच्छिक रूप से चुनने का निर्णय लिया गया।
  • अन्य सभी अनुरोध (और यह रिकॉर्ड) AllMajorityRoute का उपयोग करके संसाधित किए AllMajorityRoute । यह हैंडलर पूल के सभी सर्वरों को अनुरोध भेजता है और उनमें से कम से कम N / 2 + 1 से प्रतिक्रियाओं का इंतजार करता है। मुझे लेखन कार्यों के लिए AllSyncRoute के उपयोग को छोड़ना पड़ा, क्योंकि इस पद्धति के लिए समूह के सभी सर्वरों से सकारात्मक प्रतिक्रिया की आवश्यकता होती है - अन्यथा यह SERVER_ERROR लौटा SERVER_ERROR । यद्यपि mcrouter डेटा को सुलभ कैश में रखेगा, कॉलिंग PHP फ़ंक्शन एक त्रुटि लौटाएगा और एक नोटिस उत्पन्न करेगा। AllMajorityRoute इतना सख्त नहीं है और उपरोक्त समस्याओं के बिना नोड्स के आधे तक decommissioning की अनुमति देता है।

इस योजना का मुख्य नुकसान यह है कि यदि वास्तव में कैश में कोई डेटा नहीं है, तो क्लाइंट से प्रत्येक अनुरोध के लिए, एन से अनुरोध करने के लिए पूल में सभी सर्वरों को निष्पादित किया जाएगा। आप पूल में सर्वरों की संख्या को कम कर सकते हैं, उदाहरण के लिए, दो तक: भंडारण विश्वसनीयता का त्याग करते हुए, हमें गुम चाबियों के अनुरोधों से अधिक गति और कम भार मिलेगा।

NB : विकी में प्रलेखन और परियोजना के मुद्दे (बंद वाले सहित), विभिन्न विन्यासों के एक पूरे भंडार का प्रतिनिधित्व करते हैं, mcrouter सीखने के लिए उपयोगी लिंक भी हो सकते हैं।

Mcrouter बनाएँ और चलाएँ


आवेदन (और खुद को याद किया जाता है) कुबेरनेट्स में हमारे लिए काम करता है - क्रमशः, एक ही स्थान और mcrouter में। कंटेनर बनाने के लिए, हम वेयरफ का उपयोग करते हैं , जिसके लिए कॉन्फ़िगरेशन इस तरह दिखाई देगा:

एनबी : इस लेख में लिस्टिंग फ़्लैंट / 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 )

... और हेल्म चार्ट को फेंक देते हैं। दिलचस्प से - प्रतिकृतियों की संख्या पर केवल एक कॉन्फ़िगर जनरेटर है (यदि किसी के पास अधिक संक्षिप्त और सुरुचिपूर्ण विकल्प है - टिप्पणियों में साझा करें) :

 {{- $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 " के अनुरोध पर सबसे पुरानी अप्रयुक्त परियोजना समस्या सबसे आगे दिखाई दी - मेमेकैल्ड बाइनरी प्रोटोकॉल के लिए समर्थन की कमी

एनबी : मेमेकैड में एएससीआईआई प्रोटोकॉल बाइनरी की तुलना में धीमा है, साथ ही सुसंगत कुंजी हैशिंग के नियमित साधन जो केवल बाइनरी प्रोटोकॉल के साथ काम करते हैं। लेकिन यह किसी विशेष मामले के लिए समस्याएं पैदा नहीं करता है।

बात टोपी में है: यह केवल ASCII प्रोटोकॉल पर स्विच करने के लिए बनी हुई है और यह काम करेगी ...। हालांकि, इस मामले में, php.net पर प्रलेखन में जवाब खोजने की आदत ने एक क्रूर मजाक खेला। आपको वहां सही उत्तर नहीं मिला ... जब तक कि निश्चित रूप से, आप अंत तक नहीं जा रहे हैं, जहां "उपयोगकर्ता द्वारा योगदान किए गए नोट्स" अनुभाग में एक सही और अवांछनीय रूप से बमबारी वाला उत्तर होगा

हां, सही विकल्प का नाम memcached.sess_binary_protocol । इसे अक्षम किया जाना चाहिए, जिसके बाद सत्र काम करना शुरू कर देंगे। यह केवल PHP के साथ फली में mcrouter के साथ कंटेनर रखने के लिए बनी हुई है!

निष्कर्ष


इस प्रकार, अकेले अवसंरचनात्मक परिवर्तनों की मदद से, हम समस्या का हल करने में सक्षम थे: मेमकास्टेड दोष सहिष्णुता के साथ समस्या हल हो गई थी, कैश भंडारण की विश्वसनीयता बढ़ गई थी। आवेदन के लिए स्पष्ट लाभ के अलावा, इसने मंच पर काम करते समय पैंतरेबाज़ी के लिए जगह दी: जब सभी घटकों में एक रिजर्व होता है, तो व्यवस्थापक का जीवन बहुत सरल होता है। हां, इस पद्धति की कमियां भी हैं, यह एक "बैसाखी" की तरह लग सकता है, लेकिन अगर यह पैसे बचाता है, समस्या को दूर करता है और नए लोगों का कारण नहीं बनता है - क्यों नहीं?

पुनश्च


हमारे ब्लॉग में भी पढ़ें:

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


All Articles