Computer >> कंप्यूटर ट्यूटोरियल >  >> प्रोग्रामिंग >> Ruby

रेल्स व्यू कैशिंग में महारत: एक व्यापक गाइड

कैशिंग एक सामान्य शब्द है जिसका अर्थ है किसी कोड के परिणाम को संग्रहीत करना ताकि हम उसे बाद में जल्दी से पुनः प्राप्त कर सकें। यह हमें उदाहरण के लिए, डेटाबेस को बार-बार हिट करने से बचाता है ताकि ऐसे डेटा को प्राप्त किया जा सके जो शायद ही कभी बदलता है। हालांकि कैशिंग की सामान्य अवधारणा सभी प्रकार के कैशिंग के लिए समान है, रेल्स हमें इस आधार पर अलग-अलग सहायता प्रदान करता है कि हम क्या कैश करने का प्रयास कर रहे हैं।

रूबी ऑन रेल्स व्यूज़ को कैसे रेंडर करता है

पहले, आइए थोड़ी भ्रमित करने वाली शब्दावली को कवर करें। जिसे रेल्स समुदाय "व्यूज़" कहता है, वे फाइलें हैं जो आपके app/views निर्देशिका के अंदर रहती हैं। आमतौर पर, ये .html.erb फाइलें होती हैं, हालांकि अन्य विकल्प भी हैं (यानी, सादा .html, .js.erb, या ऐसी फाइलें जो अन्य प्रीप्रोसेसर का उपयोग करती हैं, जैसे स्लिम और हैमल)। कई अन्य वेब फ्रेमवर्क में, इन फाइलों को "टेम्पलेट्स" कहा जाता है, जो मुझे लगता है कि उनके उपयोग का बेहतर वर्णन करता है।

जब एक रेल्स एप्लिकेशन को GET अनुरोध प्राप्त होता है, तो इसे एक विशेष कंट्रोलर एक्शन पर रूट किया जाता है, उदाहरण के लिए, UsersController#index। एक्शन तब डेटाबेस से किसी भी आवश्यक जानकारी को इकट्ठा करने और व्यू/टेम्पलेट फाइल को रेंडर करने के लिए उपयोग करने के लिए पास करने के लिए जिम्मेदार होता है। इस बिंदु पर, हम "व्यू लेयर" में प्रवेश कर रहे हैं।

आमतौर पर, आपका व्यू (या टेम्पलेट) हार्ड-कोडेड HTML मार्कअप और डायनामिक रूबी कोड का मिश्रण होगा:

#app/views/users/index.html.erb 
<div class='user-list'> 
  <% @users.each do |user| %> 
    <div class='user-name'><%= user.name %></div> 
  <% end %> 
</div> 

फाइल में रूबी कोड को व्यू को रेंडर करने के लिए निष्पादित करने की आवश्यकता होती है (erb के लिए वह सब कुछ जो <% %> टैग में है)। पेज को 100 बार रिफ्रेश करें, और @users.each... 100 बार निष्पादित होगा। शामिल किसी भी पार्शियल के लिए भी यही सच है; प्रोसेसर को पार्शियल html.erb फाइल को लोड करना होगा, उसके अंदर सभी रूबी कोड को निष्पादित करना होगा, और अनुरोधकर्ता को वापस भेजने के लिए एक सिंगल HTML फाइल में परिणामों को संयोजित करना होगा।

धीमे व्यूज़ का कारण क्या है

आपने शायद देखा होगा कि जब आप विकास के दौरान एक पेज देखते हैं, तो रेल्स बहुत सारी लॉग जानकारी प्रिंट करता है, जो निम्नलिखित जैसी दिखती है:

Processing by PagesController#home as HTML 
Rendering layouts/application.html.erb 
Rendering pages/home.html.erb within layouts/application 
Rendered pages/home.html.erb within layouts/application (Duration: 4.0ms | Allocations: 1169) 
Rendered layouts/application.html.erb (Duration: 35.9ms | Allocations: 8587) 
Completed 200 OK in 68ms (Views: 40.0ms | ActiveRecord: 15.7ms | Allocations: 14307) 

