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

रूबी कंकरेंसी में गहरा गोता: मल्टी-प्रोसेस तकनीकों में महारत

मास्टरिंग कंकरेंसी पर पिछले रूबी मैजिक लेख में, हमने हमें रूबी डेवलपर्स के रूप में उपलब्ध कंकरेंसी प्राप्त करने के तीन तरीकों का परिचय दिया। यह लेख तीन-भाग श्रृंखला में पहला है जहां हम प्रत्येक विधि में गहराई से गोता लगाते हैं।

पहला: मल्टी-प्रोसेस। इस विधि के साथ एक मास्टर प्रोसेस खुद को कई वर्कर प्रोसेस में फोर्क करता है। वर्कर प्रोसेस वास्तविक काम करता है, जबकि मास्टर वर्कर्स को प्रबंधित करता है।

इस लेख में उदाहरणों में उपयोग किया गया पूरा सोर्स कोड GitHub पर उपलब्ध है, ताकि आप स्वयं इसके साथ प्रयोग कर सकें।

आइए एक चैट सिस्टम बनाएं!

चैट सिस्टम बनाना कंकरेंसी में गोता लगाने का एक अच्छा तरीका है। हमें चैट सिस्टम के एक सर्वर घटक की आवश्यकता होगी जो कई क्लाइंट के साथ कनेक्शन बनाए रखने में सक्षम हो। यह हमें एक क्लाइंट से प्राप्त संदेशों को अन्य सभी जुड़े क्लाइंट को वितरित करने की अनुमति देगा।

हमारा चैट सर्वर बाएं टैब में चल रहा है। दाएं टैब में दो चैट क्लाइंट चल रहे हैं। एक क्लाइंट द्वारा भेजा गया कोई भी संदेश अन्य सभी क्लाइंट द्वारा प्राप्त किया जाएगा।

चैट क्लाइंट

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

# client.rb
# $ ruby client.rb
require 'socket'
client = TCPSocket.open(ARGV[0], 2000)
Thread.new do
  while line = client.gets
    puts line.chop
  end
end
while input = STDIN.gets.chomp
  client.puts input
end

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

चैट सर्वर

इस उदाहरण में एक क्लाइंट अन्य क्लाइंट के साथ संवाद करने के लिए चैट सर्वर से जुड़ता है। सभी तीन कंकरेंसी दृष्टिकोणों के लिए हम रूबी के मानक पुस्तकालय से उसी TCP सर्वर का उपयोग करेंगे।

# server_processes.rb
# $ ruby server_processes.rb
require 'socket'
puts 'Starting server on port 2000'
server = TCPServer.open(2000)

इस बिंदु तक कोड सभी तीन कंकरेंसी मॉडल के लिए समान है। प्रत्येक मॉडल में चैट सर्वर को तब दो परिदृश्यों को संभालने की आवश्यकता होगी:

  1. क्लाइंट से नए कनेक्शन स्वीकार करें।
  2. क्लाइंट से संदेश प्राप्त करें और उन्हें अन्य सभी क्लाइंट को भेजें।

मल्टी-प्रोसेस चैट सर्वर

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

प्रोसेस फोर्क करना

जब आप fork मेथड को कॉल करते हैं, तो यह वर्तमान प्रोसेस की एक प्रति बनाता है जिसमें ठीक वही स्थिति होती है जिसमें प्रोसेस होता है।

एक फोर्क्ड प्रोसेस की अपनी प्रोसेस आईडी होती है, और top या एक्टिविटी मॉनिटर जैसे टूल में अलग से दिखाई देगा। यह कुछ इस तरह दिखता है:

जिस प्रोसेस से आप शुरू करते हैं उसे मास्टर प्रोसेस कहा जाता है, और मास्टर प्रोसेस से फोर्क किए गए प्रोसेस को वर्कर प्रोसेस कहा जाता है।

चूंकि ये नए फोर्क्ड वर्कर प्रोसेस वास्तव में अलग प्रोसेस हैं, हम उनके और मास्टर प्रोसेस के बीच मेमोरी साझा नहीं कर सकते। हमें उनके बीच संवाद करने के लिए कुछ चाहिए।

यूनिक्स पाइप

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

निम्नलिखित उदाहरण में हम रूबी में एक पाइप सेट करते हैं - एक पढ़ने और एक लिखने वाले सिरे के साथ - और हम मास्टर प्रोसेस को fork करते हैं। fork को पास किए गए ब्लॉक के भीतर का कोड फोर्क्ड प्रोसेस में चल रहा है। मूल प्रोसेस इस ब्लॉक के बाद जारी रहता है। फिर हम फोर्क्ड प्रोसेस से मूल प्रोसेस को एक संदेश लिखते हैं।

reader, writer = IO.pipe
fork do
  # यह फोर्क्ड प्रोसेस में चल रहा है।
  writer.puts 'Hello from the forked process'
end
# यह मूल प्रोसेस में चल रहा है, यह फोर्क्ड प्रोसेस से संदेश को puts करेगा।
puts reader.gets

पाइप का उपयोग करके हम पूरी तरह से एक दूसरे से अलग होने के बावजूद अलग प्रोसेस के बीच संवाद कर सकते हैं।

चैट सर्वर का कार्यान्वयन

