কম্পিউটার টিউটোরিয়াল

রুবি কাঙ্কারেন্সি মাস্টার করুন: মাল্টি-থ্রেডেড চ্যাট সিস্টেম বিল্ড করুন

রুবি ম্যাজিকের আগের এডিশনে আমরা দেখিয়েছিলাম কীভাবে আপনি একটি চ্যাট সিস্টেম ইমপ্লিমেন্ট করতে পারেন একাধিক প্রসেস ব্যবহার করে। এইবার আমরা দেখাব কীভাবে আপনি একই জিনিস একাধিক থ্রেড ব্যবহার করে করতে পারেন।

কুইক রিক্যাপ

আপনি যদি বেসিক সেটআপের ফুল এক্সপ্লানেশন চান তবে আগের আর্টিকেল চেক আউট করুন। কিন্তু দ্রুত মনে করাতে: আমাদের চ্যাট সিস্টেম এভাবে দেখে:

আমরা আগের মতো একই ক্লায়েন্ট ব্যবহার করছি:

# 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

সার্ভারের বেসিক সেটআপ একই:

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

এই আর্টিকেলের উদাহরণগুলোর ফুল সোর্স কোড গিটহাবে উপলব্ধ, যাতে আপনি নিজে এক্সপেরিমেন্ট করতে পারেন।

মাল্টি-থ্রেডেড চ্যাট সার্ভার

এখন আমরা সেটি পাচ্ছি যেটি মাল্টি-প্রসেস ইমপ্লিমেন্টেশনের থেকে আলাদা। মাল্টি-থ্রেডিং ব্যবহার করে আমরা একই রুবি প্রসেসে একাধিক জিনিস একসাথে করতে পারি। আমরা সেটি করব একাধিক থ্রেড স্পন করে যারা কাজ করবে।

থ্রেড

একটি থ্রেড স্বাধীনভাবে রান করে, একটি প্রসেসের ভেতরে কোড এক্সিকিউট করে। একাধিক থ্রেড একই প্রসেসে বাস করতে পারে এবং সেগুলো মেমরি শেয়ার করতে পারে। রুবি কাঙ্কারেন্সি মাস্টার করুন: মাল্টি-থ্রেডেড চ্যাট সিস্টেম বিল্ড করুন

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

mutex = Mutex.new
messages = []

পরবর্তী আমরা একটি লুপ শুরু করব যেখানে আমরা চ্যাট ক্লায়েন্ট থেকে ইনকামিং কানেকশন এক্সেপ্ট করব। একবার কানেকশন স্থাপন হলে, আমরা একটি থ্রেড স্পন করব যেটি সেটি ক্লায়েন্ট কানেকশন থেকে ইনকামিং এবং আউটগোইং মেসেজ হ্যান্ডল করবে।

Thread.new কল ব্লক করে যতক্ষণ না server.accept কিছু রিটার্ন করে, এবং তারপর ফলোইং ব্লক নতুন তৈরি থ্রেডে ইয়িল্ড করে। থ্রেডের কোড তারপর প্রথম লাইন পড়ে যেটি পাঠানো হয়েছে এবং সেটি নিকনেম হিসেবে স্টোর করে। অবশেষে ইটা মেসেজ পাঠানো এবং পাওয়া শুরু করে।

loop do
  Thread.new(server.accept) do |socket|
    nickname = read_line_from(socket)
    # Send incoming message (coming up)
    # Read incoming messages (coming up)
  end
end

Mutex

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

সার্ভার সকেট থেকে ইনকামিং মেসেজ পড়ে। এটা synchronize ব্যবহার করে মেসেজ স্টোরে লক পায়, যাতে সেটি নিরাপদে একটি মেসেজ যোগ করতে পারে মেসেজ Array-এ।

# Read incoming messages
while incoming = read_line_from(socket)
  mutex.synchronize do
    messages.push(
      :time => Time.now,
      :nickname => nickname,
      :text => incoming
    )
  end
end

শেষে, একটি Thread স্পন করা হয় যেটি কন্টিনিউয়াসলি একটি লুপে রান করে, নিশ্চিত করতে যে সার্ভার tarafından প্রাপ্ত সব নতুন মেসেজ ক্লায়েন্টে পাঠানো হচ্ছে। আবার সেটি লক পায় যাতে সেটি জানে অন্যান্য থ্রেড ইন্টারফেয়ার করছে না। এক টিক লুপ শেষ হলে সেটি একটু স্লিপ করে এবং তারপর কন্টিনিউ করে।

# Send incoming message
Thread.new do
  sent_until = Time.now
  loop do
    messages_to_send = mutex.synchronize do
      get_messages_to_send(nickname, messages, sent_until).tap do
        sent_until = Time.now
      end
    end
    messages_to_send.each do |message|
      socket.puts "#{message[:nickname]}: #{message[:text]}"
    end
    sleep 0.2
  end
end

গ্লোবাল ইন্টারপ্রেটার লক

আপনি শুনে থাকতে পারেন রুবি "রিয়াল" থ্রেডিং করতে পারে না রুবির গ্লোবাল ইন্টারপ্রেটার লক (GIL) এর কারণে। এটি আংশিক সত্য। GIL হলো সকল রুবি কোড এক্সিকিউশনের চারপাশে একটি লক যা একটি রুবি প্রসেসকে একাধিক সিপিইউ কাঙ্কারেন্টলি ব্যবহার করতে বাধা দেয়। IO অপারেশন (যেমন এই আর্টিকেলে আমরা ব্যবহার করেছি নেটওয়ার্ক কানেকশন) GIL-এর বাইরে অপারেট করে, যাতে আপনি এই কেসে ডিসেন্ট কাঙ্কারেন্সি অর্জন করতে পারেন।

উপসংহার

এখন আমাদের একটি চ্যাট সার্ভার আছে যেটি একটি সিঙ্গেল প্রসেসে রান করছে কানেকশন প্রতি একটি থ্রেড ব্যবহার করে। এটা মাল্টি-প্রসেস ইমপ্লিমেন্টেশনের চেয়ে অনেক কম রিসোর্স ব্যবহার করবে। আপনি কোডের ডিটেইলস দেখতে বা ট্রাই করতে চান তবে উদাহরণ কোডটি এখানে পাবেন।

এই সিরিজের ফাইনাল আর্টিকেলে আমরা এই একই চ্যাট সার্ভার ইমপ্লিমেন্ট করব একটি সিঙ্গেল থ্রেড এবং একটি ইভেন্ট লুপ ব্যবহার করে। সিদ্ধান্তিকভাবে এটা থ্রেড ইমপ্লিমেন্টেশনের চেয়ে কম রিসোর্স ব্যবহার করবে!

  1. কনকারেন্সি আয়ত্ত করুন: হাই-পারফরম্যান্স অ্যাপের জন্য কার্যকর স্কেলিং

  2. রেইলস ডিপ ডাইভ সমগ্রভাবে কীভাবে করবেন: একটি গাইড

  3. রেইলসে কখন ডিপ ডাইভ করবেন: আপনার মাস্টারির টাইমিং

  4. ৫০-জন টিমের ওয়ার্ডপ্রেস স্টুডিও বানানো: মারিও পেশেভ (Mario Peshev) এর অভিজ্ঞতা থেকে শেখা