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

रूबी एक्सेप्शन हैंडलिंग में महारत: रिट्राई करना, सुनिश्चित करना, और फिर से उठाना

उठाए गए एक्सेप्शन्स को रेस्क्यू किया जा सकता है ताकि चीजें गलत होने पर वैकल्पिक कोड पथ निष्पादित किया जा सके, लेकिन एक्सेप्शन्स को संभालने के और भी तरीके हैं। ऐपसिग्नल अकादमी के इस संस्करण में, हम retry और ensure कीवर्ड्स पर जाएंगे, और हम रेस्क्यू किए गए एक्सेप्शन्स को फिर से उठाने को देखेंगे।

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

ensure

ensure कीवर्ड का उपयोग सुनिश्चित करने के लिए किया जाता है कि कोड का एक ब्लॉक चले, भले ही एक्सेप्शन हो।

हमारी लाइब्रेरी में, हम Net::HTTP.start द्वारा खोले गए TCP कनेक्शन को बंद करना सुनिश्चित करना चाहेंगे, भले ही अनुरोध विफल हो जाए क्योंकि यह टाइम आउट हो जाता है, उदाहरण के लिए। ऐसा करने के लिए, हम पहले अपने अनुरोध को begin/ensure/end ब्लॉक में लपेटेंगे। ensure भाग में कोड हमेशा चलेगा, भले ही पूर्ववर्ती begin ब्लॉक में एक्सेप्शन उठाया गया हो।

ensure ब्लॉक में, हम Net::HTTP#finish को कॉल करके TCP कनेक्शन बंद करना सुनिश्चित करेंगे जब तक http वेरिएबल nil न हो, जो TCP कनेक्शन खोलने में विफल होने पर हो सकता है (जो एक एक्सेप्शन भी उठाएगा)।

require "net/http"
begin
  puts "Opening TCP connection..."
  http = Net::HTTP.start(uri.host, uri.port)
  puts "Sending HTTP request..."
  puts http.request_get(uri.path).body
ensure
  if http
    puts "Closing the TCP connection..."
    http.finish
  end
end

नोट: हम बाद में पुनः प्रयास करने के लिए कनेक्शन का उपयोग करने की अनुमति देने के लिए TCP कनेक्शन को मैन्युअल रूप से बंद करते हैं। हालांकि, चूंकि Net::HTTP.start एक ब्लॉक लेता है जो कनेक्शन बंद होने को सुनिश्चित करता है, उपरोक्त नमूने को ensure को हटाने के लिए रीराइट किया जा सकता है। दिलचस्प रूप से पर्याप्त, ensure ब्लॉक यह भी है कि इसे Net::HTTP में कैसे लागू किया गया है।

retry

retry कीवर्ड ब्लॉक में कोड के एक टुकड़े को पुनः प्रयास करने की अनुमति देता है। rescue ब्लॉक के साथ संयुक्त, हम इसका उपयोग फिर से प्रयास करने के लिए कर सकते हैं यदि हम कनेक्शन खोलने में विफल होते हैं, या यदि API प्रतिक्रिया देने में बहुत लंबा समय लेता है।

ऐसा करने के लिए, हम Net::HTTP.start कॉल में read_timeout जोड़ेंगे जो टाइमआउट को 10 सेकंड पर सेट करता है। यदि तब तक हमारे अनुरोध की प्रतिक्रिया नहीं आई है, तो यह Net::ReadTimeout उठाएगा।

हम Errno::ECONNREFUSED पर भी मैच करेंगे ताकि API पूरी तरह से डाउन होने को संभाला जा सके, जो हमें TCP कनेक्शन खोलने से रोकेगा। उस स्थिति में, http वेरिएबल nil है।

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

require "net/http"
http = nil
uri = URI("https://localhost:4567/")
begin
  unless http
    puts "Opening TCP connection..."
    http = Net::HTTP.start(uri.host, uri.port, read_timeout: 10)
  end
  puts "Executing HTTP request..."
  puts http.request_get(uri.path).body
rescue Errno::ECONNREFUSED, Net::ReadTimeout => e
  puts "Timeout (#{e}), retrying in 1 second..."
  sleep(1)
  retry
ensure
  if http
    puts "Closing the TCP connection..."
    http.finish
  end
end

अब, हमारा अनुरोध हर सेकंड पुनः प्रयास करेगा जब तक कोई Net::ReadTimeout नहीं उठाया जाता।

$ ruby retry.rb
Opening TCP connection...
Executing HTTP request...
Timeout (Net::ReadTimeout), retrying in 1 second...
Executing HTTP request...
Timeout (Net::ReadTimeout), retrying in 1 second...
Executing HTTP request...
Timeout (Net::ReadTimeout), retrying in 1 second...
Executing HTTP request...
... (अंतहीन लूप में) 

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

हार मानना: raise का उपयोग करके एक्सेप्शन्स को फिर से उठाना

जब एक एक्सेप्शन रेस्क्यू किया जाता है, तो उठाया गया एक्सेप्शन ऑब्जेक्ट rescue ब्लॉक को पास किया जाता है। हम इसका उपयोग एक्सेप्शन से डेटा निकालने के लिए कर सकते हैं, जैसे मैसेज को लॉग में प्रिंट करना, लेकिन हम इसका उपयोग बिल्कुल उसी एक्सेप्शन को फिर से उठाने के लिए भी कर सकते हैं, उसी स्टैक ट्रेस के साथ।