पहले हम सभी क्लाइंट के लिए पाइप और उनके "राइटर्स" (पाइप का लिखने वाला सिरा) को ट्रैक करने के लिए एक सरणी सेट करते हैं, ताकि हम क्लाइंट के साथ संवाद कर सकें। फिर हम सुनिश्चित करते हैं कि क्लाइंट से आने वाले सभी संदेश अन्य सभी क्लाइंट को भेजे जाते हैं।

client_writers = []
master_reader, master_writer = IO.pipe
write_incoming_messages_to_child_processes(master_reader, client_writers)

यदि आप देखना चाहते हैं कि यह कैसे काम करता है तो write_incoming_messages_to_child_processes का कार्यान्वयन GitHub पर पा सकते हैं।

नए कनेक्शन स्वीकार करना

हमें आने वाले कनेक्शन स्वीकार करने और पाइप सेट करने की आवश्यकता होगी। नए राइटर को client_writers सरणी पर पुश किया जाएगा। मुख्य प्रोसेस सरणी के माध्यम से लूप करने और प्रत्येक वर्कर प्रोसेस को उसके पाइप पर लिखकर संदेश भेजने में सक्षम होगा।

फिर हम मास्टर प्रोसेस को फोर्क करते हैं, और फोर्क्ड वर्कर प्रोसेस के भीतर का कोड क्लाइंट कनेक्शन को संभालेगा।

loop do
  while socket = server.accept
    # एक क्लाइंट रीडर और राइटर बनाएं ताकि मास्टर
    # प्रोसेस हमें वापस संदेश लिख सके।
    client_reader, client_writer = IO.pipe
    # क्लाइंट राइटर को राइटर्स की सूची पर रखें ताकि
    # मास्टर प्रोसेस उन्हें लिख सके।
    client_writers.push(client_writer)
    # चाइल्ड प्रोसेस फोर्क करें, फोर्क ब्लॉक के भीतर सब कुछ
    # केवल चाइल्ड प्रोसेस में चलता है।
    fork do
      # कनेक्शन संभालें
    end
  end
end

क्लाइंट कनेक्शन संभालना

हमें क्लाइंट कनेक्शन को भी संभालने की आवश्यकता है।

फोर्क्ड प्रोसेस क्लाइंट से निकनेम प्राप्त करके शुरू होता है (क्लाइंट डिफ़ॉल्ट रूप से निकनेम भेजता है)। उसके बाद यह write_incoming_messages_to_client में एक थ्रेड शुरू करता है जो मुख्य प्रोसेस से संदेशों को सुनता है।

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

nickname = read_line_from(socket)
puts "#{Process.pid}: Accepted connection from #{nickname}"
write_incoming_messages_to_client(nickname, client_reader, socket)
# क्लाइंट से आने वाले संदेश पढ़ें।
while incoming = read_line_from(socket)
  master_writer.puts "#{nickname}: #{incoming}"
end
puts "#{Process.pid}: Disconnected #{nickname}"

एक कार्यशील चैट सिस्टम

अब पूरा चैट सिस्टम काम करता है! लेकिन जैसा कि आप देख सकते हैं, मल्टीप्रोसेसिंग का उपयोग करने वाला प्रोग्राम लिखना काफी जटिल है और बहुत सारे संसाधनों का उपयोग करता है। उल्टा यह है कि यह बहुत मजबूत है। यदि चाइल्ड प्रोसेस में से एक क्रैश हो जाता है तो बाकी सिस्टम बस काम करता रहता है। आप उदाहरण कोड चलाकर और kill -9 <process-id> को प्रोसेस में से एक पर चलाकर इसे आज़मा सकते हैं (आप सर्वर के लॉग आउटपुट में प्रोसेस आईडी पा सकते हैं)।

अगले लेख में हम उसी चैट सिस्टम को केवल थ्रेड का उपयोग करके लागू करेंगे, ताकि हम एक ही प्रोसेस और कम मेमोरी का उपयोग करके समान सुविधाओं वाला सर्वर चला सकें।

  1. पोर्ट पर प्रोसेस कैसे मारें? पोर्ट पर प्रोसेस कैसे मारें?

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

  1. LSM.EXE क्या है? क्या यह वायरस है? LSM.EXE क्या है? क्या यह वायरस है?

    कुछ उपयोगकर्ता यह सोच रहे हैं कि lsm.exe वास्तविक है या दुर्भावनापूर्ण यह पता लगाने के बाद कि प्रक्रिया टास्क मैनेजर में एक स्थिर उपस्थिति है और काफी मात्रा में सिस्टम संसाधनों का उपभोग करती है।हालांकि इस बात की बहुत संभावना है कि प्रक्रिया वास्तव में वैध है, उपयोगकर्ताओं को नीचे निर्दिष्ट जांच करने

  1. रूबी में यूनिक्स डेमॉन को समझें: एक सैद्धांतिक गाइड रूबी में यूनिक्स डेमॉन को समझें: एक सैद्धांतिक गाइड

    यूनिक्स डेमॉन वे प्रोग्राम होते हैं जो बैकग्राउंड में चलते हैं। Nginx, Postgres और OpenSSH कुछ उदाहरण हैं। वे अपने प्रोसेस को डिटैच करने के लिए कुछ विशेष तरकीबों का उपयोग करते हैं, और उन्हें किसी भी टर्मिनल से स्वतंत्र रूप से चलने देते हैं। मैं हमेशा से डेमॉन के प्रति थोड़ा आकर्षित रहा हूँ - शायद ना