রুবি ম্যাজিকের আগের একটি আর্টিকেলে মাস্টারিং কাঙ্কারেন্সি-এ, আমরা রুবি ডেভেলপারদের জন্য উপলব্ধ তিনটি কাঙ্কারেন্সি অর্জনের পদ্ধতির একটি ইন্ট্রোডাকশন দিয়েছিলাম। এই আর্টিকেল তিন-পার্ট সিরিজের প্রথম যেখানে আমরা প্রতিটি পদ্ধতিতে গভীরে যাব।
প্রথম: মাল্টি-প্রসেস। এই পদ্ধতিতে একটি মাস্টার প্রসেস নিজেকে একাধিক ওয়ার্কার প্রসেসে ফর্ক করে। ওয়ার্কার প্রসেস আসল কাজ করে, যখন মাস্টার ওয়ার্কারদের ম্যানেজ করে।
এই আর্টিকেলের উদাহরণগুলোর ফুল সোর্স কোড গিটহাবে উপলব্ধ, যাতে আপনি নিজে এক্সপেরিমেন্ট করতে পারেন।
একটি চ্যাট সিস্টেম বানাই!
একটি চ্যাট সিস্টেম বানানো কাঙ্কারেন্সি-তে গোতা লাগার একটি ভালো উপায়। আমাদের একটি চ্যাট সিস্টেমের সার্ভার কম্পোনেন্ট দরকার যা একাধিক ক্লায়েন্টের সাথে কানেকশন মেইনটেইন করতে পারে। এটা আমাদের এক ক্লায়েন্ট থেকে প্রাপ্ত মেসেজ সব অন্য কানেক্টেড ক্লায়েন্টে ডিস্ট্রিবিউট করতে দেবে।
আমাদের চ্যাট সার্ভার বাম ট্যাবে রান করছে। দুটি চ্যাট ক্লায়েন্ট ডান ট্যাবে রান করছে। কোনো ক্লায়েন্ট দ্বারা পাঠানো যেকোনো মেসেজ সব অন্য ক্লায়েন্টে প্রাপ্ত হবে।
চ্যাট ক্লায়েন্ট
এই আর্টিকেল ফোকাস করে চ্যাট সার্ভারের ওপর, কিন্তু সেটির সাথে কমিউনিকেট করার জন্য আমাদের একটি চ্যাট ক্লায়েন্ট আগে দরকার। নিচের কোড আমাদের খুব সিম্পল ক্লায়েন্ট হবে। (একটি আরও কম্প্লিট উদাহরণ গিটহাবে পাওয়া যাবে।)
# 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
ক্লায়েন্ট পোর্ট ২০০০-এ রান করা একটি সার্ভারের সাথে TCP কানেকশন খুলে। কানেক্ট হলে, সেটি একটি থ্রেড স্পন করে যা সার্ভার যা পাঠাবে সেটি puts করবে, যাতে চ্যাট টার্মিনাল আউটপুটে見える। অবশেষে, একটি হুইল লুপ আছে যা আপনি যা টাইপ করবেন সেটি সার্ভারে পাঠাবে, যেটি সব অন্য কানেক্টেড ক্লায়েন্টে পাঠাবে।
চ্যাট সার্ভার
এই উদাহরণে একটি ক্লায়েন্ট একটি চ্যাট সার্ভারের সাথে কানেক্ট করে অন্য ক্লায়েন্টের সাথে কমিউনিকেট করার জন্য। সব তিনটি কাঙ্কারেন্সি মডেলের জন্য আমরা রুবির স্ট্যান্ডার্ড লাইব্রেরি থেকে একই TCP সার্ভার ব্যবহার করব।
# server_processes.rb
# $ ruby server_processes.rb
require 'socket'
puts 'Starting server on port 2000'
server = TCPServer.open(2000)
এই পয়েন্ট পর্যন্ত কোড সব তিনটি কাঙ্কারেন্সি মডেলের জন্য একই। চ্যাট সার্ভার প্রতিটি মডেলে তারপর দুটি সিনেরিও হ্যান্ডল করতে হবে:
- ক্লায়েন্ট থেকে নতুন কানেকশন এক্সেপ্ট করা।
- ক্লায়েন্ট থেকে মেসেজ প্রাপ্ত করা এবং সেগুলো সব অন্য ক্লায়েন্টে পাঠানো।
একটি মাল্টি-প্রসেস চ্যাট সার্ভার
এই দুটি সিনেরিও মাল্টি-প্রসেস চ্যাট সার্ভারের সাথে হ্যান্ডল করার জন্য, আমরা প্রতিটি ক্লায়েন্ট কানেকশনের জন্য একটি প্রসেস স্পন করব। এই প্রসেস সেটি ক্লায়েন্টের জন্য পাঠানো এবং প্রাপ্ত সব মেসেজ হ্যান্ডল করবে। আমরা মূল সার্ভার প্রসেস ফর্ক করে এই প্রসেসগুলো তৈরি করতে পারি।
প্রসেস ফর্ক করা
যখন আপনি fork মেথড কল করেন, এটি বর্তমান প্রসেসের একটি কপি তৈরি করে একই স্টেটের সাথে যেটি প্রসেসটি আছে।
একটি ফর্কড প্রসেসের নিজস্ব প্রসেস আইডি থাকে, এবং top বা Activity Monitor-এর মতো টুলে আলাদা করে দেখা যায়। সেটি কিছুটা এভাবে দেখে:
আপনি যেটি দিয়ে শুরু করেন সেটিকে মাস্টার প্রসেস বলা হয়, এবং মাস্টার প্রসেস থেকে ফর্ক করা প্রসেসগুলোক ওয়ার্কার প্রসেস বলা হয়।
যেহেতু এই নতুন ফর্কড ওয়ার্কার প্রসেসগুলো সত্যিকারের আলাদা প্রসেস, আমরা মেমরি শেয়ার করতে পারি না সেগুলো এবং মাস্টার প্রসেসের মধ্যে। আমাদের সেগুলোর মধ্যে কমিউনিকেশন জন্য কিছু দরকার।
ইউনিক্স পাইপ
প্রসেসের মধ্যে কমিউনিকেশনের জন্য আমরা ইউনিক্স পাইপ ব্যবহার করব। একটি ইউনিক্স পাইপ দুটি প্রসেসের মধ্যে একটি টু-ওয়ে বাইট স্ট্রিম সেট আপ করে, এবং আপনি এটা ব্যবহার করতে পারেন এক প্রসেস থেকে অন্যটিতে ডেটা পাঠাতে। সৌভাগ্যক্রমে, রুবি এই পাইপের চারপাশে একটি সুন্দর র্যাপার অফার করে যাতে আমাদের চাকা আবার আবিষ্কার করতে না হয়।
নিচের উদাহরণে আমরা রুবিতে একটি পাইপ সেট আপ করি - একটি রিডিং এবং একটি রাইটিং এন্ড - এবং আমরা মাস্টার প্রসেস ফর্ক করি। fork-এ পাস করা ব্লকে কোড ফর্কড প্রসেসে রান করে। মূল প্রসেস এই ব্লকের পর কন্টিনিউ করে। তারপর আমরা ফর্কড প্রসেস থেকে মূল প্রসেসে একটি মেসেজ লিখি।
reader, writer = IO.pipe
fork do
# This is running in the forked process.
writer.puts 'Hello from the forked process'
end
# This is running in the original process, it will puts the
# message from the forked process.
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-এর ইমপ্লিমেন্টেশন গিটহাবে পাওয়া যাবে আপনি যদি ডিটেইলস দেখতে চান কীভাবে সেটি অপারেট করে।
নতুন কানেকশন এক্সেপ্ট করা
আমাদের ইনকামিং কানেকশন এক্সেপ্ট করতে হবে এবং পাইপ সেট আপ করতে হবে। নতুন রাইটার client_writers অ্যারেতে পুশ হবে। মূল প্রসেস অ্যারেতে লুপ করে প্রতিটি ওয়ার্কার প্রসেসে একটি মেসেজ পাঠাতে পারবে এর পাইপে লিখে।
তারপর আমরা মাস্টার প্রসেস ফর্ক করি, এবং ফর্কড ওয়ার্কার প্রসেসে কোড ক্লায়েন্ট কানেকশন হ্যান্ডল করবে।
loop do
while socket = server.accept
# Create a client reader and writer so that the master
# process can write messages back to us.
client_reader, client_writer = IO.pipe
# Put the client writer on the list of writers so the
# master process can write to them.
client_writers.push(client_writer)
# Fork child process, everything in the fork block
# only runs in the child process.
fork do
# Handle connection
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)
# Read incoming messages from the client.
while incoming = read_line_from(socket)
master_writer.puts "#{nickname}: #{incoming}"
end
puts "#{Process.pid}: Disconnected #{nickname}"
একটি ওয়ার্কিং চ্যাট সিস্টেম
এখন পুরো চ্যাট সিস্টেম কাজ করছে! কিন্তু আপনি দেখতে পাচ্ছেন, মাল্টিপ্রসেসিং ব্যবহার করে একটি প্রোগ্রাম লিখা খুব কমপ্লেক্স এবং অনেক রিসোর্স ব্যবহার করে। অপসাইড হলো এটা খুব রবাস্ট। যদি একটি চাইল্ড প্রসেস ক্র্যাশ করে বাকি সিস্টেম শুধু চলতে থাকে। আপনি সেটি ট্রাই করতে পারেন উদাহরণ কোড রান করে এবং kill -9 রান করে একটি প্রসেসে (আপনি প্রসেস আইডি সার্ভারের লগ আউটপুটে পাবেন)।
পরবর্তী আর্টিকেলে আমরা এই একই চ্যাট সিস্টেম ইমপ্লিমেন্ট করব শুধু থ্রেড ব্যবহার করে, যাতে আমরা একই ফিচারসহ একটি সার্ভার রান করতে পারি শুধু একটি প্রসেস এবং কম মেমরি দিয়ে।