begin
  raise "Exception!"
rescue RuntimeError => e
  puts "Exception happened: #{e}"
  raise e
end

चूंकि हमारे पास rescue ब्लॉक में एक्सेप्शन ऑब्जेक्ट तक पहुंच है, हम कंसोल या एरर मॉनिटर पर एरर लॉग कर सकते हैं। वास्तव में, रेस्क्यू करना और फिर से उठाना ठीक वही है जो ऐपसिग्नल के इंटीग्रेशन एरर्स को ट्रैक करने के लिए करते हैं।

नोट: रूबी अंतिम उठाए गए एक्सेप्शन को $! नामक वेरिएबल में स्टोर करता है, और raise कीवर्ड डिफ़ॉल्ट रूप से इसका उपयोग करेगा। बिना किसी आर्ग्युमेंट्स के raise कॉल करने से अंतिम एक्सेप्शन फिर से उठेगा।

हमारी लाइब्रेरी में, हम कुछ पुनः प्रयासों के बाद API का दबाव कम करने के लिए फिर से उठाने का उपयोग कर सकते हैं। ऐसा करने के लिए, हम retries वेरिएबल में किए गए पुनः प्रयासों की संख्या को ट्रैक करेंगे।

जब भी टाइमआउट होता है, हम नंबर बढ़ाएंगे और जांचेंगे कि क्या यह तीन से कम या बराबर है, क्योंकि हम अधिकतम तीन पुनः प्रयास करना चाहेंगे। यदि हां, तो हम retry करेंगे। यदि नहीं, तो हम अंतिम एक्सेप्शन को फिर से उठाने के लिए raise करेंगे।

require "net/http"
http = nil
uri = URI("https://localhost:4567/")
retries = 0
begin
  unless http
    puts "Opening TCP connection..."
    http = Net::HTTP.start(uri.host, uri.port, read_timeout: 1)
  end
  puts "Executing HTTP request..."
  puts http.request_get(uri.path).body
rescue Errno::ECONNREFUSED, Net::ReadTimeout => e
  if (retries += 1) <= 3
    puts "Timeout (#{e}), retrying in #{retries} second(s)..."
    sleep(retries)
    retry
  else
    raise
  end
ensure
  if http
    puts 'Closing the TCP connection...'
    http.finish
  end
end

sleep कॉल में retries वेरिएबल का उपयोग करके, हम प्रत्येक नए प्रयास के लिए प्रतीक्षा समय बढ़ा सकते हैं।

$ ruby reraise.rb
Opening TCP connection...
Executing HTTP request...
Timeout (Net::ReadTimeout), retrying in 1 second(s)...
Executing HTTP request...
Timeout (Net::ReadTimeout), retrying in 2 second(s)...
Executing HTTP request...
Timeout (Net::ReadTimeout), retrying in 3 second(s)...
Executing HTTP request...
Closing the TCP connection...
/lib/ruby/2.4.0/net/protocol.rb:176:in `rbuf_fill': Net::ReadTimeout (Net::ReadTimeout)
... from reraise.rb:13:in `
'

हमारा अनुरोध तीन बार पुनः प्रयास किया जाता है इससे पहले कि कोड हार मान ले और अंतिम एरर को फिर से उठाए। हम तब एरर को एक लेवल ऊपर संभाल सकते हैं, या अपने ऐप को क्रैश कर सकते हैं यदि यह API की प्रतिक्रिया के बिना अपना काम खत्म नहीं कर सकता।

एक लचीला वेब API क्लाइंट

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

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

  1. Ruby में कस्टम अपवाद में महारत हासिल करें: एक चरण-दर-चरण गाइड Ruby में कस्टम अपवाद में महारत हासिल करें: एक चरण-दर-चरण गाइड

    Ruby में अपने स्वयं के अपवाद बनाना आसान है। बस इन चरणों का पालन करें: 1. एक नई क्लास बनाएं अपवाद क्लास हैं, Ruby में बाकी सब की तरह! एक नए प्रकार का अपवाद बनाने के लिए, बस एक क्लास बनाएं जो StandardError, या उसके बच्चों में से एक से इनहेरिट करती है। class MyError < StandardError end raise MyError

  1. रूबी एक्सेप्शन को प्रासंगिक डेटा के साथ बढ़ाएं – एक व्यावहारिक गाइड रूबी एक्सेप्शन को प्रासंगिक डेटा के साथ बढ़ाएं – एक व्यावहारिक गाइड

    कभी-कभी मानक बैकट्रेस/एरर मैसेज कॉम्बो पर्याप्त नहीं होता। कभी-कभी आपको एरर के कारण का पता लगाने के लिए अतिरिक्त डेटा की आवश्यकता होती है। सौभाग्य से, रूबी में यह करना बेहद आसान है। एरर मैसेज कस्टमाइज़ करना अपने एरर में प्रासंगिक जानकारी जोड़ने का सबसे सरल तरीका है उसे एक्सेप्शन के मैसेज में शामिल क

  1. रूबी लॉगिंग में महारत हासिल करें: Logger और Lograge के साथ विशेषज्ञ गाइड रूबी लॉगिंग में महारत हासिल करें: Logger और Lograge के साथ विशेषज्ञ गाइड

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