अंतिम पंक्ति इस स्तर पर हमारे लिए सबसे उपयोगी है। बाएं से दाएं समय का अनुसरण करके, हम देखते हैं कि रेल्स द्वारा ब्राउज़र को प्रतिक्रिया लौटाने में लिया गया कुल समय 68ms था, जिसमें से 40ms erb फाइलों को रेंडर करने में और 15.7ms ActiveRecord क्वेरीज़ को प्रोसेस करने में खर्च हुआ।

हालांकि यह एक तुच्छ उदाहरण है, यह यह भी दिखाता है कि हम व्यू लेयर को कैश करने पर विचार क्यों करना चाह सकते हैं। यहां तक कि अगर हम जादुई रूप से ActiveRecord क्वेरीज़ को तुरंत होने दें, तो हम erb को रेंडर करने में दोगुने से अधिक समय खर्च कर रहे हैं।

हमारे व्यू रेंडरिंग के धीमे होने के कुछ कारण हो सकते हैं; उदाहरण के लिए, हम व्यूज़ के भीतर महंगे DB क्वेरीज़ कॉल कर रहे होंगे या लूप्स के भीतर बहुत सारा काम कर रहे होंगे। सबसे आम स्थितियों में से एक जो मैंने देखी है, वह है बस बहुत सारे पार्शियल्स को रेंडर करना, शायद कई स्तरों की नेस्टिंग के साथ।

एक ईमेल इनबॉक्स की कल्पना करें, जहां हमारे पास एक पार्शियल हो सकता है जो एक व्यक्तिगत पंक्ति को संभालता है:

# app/views/emails/_email.html.erb 
<li class="email-line"> 
  <div class="email-sender"> 
    <%= email.from_address %> 
  </div> 
  <div class="email-subject"> 
    <%= email.subject %> 
  </div> 
</div> 

और, हमारे मुख्य इनबॉक्स पेज में, हम प्रत्येक ईमेल के लिए पार्शियल को रेंडर करते हैं:

# app/views/emails/index.html.erb 
... 
<% @emails.each do |email| %> 
  <%= render email %> 
<% end %> 

अगर हमारे इनबॉक्स में 100 संदेश हैं, तो हम _email.html.erb पार्शियल्स को 100 बार रेंडर कर रहे हैं। हमारे तुच्छ उदाहरण के साथ, यह बहुत चिंता का विषय नहीं है। मेरी मशीन पर, पार्शियल को पूरे इंडेक्स को रेंडर करने में केवल 15ms लगते हैं। बेशक, वास्तविक दुनिया के उदाहरण अधिक जटिल होंगे और उनमें उनके भीतर अन्य पार्शियल्स भी शामिल हो सकते हैं; रेंडर समय का बढ़ना मुश्किल नहीं है। यहां तक कि अगर हमारे _email पार्शियल को रेंडर करने में केवल 1-2ms लगते हैं, तो पूरे संग्रह को करने में 100-200ms लगेंगे।

सौभाग्य से, रेल्स में कुछ अंतर्निहित कार्यक्षमता है जो हमें इस समस्या को हल करने के लिए आसानी से कैशिंग जोड़ने में मदद करती है, चाहे हम सिर्फ _email पार्शियल को कैश करना चाहते हों, index पेज को, या दोनों को।

व्यू कैशिंग क्या है

रूबी ऑन रेल्स में व्यू कैशिंग एक व्यू द्वारा उत्पन्न HTML को लेना और बाद के लिए संग्रहीत करना है। हालांकि रेल्स में इन्हें फाइलसिस्टम पर लिखने या उन्हें मेमोरी में रखने के लिए समर्थन है, उत्पादन उपयोग के लिए, आप लगभग निश्चित रूप से एक स्टैंडअलोन कैशिंग सर्वर चाहेंगे, जैसे Memcached या Redis। रेल्स का memory_store विकास के लिए उपयोगी है लेकिन इसे प्रक्रियाओं (जैसे, कई सर्वर/डाइनोज़ या फोर्किंग सर्वर, जैसे यूनिकॉर्न) में साझा नहीं किया जा सकता है। इसी तरह, file_store सर्वर के लिए स्थानीय है। इसलिए, इसे कई बॉक्सों में साझा नहीं किया जा सकता है, और यह समाप्त हो चुकी प्रविष्टियों को स्वचालित रूप से नहीं हटाएगा, इसलिए आपको अपने सर्वर की डिस्क को भरने से रोकने के लिए समय-समय पर Rails.cache.clear को कॉल करना होगा।

