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

ऐपसिग्नल मेट्रिक्स के साथ कुशल रूप से स्केलिंग कतार कार्यकर्ता

अधिकांश वेब ऐप्स पृष्ठभूमि कतार से लाभान्वित हो सकते हैं, जिसका उपयोग अक्सर त्रुटि-प्रवण या समय लेने वाली साइड जॉब को संसाधित करने के लिए किया जाता है। ये पृष्ठभूमि कार्य ईमेल भेजने से लेकर कैश अपडेट करने तक, मुख्य व्यावसायिक तर्क प्रदर्शन करने तक भिन्न हो सकते हैं।

चूंकि कोई भी पृष्ठभूमि कतार प्रणाली उन नौकरियों की संख्या को मापती है जिन्हें संसाधित करने की आवश्यकता होती है, उन नौकरियों को संसाधित करने वाले श्रमिकों के पूल को भी स्केल करने की आवश्यकता होती है। ऐसे मामलों में जहां नौकरियों की दर अलग-अलग होती है, कतार श्रमिकों की संख्या को बढ़ाना एक महत्वपूर्ण पहलू बन जाता है। प्रसंस्करण गति बनाए रखने में। इसके अतिरिक्त, कम कतार थ्रूपुट के दौरान श्रमिकों को कम करने से महत्वपूर्ण बचत मिल सकती है!

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

अंगूठे का कतार नियम

