चीजें गलत होने पर वैकल्पिक कोड पथ निष्पादित करने के लिए उठाए गए अपवादों को बचाया जा सकता है, लेकिन अपवादों को संभालने के और भी तरीके हैं। AppSignal अकादमी के इस संस्करण में, हम retry
पर जाएंगे और ensure
कीवर्ड, और हम बचाए गए अपवादों को फिर से बढ़ाने पर विचार करेंगे।
आइए मान लें कि हम एक अविश्वसनीय वेब एपीआई के साथ संचार कर रहे हैं। इसके अलावा हर बार एक बार नीचे होने के अलावा, यह इतना धीमा है कि इसके अनुरोधों में सेकंड लग सकते हैं। हमारी लाइब्रेरी इस एपीआई पर निर्भर करती है, और हमें इसे यथासंभव लचीला बनाने की आवश्यकता है।
ensure
ensure
कीवर्ड का उपयोग सुनिश्चित करने . के लिए किया जाता है अपवाद होने पर भी कोड का एक ब्लॉक चलता है।
हमारे पुस्तकालय में, हम यह सुनिश्चित करना चाहते हैं कि टीसीपी कनेक्शन Net::HTTP.start
द्वारा खोला गया है बंद है, भले ही अनुरोध विफल हो जाए क्योंकि यह समय समाप्त हो गया है, उदाहरण के लिए। ऐसा करने के लिए, हम सबसे पहले अपने अनुरोध को begin
. में लपेटेंगे /ensure
/end
खंड मैथा। कोड में ensure
भाग हमेशा चलेगा, भले ही पूर्ववर्ती begin
. में कोई अपवाद उठाया गया हो ब्लॉक करें।
ensure
. में ब्लॉक करें, हम Net::HTTP#finish
पर कॉल करके TCP कनेक्शन को बंद करना सुनिश्चित करेंगे। जब तक http
वेरिएबल nil
है , जो हो सकता है टीसीपी कनेक्शन खोलना विफल रहता है (जो एक अपवाद भी उठाएगा)।
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
नोट :हम टीसीपी कनेक्शन को मैन्युअल रूप से बंद कर देते हैं ताकि बाद में पुन:प्रयास करते समय हमें कनेक्शन का उपयोग करने की अनुमति मिल सके। हालांकि, चूंकि Net::HTTP.start
एक ब्लॉक लेता है जो यह सुनिश्चित करता है कि कनेक्शन बंद है, ऊपर दिए गए नमूने को ensure
को हटाने के लिए फिर से लिखा जा सकता है . दिलचस्प बात यह है कि यह सुनिश्चित करने वाला ब्लॉक भी है कि इसे नेट ::एचटीटीपी में कैसे लागू किया जाता है।
retry
retry
कीवर्ड एक ब्लॉक में कोड के एक टुकड़े को पुनः प्रयास करने की अनुमति देता है। एक rescue
. के साथ संयुक्त ब्लॉक, यदि हम कनेक्शन खोलने में विफल रहते हैं, या एपीआई को प्रतिक्रिया देने में बहुत अधिक समय लगता है, तो हम इसे फिर से प्रयास करने के लिए उपयोग कर सकते हैं।
ऐसा करने के लिए, हम एक read_timeout
जोड़ देंगे Net::HTTP.start
. पर कॉल जो टाइमआउट को 10 सेकंड पर सेट करता है। अगर हमारे अनुरोध का जवाब तब तक नहीं आया है, तो यह एक Net::ReadTimeout
उठाएगा ।
हम Errno::ECONNREFUSED
. पर भी मिलान करेंगे एपीआई को पूरी तरह से डाउन होने से निपटने के लिए, जो हमें टीसीपी कनेक्शन खोलने से रोकेगा। उस स्थिति में, http
वेरिएबल nil
है ।
अपवाद बचा लिया गया है और retry
begin
. शुरू करने के लिए कहा जाता है फिर से ब्लॉक करें, जिसके परिणामस्वरूप कोड वही अनुरोध कर रहा है जब तक कि कोई टाइमआउट न हो। हम 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...
... (in an endless loop)
हालांकि यह सुनिश्चित कर सकता है कि किसी भी टाइमआउट के लिए कभी भी कोई अपवाद नहीं उठाया गया है, इसे इस तरह से पुनः प्रयास करने से निश्चित रूप से उस एपीआई को फिर से प्राप्त करने में मदद नहीं मिलेगी। यह समस्याग्रस्त है क्योंकि यदि एपीआई अनुत्तरदायी रहता है तो यह कोड हमेशा के लिए लूपिंग करता रहेगा। इसके बजाय, हमें अपने पुनर्प्रयासों को फैलाना चाहिए और थोड़ी देर बाद छोड़ देना चाहिए।
छोड़ देना:raise
. का उपयोग करके अपवादों को फिर से बढ़ाना
जब कोई अपवाद बचाया जाता है, तो उठाई गई अपवाद वस्तु rescue
. को पास कर दी जाती है खंड मैथा। हम इसका उपयोग अपवाद से डेटा निकालने के लिए कर सकते हैं, जैसे संदेश को लॉग में प्रिंट करना, लेकिन हम इसका उपयोग उसी स्टैक ट्रेस के साथ ठीक उसी अपवाद को फिर से करने के लिए भी कर सकते हैं।
begin
raise "Exception!"
rescue RuntimeError => e
puts "Exception happened: #{e}"
raise e
end
चूंकि हमारे पास rescue
. में अपवाद वस्तु तक पहुंच है ब्लॉक, हम कंसोल या त्रुटि मॉनिटर में त्रुटि लॉग कर सकते हैं। वास्तव में, ऐपसिग्नल के एकीकरण त्रुटियों को ट्रैक करने का तरीका ठीक उसी तरह से है जैसे बचाव और पुनर्व्यवस्थित करना।
नोट :रूबी अंतिम उठाए गए अपवाद को $!
. नामक एक चर में संग्रहीत करता है , और raise
कीवर्ड डिफ़ॉल्ट रूप से इसका उपयोग करेगा। कॉल करना raise
बिना किसी तर्क के अंतिम अपवाद को फिर से उठाया जाएगा।
हमारे पुस्तकालय में, हम कुछ पुनर्प्रयासों के बाद एपीआई के दबाव को लेने के लिए पुनरावर्तन का उपयोग कर सकते हैं। ऐसा करने के लिए, हम 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
retries
. का उपयोग करके कॉल में परिवर्तनशील sleep
, हम हर नए प्रयास के लिए प्रतीक्षा समय बढ़ा सकते हैं।
$ 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 `<main>'
कोड छोड़ने से पहले हमारे अनुरोध का तीन बार पुन:प्रयास किया जाता है और अंतिम त्रुटि को फिर से उठाया जाता है। फिर हम त्रुटि को एक स्तर ऊपर संभाल सकते हैं, या हमारे ऐप को क्रैश कर सकते हैं यदि यह एपीआई की प्रतिक्रिया के बिना अपना काम पूरा नहीं कर सकता है।
एक लचीला वेब API क्लाइंट
इन विधियों को मिलाकर, हमने कोड की लगभग बीस पंक्तियों में एक लचीला वेब एपीआई क्लाइंट बनाया है। यदि यह डाउन या अनुत्तरदायी है, तो यह अनुरोधों का पुन:प्रयास करेगा, और जब यह फिर से वापस नहीं आएगा तो हम इसे छोड़ देंगे।
हमें उम्मीद है कि आपने अपवादों को संभालने के बारे में कुछ नया सीखा है और यह जानना अच्छा लगेगा कि आपने इस लेख के बारे में क्या सोचा (या ऐपसिग्नल अकादमी श्रृंखला में से कोई भी)। कृपया हमें यह बताने में संकोच न करें कि आप क्या सोचते हैं, या यदि आपके पास कोई रूबी विषय है जिसके बारे में आप अधिक जानना चाहते हैं।