कैशिंग स्टोर को सक्षम करना आपके वातावरण कॉन्फ़िगरेशन फाइल (जैसे, config/environments/production.rb) में किया जा सकता है:

 # मेमोरी स्टोर टेस्टिंग के लिए आसान है 
 # विकास के दौरान लेकिन सलाह नहीं दी जाती 
 # उत्पादन के लिए 
 config.cache_store = :memory_store 

एक डिफ़ॉल्ट इंस्टॉलेशन में, आपका development.rb में पहले से ही आपके लिए कुछ कॉन्फ़िगरेशन होगा ताकि आप अपनी मशीन पर कैशिंग को आसानी से टॉगल कर सकें। बस कैशिंग को चालू और बंद करने के लिए rails dev:cache चलाएं।

रेल्स में एक व्यू को कैश करना धोखे से सरल है, इसलिए प्रदर्शन अंतर को स्पष्ट करने के लिए, मैं कृत्रिम देरी बनाने के लिए सिर्फ sleep(5) का उपयोग करूंगा:

<% cache do %> 
  <div> 
    <p>Hi <%= @user.name %> <% sleep(5) %> 
  </div> 
<% end %> 

पहली बार इस व्यू को रेंडर करने में 5 सेकंड लगते हैं, जैसा कि अपेक्षित था। हालांकि, इसे दूसरी बार लोड करने में केवल कुछ मिलीसेकंड लगते हैं क्योंकि cache do ब्लॉक के अंदर सब कुछ कैश से प्राप्त किया जाता है।

उदाहरण द्वारा व्यू कैशिंग जोड़ना

आइए एक छोटे उदाहरण व्यू को लें और कैशिंग के लिए हमारे विकल्पों के माध्यम से चलें। हम मान लेंगे कि यह व्यू वास्तव में कुछ प्रदर्शन समस्याएं पैदा कर रहा है:

# app/views/user/show.html.erb 
<div> Hi <%= @user.name %>! <div> 
<div> Here's your list of posts, you've written <%= @user.posts.count %> so far 
  <% @user.posts.each do |post| 
    <div><%= post.body %></div> 
  <% end %> 
</div> 
<% sleep(5) #कृत्रिम देरी %> 

यह हमें काम करने के लिए एक बुनियादी कंकाल देता है, साथ ही हमारी कृत्रिम 5-सेकंड की देरी। पहले, हम पूरे show.html.erb फाइल को cache do ब्लॉक में लपेट सकते हैं, जैसा कि पहले वर्णित है। अब, एक बार कैश गर्म होने पर, हमें अच्छे, तेज़ रेंडरिंग समय मिलते हैं। इस योजना के साथ मुद्दों को देखना शुरू करने में ज्यादा समय नहीं लगता, हालांकि।

पहला, क्या होता है अगर यूज़र्स अपना नाम बदलते हैं? हमने रेल्स को नहीं बताया है कि हमारे कैश्ड पेज को कब समाप्त करना है, इसलिए यूज़र को कभी भी अपडेटेड वर्जन नहीं दिख सकता है। एक आसान समाधान है @user ऑब्जेक्ट को cache मेथड में पास करना:

<% cache(@user) do %> 
  <div> Hi <%= @user.name %>! </div> 
  ... 
  <% sleep(5) #कृत्रिम देरी %> 
<% end %> 

इस श्रृंखला के पिछले लेख में लो-लेवल कैशिंग पर कैश कुंजियों के विवरण को कवर किया गया था, इसलिए मैं इसे यहां फिर से कवर नहीं करूंगा। अभी के लिए, यह जानना पर्याप्त है कि अगर हम cache() में एक मॉडल पास करते हैं, तो यह कैश में देखने के लिए एक कुंजी उत्पन्न करने के लिए उस मॉडल के updated_at एट्रिब्यूट का उपयोग करेगा। दूसरे शब्दों में, जब भी @user अपडेट होता है, यह कैश्ड पेज समाप्त हो जाएगा, और रेल्स HTML को फिर से रेंडर करेगा।

