कैशिंग का वर्णन करने का एक सामान्य तरीका कुछ डेटा को स्टोर करना है ताकि हम इसे बाद में जल्दी से पुनः प्राप्त कर सकें। कभी-कभी, इसका मतलब कंप्यूटेड डेटा को स्टोर करना होता है ताकि इसे पुनः गणना करने की आवश्यकता न हो, लेकिन यह स्थानीय रूप से डेटा स्टोर करने को भी संदर्भित कर सकता है ताकि इसे फिर से लाने की आवश्यकता न हो। आपका कंप्यूटर इसे लगातार करता है, क्योंकि आपका ऑपरेटिंग सिस्टम अक्सर एक्सेस किए गए डेटा को RAM में रखने की कोशिश करता है ताकि इसे हार्ड ड्राइव या SSD से फिर से लाने की आवश्यकता न हो।
इसी तरह, आपका ब्राउज़र उन संसाधनों का पुन: उपयोग करने का प्रयास करता है जो उसने पहले ही डाउनलोड कर लिए हैं। आपने शायद इसे स्वयं देखा है जब पहली बार किसी नई वेबसाइट पर जा रहे हों। प्रारंभिक लोड में अधिक समय लगता है क्योंकि आपके ब्राउज़र को सब कुछ खींचना पड़ता है जिसकी उसे आवश्यकता होती है, जिसमें सभी इमेजेज, जावास्क्रिप्ट, और स्टाइलशीट्स शामिल हैं। एक मजेदार तथ्य यह है कि जब आप CNN होमपेज को नए सिरे से डाउनलोड करते हैं, तो आपका ब्राउज़र 1993 के मूल Doom गेम से अधिक डेटा लाता है। जिज्ञासु लोगों के लिए, इस ब्लॉग पोस्ट को लिखने के समय, CNN मेरी मशीन पर 3MB से अधिक डाउनलोड करता है, ~15MB से संकुचित, और यह एक विज्ञापन ब्लॉकर सक्षम होने के साथ, जबकि मूल Doom इंस्टॉलर ~2.2MB था।
ब्राउज़र के लिए इस डेटा को कैश करने के लिए, सर्वर के साथ कुछ समन्वय की आवश्यकता होती है। ब्राउज़र को यह जानना होगा कि वह क्या कैश कर सकता है और कब तक; अन्यथा, यह आपको पुरानी सामग्री दिखा सकता है जब सर्वर के पास एक नया वर्जन उपलब्ध हो। इस लेख में, हम देखेंगे कि यह क्लाइंट-सर्वर कैशिंग समन्वय कैसे किया जाता है और रेल्स इसे बदलने के लिए क्या प्रदान करता है।
हालांकि फोकस इस बात पर है कि रूबी ऑन रेल्स इसे कैसे संभालता है, वास्तविक तंत्र HTTP विनिर्देशों का हिस्सा है। दूसरे शब्दों में, हम यहां जिस कैशिंग के बारे में बात कर रहे हैं, वह इंटरनेट के बुनियादी ढांचे में बेक की गई है, जो इसे आधुनिक वेबसाइटों और फ्रेमवर्क के विकास का एक आधारशिला बनाती है। विभिन्न फ्रेमवर्क, जैसे रेल्स, सिंगल-पेज एप्लिकेशंस (SPAs), और यहां तक कि स्टेटिक साइट्स, सभी प्रदर्शन को बेहतर बनाने में मदद के लिए इन तंत्रों का उपयोग कर सकते हैं।
HTTP रिक्वेस्ट-रिस्पॉन्स
आप शायद रिक्वेस्ट-रिस्पॉन्स जीवनचक्र से परिचित हैं, कम से कम उच्च स्तर पर: आप किसी वेबसाइट पर एक लिंक पर क्लिक करते हैं, आपका ब्राउज़र उस कंटेंट के लिए सर्वर को एक रिक्वेस्ट भेजता है, और सर्वर वह कंटेंट वापस भेजता है (ध्यान दें कि मैं यहां जानबूझकर बहुत सारी जटिलता को छोड़ रहा हूं)।
आइए इस आगे-पीछे लेनदेन में भेजे जा रहे वास्तविक डेटा में थोड़ा गहराई से जाएं। प्रत्येक HTTP संदेश में एक हेडर और एक बॉडी होती है (इसे <head> और <body> HTML टैग्स के साथ भ्रमित न करें)। रिक्वेस्ट हेडर सर्वर को बताता है कि आप किस पथ तक पहुंचने की कोशिश कर रहे हैं और किस HTTP मेथड का उपयोग करना है (जैसे, GET/PUT/PATCH/POST)। यदि आवश्यक हो, तो आप अपने ब्राउज़र के डेवलपर टूल्स या कमांड-लाइन टूल, जैसे curl का उपयोग करके इन हेडर्स में खुदाई कर सकते हैं:
# curl -v honeybadger.io ... > GET / HTTP/1.1 > Host: honeybadger.io > User-Agent: curl/7.64.1 > Accept: */* आउटपुट का यह पहला भाग रिक्वेस्ट हेडर है। हम honeybadger.io पर एक GET जारी कर रहे हैं। इसके बाद सर्वर द्वारा वापस भेजा गया ( "रिस्पॉन्स हेडर" ) आता है:
> < HTTP/1.1 301 Moved Permanently < Cache-Control: public, max-age=0, must-revalidate < Content-Length: 39 < Content-Security-Policy: frame-ancestors 'none' ... < Content-Type: text/plain रिस्पॉन्स में HTTP कोड शामिल है (जैसे, सफलता के लिए 200 या नहीं मिला के लिए 404)। इस उदाहरण में, यह एक स्थायी रीडायरेक्ट (301) है क्योंकि curl http URL से संपर्क करने की कोशिश कर रहा है, जो सुरक्षित https URL पर रीडायरेक्ट करता है। रिस्पॉन्स हेडर में कंटेंट टाइप भी शामिल है, जो यहां text/plain है, लेकिन कुछ अन्य सामान्य विकल्प text/html, text/css, text/javascript, और application/json हैं।
रिस्पॉन्स बॉडी हेडर का अनुसरण करती है। हमारे मामले में, बॉडी खाली है क्योंकि 301 रीडायरेक्ट को बॉडी की आवश्यकता नहीं होती है। यदि हम curl -v https://www.honeybadger.io के साथ फिर से कोशिश करते, तो आप यहां होमपेज कंटेंट देखेंगे, वही जैसा कि आप ब्राउज़र में स्रोत देख रहे थे।
यदि आप इसे स्वयं आजमाना चाहते हैं तो यहां दो सुझाव हैं:
- curl के साथ केवल रिस्पॉन्स हेडर दिखाने के लिए (जैसे, कोई रिक्वेस्ट हेडर्स या रिस्पॉन्स बॉडी नहीं),
-Iविकल्प का उपयोग करें, जैसेcurl -I localhost:3000में। - डिफ़ॉल्ट रूप से, रेल्स डेवलपमेंट एनवायरनमेंट में कैश नहीं करता है; आपको पहले
rails dev:cacheचलाने की आवश्यकता हो सकती है।
Cache-Control HTTP हेडर
जहां तक कैशिंग का सवाल है, हम जिस मुख्य हेडर की परवाह करते हैं, वह Cache-Control हेडर है। यह निर्धारित करने में मदद करता है कि कौन सी मशीनें हमारे रेल्स सर्वर से एक रिस्पॉन्स को कैश कर सकती हैं और वह कैश्ड डेटा कब समाप्त होता है। Cache-Control हेडर के भीतर, कई फील्ड्स हैं, जिनमें से अधिकांश वैकल्पिक हैं। हम यहां कुछ सबसे प्रासंगिक प्रविष्टियों के माध्यम से जाएंगे, लेकिन अधिक जानकारी के लिए, आप w3.org पर आधिकारिक HTTP स्पेक देख सकते हैं।
एक बुनियादी आउट-ऑफ-द-बॉक्स रेल्स रिस्पॉन्स हेडर से एक नमूना यहां दिया गया है:
< Content-Type: text/html; charset=utf-8 < Etag: W/"b41ce6c6d4bde17fd61a09e36b1e52ad" < Cache-Control: max-age=0, private, must-revalidate max-age
max-age फील्ड एक पूर्णांक है जिसमें वह सेकंड की संख्या होती है जिसके लिए रिस्पॉन्स वैध है। डिफ़ॉल्ट रूप से, एक व्यू के लिए रेल्स रिस्पॉन्स में यह 0 पर सेट होगा (यानी, रिस्पॉन्स तुरंत समाप्त हो जाता है, और ब्राउज़र को हमेशा एक नया वर्जन प्राप्त करना चाहिए)।
public/private
हेडर में public या private शामिल करने से यह सेट होता है कि किन सर्वरों को हमारे रिस्पॉन्स को कैश करने की अनुमति है। यदि हेडर में private शामिल है, तो इसे केवल रिक्वेस्ट करने वाले क्लाइंट (जैसे, ब्राउज़र) द्वारा कैश किया जाना है, कोई अन्य सर्वर नहीं जिसे वहां पहुंचने के लिए पार करना पड़ा हो, जैसे कंटेंट डिलीवरी नेटवर्क (CDNs) या प्रॉक्सीज। यदि हेडर में public शामिल है, तो ये मध्यस्थ सर्वर रिस्पॉन्स को कैश करने की अनुमति देते हैं। रेल्स डिफ़ॉल्ट रूप से प्रत्येक हेडर को private पर सेट करता है।
must-revalidate
रेल्स डिफ़ॉल्ट रूप से must-revalidate फील्ड भी सेट करता है। इसका अर्थ है कि क्लाइंट को अपने कैश्ड वर्जन का उपयोग करने से पहले यह पुष्टि करने के लिए सर्वर से संपर्क करना होगा कि उसका कैश्ड वर्जन अभी भी वैध है। यह निर्धारित करने के लिए कि कैश्ड वर्जन वैध है या नहीं, क्लाइंट और सर्वर ETags का उपयोग करते हैं।
ETags
ETags सर्वर द्वारा एक ऑप्शनल HTTP हेडर है जब यह क्लाइंट को एक रिस्पॉन्स भेजता है। आमतौर, यह रिस्पॉन्स पर किसी प्रकार का चेकसम होता है। जब क्लाइंट (यानी, आपका ब्राउज़र) को इस संसाधन को फिर से रिक्वेस्ट करने की आवश्यकता होती है, तो यह (यह मानते हुए कि उसके पास पिछला रिस्पॉन्स कैश्ड है) प्राप्त किए गए ETag को If-None-Match HTTP हेडर में शामिल करता है। सर्वर तब 304 HTTP कोड ("Not Modified") और एक खाली बॉडी के साथ रिस्पॉन्ड कर सकता है। इसका अर्थ है कि सर्वर पर वर्जन नहीं बदला है, इसलिए क्लाइंट को अपने कैश्ड वर्जन का उपयोग करना चाहिए।
ETags दो प्रकार के होते हैं: स्ट्रॉन्ग और वीक (एक वीक टैग को W/ प्रीफिक्स से दर्शाया जाता है)। वे उसी तरह व्यवहार करते हैं, लेकिन एक स्ट्रॉन्ग ETag का अर्थ है कि संसाधन की दो कॉपियां (सर्वर पर वर्जन और स्थानीय कैश में एक) 100% बाइट-फॉर-बाइट समान हैं। वीक ETags, हालांकि, इंगित करते हैं कि दोनों कॉपियां बाइट-फॉर-बाइट समान नहीं हो सकती हैं, लेकिन कैश्ड वर्जन का अभी भी उपयोग किया जा सकता है। इसका एक सामान्य उदाहरण रेल्स का csrf_meta_tags हेल्पर है, जो एक टोकन बनाता है जो लगातार बदलता रहता है; इस प्रकार, भले ही आपके एप्लिकेशन में एक स्टेटिक पेज हो, यह रीफ्रेश होने पर बाइट-फॉर-बाइट समान नहीं होगा क्योंकि क्रॉस-साइट-रिक्वेस्ट-फोर्जरी (CSRF) टोकन के कारण। रेल्स डिफ़ॉल्ट रूप से वीक ETags का उपयोग करता है।
रेल्स में ETags
रेल्स व्यूज पर ETags को स्वचालित रूप से संभालता है। यह आउटगोइंग हेडर्स में ETag शामिल करता है और इनकमिंग ETag हेडर्स की जांच करने और जब उचित हो तो 304 (नॉट मॉडिफाइड) कोड वापस करने के लिए मिडलवेयर है। विशेष रूप से, हालांकि, क्योंकि रेल्स व्यूज को डायनामिकली जनरेट करता है, इसे उस व्यू के लिए ETag का पता लगाने से पहले सभी रेंडरिंग कार्य करना पड़ता है। इसका मतलब है कि भले ही ETags मेल खाते हों, आप केवल नेटवर्क पर डेटा भेजने में लगने वाले समय और बैंडविड्थ को बचा रहे हैं, व्यू कैशिंग जैसी किसी चीज़ के विपरीत, जहां आप रेंडरिंग चरण को पूरी तरह से छोड़ सकते हैं यदि एक कैश्ड वर्जन है। हालांकि, रेल्स जनरेटेड ETag को ट्वीक करने के कुछ तरीके प्रदान करता है।
stale?
कभी-बदलते CSRF टोकन से ETag को बदलने से बचने का एक तरीका ActionController में stale? हेल्पर के साथ है। यह आपको ETag (स्ट्रॉन्ग या वीक) को सीधे सेट करने की अनुमति देता है। हालांकि, आप बस इसे एक ऑब्जेक्ट पास कर सकते हैं, जैसे एक एक्टिवरिकॉर्ड मॉडल, और यह ऑब्जेक्ट के updated_at टाइमस्टैम्प के आधार पर ETag की गणना करेगा या यदि आप एक कलेक्शन पास करते हैं तो अधिकतम updated_at का उपयोग करेगा:
class UsersController < ApplicationController def index @users = User.includes(:posts).all render :index if stale?(@users) end end curl के साथ पेज को हिट करके, हम परिणाम देख सकते हैं:
# curl -I localhost:3000 -- पहला पेज लोड ETag: W/"af9ae8f2d66b9b6c4d0513f185638f1a" # curl -I localhost:3000 -- रीलोड (CSRF टोकन के कारण परिवर्तन) ETag: W/"f06158417f290334f47ea2124e08d89d" -- कंट्रोलर कोड में stale? जोड़ें # curl -I localhost:3000 -- रीलोड ETag: W/"04b9b99835c359f36551720d8e3ca6fe" -- अब `@users` का उपयोग करके ETag जनरेट कर रहा है # curl -I localhost:3000 -- रीलोड ETag: W/"04b9b99835c359f36551720d8e3ca6fe" -- कोई परिवर्तन नहीं यह हमें इस पर अधिक नियंत्रण देता है कि क्लाइंट को पूर्ण पेलोड को फिर से कब डाउनलोड करना होगा, लेकिन फिर भी उसे हर बार सर्वर के साथ जांच करनी होगी कि उसका कैश अभी भी वैध है या नहीं। क्या होगा यदि हम उस जांच को पूरी तरह से छोड़ना चाहते हैं? यहीं पर हेडर में max-age फील्ड काम आती है।
expires_in और http_cache_forever
रेल्स हमें ActionController में max-age फील्ड को समायोजित करने के लिए कुछ हेल्पर मेथड्स देता है: expires_in और http_cache_forever। वे दोनों उनके नामों के आधार पर आपकी अपेक्षा के अनुसार काम करते हैं:
class UsersController < ApplicationController def index @users = User.includes(:posts).all expires_in 10.minutes end end # curl -I localhost:3000 Cache-Control: max-age=600, private रेल्स ने max-age को 600 (सेकंड में 10 मिनट) पर सेट किया है और must-revalidate फील्ड को हटा दिया है। आप public: true नामित आर्ग्यूमेंट पास करके private फील्ड को भी बदल सकते हैं।
http_cache_forever ज्यादातर expires_in के चारों ओर एक रैपर है जो max-age को 100 साल पर सेट करता है और एक ब्लॉक लेता है:
class UsersController < ApplicationController def index @users = User.includes(:posts).all http_cache_forever(public: true) do render :index end end end # curl -I localhost:3000 Cache-Control: max-age=3155695200, public इस तरह की अत्यधिक-लंबी-अवधि की कैशिंग यही कारण है कि रेल्स एसेट्स में एक "फिंगरप्रिंट" जोड़ा जाता है, जो फाइल के कंटेंट का हैश होता है और packs/js/application-4028feaf5babc1c1617b.js जैसे फाइलनाम बनाता है। अंत में "फिंगरप्रिंट" प्रभावी रूप से फाइल के कंटेंट को फाइल के नाम से जोड़ता है। यदि कंटेंट कभी बदलता है, यहां तक कि थोड़े से तरीके से, फिंगरप्रिंट बदल जाएगा; जहां तक ब्राउज़र का संबंध है, यह एक पूरी तरह से अलग फाइल है जिसे डाउनलोड करने की आवश्यकता है।
प्रभाव के क्षेत्र
अब जब हमने कुछ कैशिंग विकल्पों को कवर कर लिया है, तो मेरी सलाह थोड़ी अजीब लग सकती है, लेकिन मैं सुझाव देता हूं कि आप इस लेख में किसी भी मेथड का उपयोग करने से बचने की कोशिश करें! ETags और HTTP कैशिंग जानने के लिए अच्छे हैं, और रेल्स हमें विशिष्ट समस्याओं को संबोधित करने के लिए कुछ विशिष्ट टूल्स देता है। चेतावनी, हालांकि, और यह एक बड़ी है, यह है कि यह सभी कैशिंग आपके एप्लिकेशन के बाहर होती है और इसलिए, आपके नियंत्रण से काफी हद तक बाहर है। यदि आप व्यू कैशिंग या रेल्स में लो-लेवल कैशिंग का उपयोग कर रहे हैं, जैसा कि इस श्रृंखला के पहले भागों में कवर किया गया है, और अमान्यता मुद्दों का सामना करते हैं, तो आपके पास विकल्प हैं; आप मॉडल्स को touch कर सकते हैं, अपडेटेड कोड पुश कर सकते हैं, या यहां तक कि कंसोल से सीधे Rails.cache तक पहुंच सकते हैं यदि आपको करना है, लेकिन HTTP कैशिंग के साथ नहीं। व्यक्तिगत रूप से, मुझे प्रोडक्शन में Rails.cache.clear चलाने की तुलना में बहुत अच्छा लगेगा, बजाय एक ऐसे मुद्दे का सामना करने के जहां साइट यूजर्स के लिए तब तक टूटी हुई है जब तक वे अपना ब्राउज़र कैश साफ़ नहीं करते (आपकी कस्टमर सर्विस टीम भी आपको इसके लिए प्यार करेगी)।
निष्कर्ष
यह रेल्स में कैशिंग पर श्रृंखला का अंत है; उम्मीद है, यह उपयोगी और जानकारीपूर्ण था। कैशिंग के लिए मेरी सलाह इस प्रकार जारी है: जितना कम आप कर सकते हैं उतना कम करें, लेकिन जितना आपको करना है उतना करें। यदि आप प्रदर्शन समस्याओं का अनुभव करते हैं, तो उन मेथड्स को देखकर शुरू करें जो अक्सर हिट होते हैं; शायद, उन्हें मेमोइज़ किया जा सकता है। क्या मान को रिक्वेस्ट्स के बीच बने रहने की आवश्यकता है? शायद उस भारी-उपयोग किए गए मेथड को कुछ लो-लेवल कैशिंग का उपयोग करना चाहिए। या, शायद, यह कोई विशेष मेथड नहीं है; यह उन सभी नेस्टेड पार्शियल्स के माध्यम से क्रंचिंग है, जो चीजों को धीमा कर रहे हैं; इस मामले में, शायद व्यू लेवल कैशिंग मदद कर सकता है। रेल्स हमें प्रत्येक मुद्दे को लक्षित करने के लिए "शार्प नाइव्स" देता है, और हमें बस यह जानने की आवश्यकता है कि उनका उपयोग कब करना है।