यदि नौकरियों को कतार के श्रमिकों द्वारा संसाधित किए जाने की तुलना में उच्च दर पर कतारबद्ध किया जाता है, तो कतार की गहराई बढ़ेगी और कतार में प्रत्येक कार्य द्वारा खर्च किया जाने वाला समय भी बढ़ेगा। आम तौर पर, हम प्रतीक्षा समय चाहते हैं (समय की मात्रा में प्रत्येक कार्य को यथासंभव कम करने के लिए - 0 सेकंड से कुछ स्वीकार्य सीमा तक। वांछित प्रतीक्षा समय को पूरा करने के लिए आवश्यक श्रमिकों की संख्या का अनुमान लगाने के लिए, हम अंगूठे के कतार नियम (QROT) का उपयोग कर सकते हैं। आमतौर पर, क्यूआरओटी को नौकरियों की कतार की सेवा के लिए आवश्यक सर्वरों की संख्या का वर्णन करने वाली असमानता के रूप में व्यक्त किया जाता है, लेकिन एक फॉर्म को इस प्रकार लिखा जा सकता है:

workers = (number_of_jobs * avg_service_time_per_job) / time_to_finish_queue

इसलिए, यदि हम 30 सेकंड के वांछित समय में अपनी कतार की सेवा के लिए आवश्यक श्रमिकों की संख्या का पता लगाना चाहते हैं, तो हमें केवल नौकरियों की संख्या (कतार का आकार) और औसत समय जानने की आवश्यकता है। प्रत्येक कार्य को निष्पादित करें। उदाहरण के लिए, यदि हमारे पास 7500 नौकरियों की कतार है और प्रत्येक कार्य को निष्पादित करने में औसतन 0.3 सेकंड का समय लगता है, तो हम उस कतार को 75 श्रमिकों के साथ 30 सेकंड में समाप्त कर सकते हैं।

प्रदर्शन मीट्रिक एक्सेस करना

कतार में नौकरियों के लिए औसत सेवा समय का अनुमान लगाने के लिए, हमें प्रत्येक नौकरी वर्ग के लिए प्रदर्शन मीट्रिक तक पहुंच की आवश्यकता होती है। सौभाग्य से, ऐपसिग्नल सामान्य कतार बैकएंड के प्रदर्शन डेटा को आउट-ऑफ-द-बॉक्स रिकॉर्ड करता है, प्रत्येक बार के लिए मीट्रिक रिकॉर्ड करता है कार्य निष्पादित किया गया है।

हम पिछले 24 घंटों में प्रत्येक कार्य प्रकार की औसत अवधि प्राप्त करने के लिए आगामी ऐपसिग्नल ग्राफ़क्यूएल एपीआई का उपयोग कर सकते हैं। यह एपीआई अभी तक पूरी तरह से सार्वजनिक नहीं है, हालांकि वर्तमान में इसका उपयोग ऐपसिग्नल के प्रदर्शन ग्राफ और अन्य डेटा डिस्प्ले के लिए किया जाता है। सौभाग्य से, ग्राफक्यूएल एपीआई हैं स्व-दस्तावेजीकरण करने का इरादा है, और हम एपीआई का आत्मनिरीक्षण करने के लिए ग्राफीक्यूएल जैसे टूल का उपयोग कर सकते हैं और यह पता लगा सकते हैं कि यह किन डेटा ऑब्जेक्ट्स को उजागर करता है।

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

require 'json'
require 'faraday'
 
class AppsignalClient
  BASE_URL = 'https://appsignal.com/'
  DEFAULT_APP_ID = ENV['APPSIGNAL_APP_ID']
  DEFAULT_TOKEN = ENV['APPSIGNAL_API_TOKEN']
  # GraphQL query to fetch the "mean" metric for the selected app.
  METRICS_QUERY = <<~GRAPHQL.freeze
    query($appId: String!, $query: [MetricAggregation!]!, $timeframe: TimeframeEnum!) {
      app(id: $appId) {
        metrics {
          list(timeframe: $timeframe, query: $query) {
            start
            end
            rows {
              fields {
                key
                value
              }
            }
          }
        }
      }
    }
  GRAPHQL
 
  def initialize(app_id: DEFAULT_APP_ID, client_secret: DEFAULT_TOKEN)
    @app_id = app_id
    @client_secret = client_secret
  end
 
  # Fetch the average duration for a job class's perform action
  # Default timeframe is last 24 hours
  def average_job_duration(job_class, timeframe: 'R24H')
    response =
      connection.post(
        'graphql',
        JSON.dump(
          query: METRICS_QUERY,
          variables: {
            appId: @app_id,
            timeframe: timeframe,
            query: [
              name: 'transaction_duration',
              headerType: legacy
tags: [
                { key: 'namespace', value: 'background' },
                { key: 'action', value: "#{job_class.name}#perform" },
              ],
              fields: [{ field: 'MEAN', aggregate: 'AVG' }],
            ],
          }
        )
      )
    data = JSON.parse(response.body, symbolize_names: true)
    rows = data.dig(:data, :app, :metrics, :list, :rows)
    # There may be no metrics in the selected timeframe
    return 0.0 if rows.empty?
 
    rows.first[:fields].first[:value]
  end
 
  private
 
  def connection
    @connection ||= Faraday.new(
      url: BASE_URL,
      params: { token: @client_secret },
      headers: { 'Content-Type' => 'application/json' },
      request: { timeout: 10 }
    ) do |faraday|
      faraday.response :raise_error
      faraday.adapter Faraday.default_adapter
    end
  end
end

इस वर्ग के साथ, हम किसी दिए गए ActiveJob वर्ग के लिए औसत कार्य अवधि प्राप्त कर सकते हैं, जो हमें मिलीसेकंड में लौटा दी जाती है:

AppsignalClient.new.average_job_duration(MyMailerJob)
# => 233.1

डिफ़ॉल्ट रूप से, यह डेटा के पिछले 24 घंटों में कार्य की औसत लेनदेन अवधि की मांग करता है। यदि हमारे कार्य (कार्यों) को उससे अधिक बार निष्पादित किया जाता है, तो हम उस विंडो को छोटा करना चाह सकते हैं, जो हमारे हाल के निष्पादनों को अधिक महत्व देता है। औसत। उदाहरण के लिए, यदि हमारे पास ऐसे कार्य हैं जो एक घंटे में सैकड़ों बार चलते हैं, तो हम अपनी timeframe बदलना चाह सकते हैं। एक घंटे तक (R1H ) इस तरह के एक काम की अवधि का बेहतर अनुमान लगाने के लिए अगर अभी निष्पादित किया जाता है।

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

कतार का आत्मनिरीक्षण करना

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

require 'resque'
 
class ResqueEstimator
  def initialize(queue: 'default')
    @queue = queue
    @cache = {}
    @appsignal_client = AppsignalClient.new
  end
 
  def enqueued_duration_estimate
    Resque.data_store.everything_in_queue(queue).map do |job|
      estimate_job_duration decode_activejob_args(job)
    end.sum
  end
 
  def estimate_job_duration(job)
    @cache[job['job_class']] ||= @appsignal_client
                                 .average_job_duration job['job_class']
  end
 
  private
 
  # ActiveJob-specific method for parsing job arguments
  # for ActiveJob+Resque integration
  def decode_activejob_args(job)
    decoded_job = job
    decoded_job = Resque.decode(job) if job.is_a? String
    decoded_job['args'].first
  end
end

इस वर्ग का उपयोग करना उतना ही सरल है:

ResqueEstimator.new(queue: 'my_queue').enqueued_duration_estimate
# => 23000 (ms)

ध्यान दें कि हम अपने estimate_job_duration में नौकरी की अवधि के एक साधारण संस्मरण का उपयोग करते हैं AppSignal API पर डुप्लिकेट कॉल से बचने के लिए विधि। सबसे अधिक संभावना है, हमारी कतार में एक ही वर्ग के कई कार्य होंगे और हम केवल एक बार प्रत्येक वर्ग के निष्पादन का अनुमान लगाकर अपने ओवरहेड को कम कर सकते हैं।

प्रदर्शन डेटा को स्केल करने के लिए उपयोग करना

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

हमें एक वांछित कतारबद्ध समय सीमा (किसी भी कार्य को कतार में प्रतीक्षा करने के लिए अधिकतम समय) पर निर्णय लेने की आवश्यकता होगी, उदा। 30 सेकंड। हमें न्यूनतम और अधिकतम कार्यकर्ता संख्या भी निर्दिष्ट करने की आवश्यकता होगी। कतार के खाली होने के बाद कतार में पहली नौकरी को संभालने के लिए, कतार के लिए कम से कम एक कार्यकर्ता को चलाने में मददगार है। हम करेंगे हमारे डेटाबेस कनेक्शन और/या सर्वर उपयोग लागत को बहुत अधिक श्रमिकों के साथ बढ़ाने से बचने के लिए अधिकतम कार्यकर्ता संख्या भी चाहते हैं।

हम अपने लिए इस तर्क को संभालने के लिए एक वर्ग बना सकते हैं, जो मूल रूप से पहले से केवल हमारे अंगूठे के कतार नियम का कार्यान्वयन है।

class ResqueWorkerScaler
  def initialize(queue: 'default', workers_range: 1..100, desired_wait_ms: 300_000)
    @queue = queue
    @workers_range = workers_range
    @desired_wait_ms = desired_wait_ms
    @estimator = ResqueEstimator.new(queue: @queue)
  end
 
  def desired_workers
    total_time_ms = @estimator.enqueued_duration_estimate
    workers_required = [(total_time_ms / desired_wait_ms).ceil, workers_range.last].min
    [workers_required, workers_range.first].max
  end
 
  def scale
    # using platform-specific scaling interface, scale to desired_workers
  end
end

हम अपने कर्मचारियों को नियमित अंतराल पर स्केल करना चाहेंगे ताकि हम मांग के आधार पर ऊपर और नीचे स्केलिंग कर सकें। हम एक रेक टास्क बना सकते हैं जो हमारे ResqueWorkerScaler . को कॉल करता है वर्ग से पैमाने के कर्मचारी:

# inside lib/tasks/resque_workers.rake
 
namespace :resque_workers do
  desc 'Scale worker pool based on enqueued jobs'
  task :scale, [:queue] => [:environment] do |_t, args|
    queue = args[:queue] || 'default'
    ResqueWorkerScaler.new(queue: queue).scale
  end
end

और फिर हम इस रेक कार्य को नियमित अंतराल पर चलाने के लिए क्रॉन जॉब सेट कर सकते हैं:

*/5 * * * * /path/to/our/rake resque_workers:scale
# scale a non-default queue:
*/5 * * * * /path/to/our/rake resque_workers:scale['my_queue']

ध्यान दें कि हमने स्केलिंग कार्य को हर 5 मिनट में चलाने के लिए सेट किया है। प्रत्येक नए कार्यकर्ता को ऑनलाइन आने और कार्य संसाधित करने में कुछ समय लगेगा - हमारे कोडबेस के आकार और रत्नों की संख्या के आधार पर 10-40 सेकंड से कहीं भी संभव है। हम उपयोग करते हैं। इसलिए, यदि हम अपने कर्मचारियों को हर मिनट स्केल करने का प्रयास करते हैं, तो हमारे वांछित परिवर्तनों के प्रभावी होने से पहले हम फिर से ऊपर या नीचे स्केलिंग करेंगे। यदि हमारा ऐप दिन के अलग-अलग समय में केवल कतार के उपयोग में उतार-चढ़ाव देख रहा है, तो हम कर सकते हैं हमारे रेक कार्य को एक घंटे के अंतराल पर कॉल करने की संभावना है। लेकिन अगर हमारी कतार का आकार घंटे के भीतर बदलता रहता है, तो हम अपनी कतार को अधिक लगातार अंतराल पर आत्मनिरीक्षण करना चाहेंगे, जैसे कि ऊपर 5 मिनट।

अगले चरण

ऐसी प्रणाली जहां वास्तविक प्रदर्शन डेटा का उपयोग बुनियादी ढांचे को स्केल करने के लिए किया जाता है, मांग के प्रति बहुत प्रतिक्रियाशील हो सकता है और विभिन्न उपयोगों के लिए लचीला हो सकता है। विशेष रूप से पृष्ठभूमि प्रसंस्करण जैसे वातावरण में, जहां मेमोरी उपयोग और लोड औसत जैसे मेजबान मीट्रिक भिन्न होने की संभावना नहीं है, प्रदर्शन मेट्रिक्स का उपयोग करके पैमाना अधिक उपयुक्त है।

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

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

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

कतारबद्ध प्रणालियाँ किसी भी परियोजना में बहुत अधिक परिवर्तनशील कार्य एकत्र करती हैं। कतार से कार्यों के निष्पादन पर प्रदर्शन डेटा के साथ, हम एक प्रतिक्रियाशील, कुशल तरीके से सभी काम करने के लिए संसाधनों को प्रभावी ढंग से माप सकते हैं।

हैप्पी स्केलिंग!

पी.एस. यदि आप रूबी मैजिक की पोस्ट प्रेस से छूटते ही पढ़ना चाहते हैं, तो हमारे रूबी मैजिक न्यूजलेटर की सदस्यता लें और एक भी पोस्ट मिस न करें!


  1. MongoDB के साथ बड़े पैमाने पर स्केलिंग का परिचय

    मूल रूप से 4 जून 2018 को ObjectRocket.com/blog पर प्रकाशित हम स्केलिंग . को परिभाषित कर सकते हैं मछली से तराजू को हटाने के रूप में। हालांकि, डेटाबेस के साथ, स्केलिंग भंडारण, डिस्क, रैम, सीपीयू, गणना चक्र, नेटवर्किंग, या अन्य संसाधनों के आसपास अतिरिक्त जरूरतों को पूरा करने के लिए विस्तार करने की क्

  1. MongoDB के साथ स्केलिंग:एक शार्डिंग इन्फ्रास्ट्रक्चर की स्थापना

    हाल ही में एक ब्लॉग पोस्ट में, मैंने चर्चा की कि आपको MongoDB को कब स्केल करना है। इस पोस्ट में, मोंगोडीबी को स्केल करने के तरीके पर ध्यान केंद्रित किया गया है। MongoDB संस्करण 3.0 ने WiredTiger को डिफ़ॉल्ट स्टोरेज इंजन के रूप में पेश किया। स्केलेबिलिटी की बात करें तो MongoDB दो दृष्टिकोण प्रदान कर

  1. रेडिस @ एज विद क्लाउडफ्लेयर वर्कर्स

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