हमने उस मामले का ध्यान रखा है जब यूज़र्स अपना नाम बदलते हैं, लेकिन उनके पोस्ट्स का क्या? किसी मौजूदा पोस्ट को बदलना या नया बनाना User के updated_at टाइमस्टैम्प को नहीं बदलेगा, इसलिए हमारा कैश्ड पेज समाप्त नहीं होगा। इसके अलावा, अगर यूज़र्स अपना नाम बदलते हैं, तो हम उनके सभी पोस्ट्स को फिर से रेंडर करेंगे, भले ही उनके पोस्ट्स नहीं बदले हों। दोनों समस्याओं को हल करने के लिए, हम "रूसी गुड़िया कैशिंग" (यानी, कैश के भीतर कैश) का उपयोग कर सकते हैं:

<% cache(@user) do %> 
  <div> Hi <%= @user.name %>! <div> 
  <div> Here's your list of posts, you've written <%= @user.posts.count %> so far<br> 
  <% @user.posts.each do |post| %> 
    <% cache(post) do %> 
      <div><%= post.body %></div> 
    <% end %> 
  <% end %> 
  </div> 
  <% sleep(5) #कृत्रिम देरी %> 
<% end %> 

अब हम प्रत्येक व्यक्तिगत रूप से रेंडर किए गए post को कैश कर रहे हैं (वास्तविक दुनिया में, यह शायद एक पार्शियल होगा)। इसलिए, भले ही @user अपडेट हो, हमें पोस्ट को फिर से रेंडर नहीं करना पड़ेगा; हम सिर्फ कैश्ड मान का उपयोग कर सकते हैं। हमारे पास अभी भी एक और मुद्दा है, हालांकि। अगर एक post बदलता है, तो हम अभी भी अपडेट को रेंडर नहीं करेंगे क्योंकि @user.update_at नहीं बदला है, इसलिए cache(@user) do के अंदर का ब्लॉक निष्पादित नहीं होगा।

इस मुद्दे को ठीक करने के लिए, हमें अपने Post मॉडल में touch: true जोड़ने की आवश्यकता है:

class Post < ApplicationRecord 
  belongs_to :user, touch: true 
end 

touch: true जोड़कर, हम ActiveRecord को बता रहे हैं कि हर बार एक पोस्ट अपडेट होता है, हम चाहते हैं कि उस यूज़र का updated_at टाइमस्टैम्प भी अपडेट हो जिसे यह "belongs to" करता है।

मुझे यह भी जोड़ना चाहिए कि रेल्स पार्शियल्स के संग्रह को रेंडर करने के लिए एक विशिष्ट हेल्पर प्रदान करता है, यह देखते हुए कि यह कितना आम है:

 <%= render partial: 'posts/post', collection: @posts, cached: true %> 

जो निम्नलिखित के कार्यात्मक रूप से समकक्ष है:

<% @posts.each do |post| %> 
  <% cache(post) do %> 
    <%= render post %> 
  <% end %> 
<% end %> 

न केवल render partial: ... cached: true फॉर्म कम वर्बोज़ है, यह आपको कुछ अतिरिक्त दक्षता भी देता है क्योंकि रेल्स आपके कैश स्टोर को मल्टीगेट जारी कर सकता है (यानी, एक ही राउंड-ट्रिप में कई कुंजी/मान जोड़े पढ़ना) बजाय आपके कैश स्टोर को संग्रह में प्रत्येक आइटम के लिए हिट करने के।

डायनामिक पेज कंटेंट

कुछ पेजों में कुछ मात्रा में 'डायनामिक' कंटेंट शामिल होना आम बात है जो पेज के बाकी हिस्सों की तुलना में बहुत तेज दर से बदलता है। यह विशेष रूप से होम पेज या डैशबोर्ड पर सच है, जहां आपके पास एक्टिविटी/न्यूज़ फीड हो सकते हैं। इन्हें हमारे कैश्ड पेज में शामिल करने का मतलब हो सकता है कि हमारे कैश को बार-बार अमान्य करने की आवश्यकता है, जो कैशिंग से मिलने वाले लाभ को सीमित करता है।

एक सरल उदाहरण के रूप में, आइए अपने व्यू में वर्तमान दिन जोड़ें:

<% cache(@user) do %> 
  <div> Hi <%= @user.name %>, hope you're having a great <%= Date.today.strftime("%A") %>! <div> 
  ... 
<% end %> 

