কম্পিউটার

মৃত্যুদন্ড নিশ্চিত করা, ব্যর্থতার পুনরায় চেষ্টা করা এবং রুবিতে ব্যতিক্রমগুলি পুনরুদ্ধার করা

উত্থাপিত ব্যতিক্রমগুলি যখন কিছু ভুল হয়ে যায় তখন একটি বিকল্প কোড পথ চালানোর জন্য উদ্ধার করা যেতে পারে, তবে ব্যতিক্রমগুলি পরিচালনা করার আরও উপায় রয়েছে। AppSignal একাডেমির এই সংস্করণে, আমরা retry করব এবং ensure কীওয়ার্ড, এবং আমরা উদ্ধারকৃত ব্যতিক্রমগুলি পুনরুদ্ধারের দিকে নজর দেব।

আসুন আমরা ভান করি যে আমরা একটি অবিশ্বস্ত ওয়েব API এর সাথে যোগাযোগ করছি৷ একে একে একে একে নিচে নেমে যাওয়া ছাড়াও, এটি এতই ধীর যে এটির অনুরোধে কয়েক সেকেন্ড সময় লাগতে পারে। আমাদের লাইব্রেরি এই API এর উপর নির্ভর করে, এবং আমাদের এটিকে যতটা সম্ভব স্থিতিস্থাপক করতে হবে।

ensure

ensure কীওয়ার্ড নিশ্চিত করার জন্য ব্যবহৃত হয় কোডের একটি ব্লক চলে, এমনকি একটি ব্যতিক্রম ঘটলেও।

আমাদের লাইব্রেরিতে, আমরা নিশ্চিত করতে চাই যে TCP সংযোগটি Net::HTTP.start দ্বারা খোলা হয়েছে বন্ধ আছে, এমনকি যদি অনুরোধটি ব্যর্থ হয় কারণ এটির সময় শেষ হয়, উদাহরণস্বরূপ। এটি করার জন্য, আমরা প্রথমে আমাদের অনুরোধটি একটি 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 অপসারণ করতে পুনরায় লেখা যেতে পারে . মজার ব্যাপার হল, নিশ্চিত ব্লক হল এটি কীভাবে Net::HTTP-এ প্রয়োগ করা হয়।

retry

retry কীওয়ার্ড একটি ব্লকে কোডের একটি অংশ পুনরায় চেষ্টা করার অনুমতি দেয়। একটি rescue এর সাথে মিলিত ব্লক করুন, আমরা সংযোগ খুলতে ব্যর্থ হলে আবার চেষ্টা করার জন্য এটি ব্যবহার করতে পারি, অথবা যদি API প্রতিক্রিয়া জানাতে খুব বেশি সময় নেয়।

এটি করতে, আমরা একটি 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)

যদিও এটি নিশ্চিত করতে পারে যে কোনও টাইমআউটের জন্য কোনও ব্যতিক্রম উত্থাপিত হবে না, তবে এটিকে আবার চেষ্টা-হ্যামার করা অবশ্যই সেই API আবার ব্যাক আপ পেতে সাহায্য করবে না। এটি সমস্যাযুক্ত কারণ এপিআই যদি প্রতিক্রিয়াহীন থাকে তবে এই কোডটি চিরতরে লুপ থাকবে। পরিবর্তে, আমাদের পুনরায় চেষ্টা ছড়িয়ে দেওয়া উচিত এবং কিছুক্ষণ পরে ছেড়ে দেওয়া উচিত।

ত্যাগ করা:raise ব্যবহার করে ব্যতিক্রমগুলি পুনরায় উত্থাপন করা

যখন একটি ব্যতিক্রম উদ্ধার করা হয়, উত্থাপিত ব্যতিক্রম বস্তুটি rescue এ পাঠানো হয় ব্লক আমরা এটিকে ব্যতিক্রম থেকে ডেটা বের করতে ব্যবহার করতে পারি, যেমন লগে বার্তাটি প্রিন্ট করার জন্য, কিন্তু আমরা একই স্ট্যাক ট্রেস সহ, একই ব্যতিক্রমটি পুনরুদ্ধার করতেও এটি ব্যবহার করতে পারি।

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

যেহেতু আমাদের rescue-এ ব্যতিক্রম বস্তুতে অ্যাক্সেস আছে ব্লক, আমরা কনসোল বা একটি ত্রুটি মনিটরে ত্রুটি লগ করতে পারি। প্রকৃতপক্ষে, AppSignal এর ইন্টিগ্রেশন ত্রুটিগুলিকে ট্র্যাক করে ঠিক কীভাবে উদ্ধার করা এবং পুনরুত্থান করা হয়৷

দ্রষ্টব্য :রুবি সর্বশেষ উত্থাপিত ব্যতিক্রমটিকে $! নামের একটি ভেরিয়েবলে সঞ্চয় করে , এবং raise কীওয়ার্ড এটি ডিফল্টরূপে ব্যবহার করবে। কল করা হচ্ছে raise কোনো যুক্তি ছাড়াই শেষ ব্যতিক্রম পুনরায় উত্থাপন করবে।

আমাদের লাইব্রেরিতে, আমরা কয়েকবার পুনঃপ্রচেষ্টার পর এপিআই-এর চাপ নিতে রিরাইজিং ব্যবহার করতে পারি। এটি করার জন্য, আমরা retry-এ কতগুলি পুনঃপ্রচার করেছি তার ট্র্যাক রাখব। পরিবর্তনশীল।

যখনই একটি টাইমআউট ঘটবে, আমরা সংখ্যাটি বৃদ্ধি করব এবং এটি তিনটির কম বা সমান কিনা তা পরীক্ষা করব, কারণ আমরা সর্বাধিক তিনটি পুনরায় চেষ্টা করতে চাই। যদি তাই হয়, আমরা 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

retry ব্যবহার করে 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 এর প্রতিক্রিয়া ছাড়াই কাজটি শেষ করতে না পারলে আমাদের অ্যাপটি ক্র্যাশ করতে পারি।

একটি স্থিতিস্থাপক ওয়েব API ক্লায়েন্ট

এই পদ্ধতিগুলিকে একত্রিত করে, আমরা প্রায় বিশ লাইনের কোডে একটি স্থিতিস্থাপক ওয়েব API ক্লায়েন্ট তৈরি করেছি। এটি বন্ধ বা প্রতিক্রিয়াশীল না হলে এটি অনুরোধগুলি পুনরায় চেষ্টা করবে এবং এটি আবার ফিরে না আসলে আমরা ছেড়ে দেব৷

আমরা আশা করি আপনি ব্যতিক্রমগুলি পরিচালনা করার বিষয়ে নতুন কিছু শিখেছেন এবং আপনি এই নিবন্ধটি (বা AppSignal একাডেমী সিরিজের অন্য যেকোনও) সম্পর্কে কী ভেবেছেন তা জানতে পছন্দ করবেন। অনুগ্রহ করে আপনি কি মনে করেন তা আমাদের জানাতে দ্বিধা করবেন না, অথবা যদি আপনার কোন রুবি বিষয় থাকে যা সম্পর্কে আপনি আরও জানতে চান।


  1. RuboCop সহ রুবি কোড লিন্টিং এবং স্বয়ংক্রিয় ফর্ম্যাটিং

  2. রুবিতে কাস্টম ব্যতিক্রম

  3. রুবিতে ব্যতিক্রমগুলিতে প্রসঙ্গ ডেটা কীভাবে যুক্ত করবেন

  4. Logger এবং Lograge সঙ্গে রুবি লগ ইন করুন