हम हर दिन कैश को अमान्य कर सकते हैं, लेकिन यह स्पष्ट कारणों से बहुत व्यावहारिक नहीं है। एक विकल्प प्लेसहोल्डर मान (या यहां तक कि सिर्फ एक खाली <span>) का उपयोग करना है और इसे जावास्क्रिप्ट से भरना है। इस तरह के दृष्टिकोण को अक्सर "जावास्क्रिप्ट स्प्रिंकल्स" कहा जाता है और यह बेसकैंप द्वारा पसंद किया जाने वाला दृष्टिकोण है, जहां रेल्स के बहुत सारे कोर कोड विकसित किए जाते हैं। परिणाम कुछ इस तरह होगा:

<% cache(@user) do %> 
  <div> Hi <%= @user.name %>, hope you're having a great <span id='greeting-day-name'>Day</span>! <div> 
  ... 
<% end %> 
<script> 
  // मान लें कि आप टर्बोलिंक्स के साथ वैनिला JS का उपयोग कर रहे हैं 
  document.addEventListener( 
    "turbolinks:load", 
    function() { 
      weekdays = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); 
      today = weekdays[new Date().getDay()]; 
      document.getElementById("greeting-day-name").textContent=today; 
    }); 
</script> 

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

<div> Hi <%= @user.name %>, hope you're having a great <%= Date.today.strftime("%A") %>! <div> 
  <% cache(@user) do %> 
    ... 
  <% end %> 

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

चेतावनी के शब्द

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

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

लो-लेवल कैशिंग के साथ, सबसे अच्छी सलाह यह है कि आप इसके उपयोग के बारे में रणनीतिक रहें। स्वीकार्य प्रदर्शन स्तर प्राप्त करने के लिए जितना संभव हो उतना कम कैशिंग का उपयोग करें, जितनी कम जगहों पर कर सकते हैं।

रेल्स का डिफ़ॉल्ट कैशिंग

इस कैशिंग श्रृंखला में अब तक हमने मैन्युअल रूप से चीजों को कैश करने के तरीकों को कवर किया है, लेकिन यहां तक कि किसी भी मैन्युअल कॉन्फ़िगरेशन के बिना, ActiveRecord पहले से ही क्वेरीज़ को तेज़ करने (या उन्हें पूरी तरह से छोड़ने) के लिए हुड के नीचे कुछ कैशिंग करता है। इस कैशिंग श्रृंखला के अगले लेख में हम देखेंगे कि ActiveRecord हमारे लिए क्या कैश कर रहा है, और कैसे, थोड़े से काम के साथ, हम इसे "काउंटर कैश" रखने के लिए प्राप्त कर सकते हैं ताकि thing.children.size जैसी लाइनों को अप-टू-डेट काउंट प्राप्त करने के लिए डेटाबेस को हिट न करना पड़े।

  1. सैमसंग गैलेक्सी S9: व्यापक गाइड और प्रमुख विशेषताएं सैमसंग गैलेक्सी S9: व्यापक गाइड और प्रमुख विशेषताएं

    लंबा इंतजार आखिरकार खत्म हुआ! सैमसंग के फ्लैगशिप फोन S9 और S9+ का हाल ही में MWC (मोबाइल वर्ल्ड कॉन्फ्रेंस) 2018 में अनावरण किया गया। बार्सिलोना में आयोजित मोबाइल वर्ल्ड कॉन्फ्रेंस 2018 में गैलेक्सी सीरीज के सैमसंग के सबसे बड़े और नवीनतम फोन का रिलीज देखा गया। सैमसंग के फ्लैगशिप फोन में इसके पिछले

  1. ब्लूटूथ 5: सुविधाओं, लाभों और अनुकूलता के लिए एक व्यापक गाइड ब्लूटूथ 5: सुविधाओं, लाभों और अनुकूलता के लिए एक व्यापक गाइड

    वाई-फाई की तरह, ब्लूटूथ किसी भी मोबाइल या अन्य डिवाइस का एक अभिन्न अंग है। न केवल इसका उपयोग फ़ाइलों और डेटा को ट्रांसफर करने के लिए किया जाता है बल्कि यह आपको अपने वायरलेस डिवाइस जैसे स्पीकर, स्पोर्ट्स इयरफोन आदि को कनेक्ट करने की भी अनुमति देता है। ब्लूटूथ ने न केवल संचार के तरीके को बदल दिया बल्

  1. PBM फाइल्स समझें: मुख्य तथ्य और उनका उपयोग कैसे करें PBM फाइल्स समझें: मुख्य तथ्य और उनका उपयोग कैसे करें

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