Action Cable-এর মতো লাইব্রেরির কারণে রেইলসে রিয়েল-টাইম ফিচার বানানো অনেক সহজ হয়েছে। AppSignal Academy-এর এই épisod-এ, আমরা রিয়েল-টাইম আপডেটে ডুব দিব এবং একটি মিনিমাল WebSocket সার্ভার বানাতে খেলব যাতে দেখতে পাই হাডের নিচে কীভাবে কাজ করে।
আমরা একটি অ্যাপ্লিকেশন বানাব যা পুশ করে ডেটা, এবং একটি WebSocket-এর ওভার Pub/Sub ব্যবহার করে। কোড শুরু করার আগে, চলুন একটু সময় বিতান করি সেই তিনটি কনসেপ্টের অর্থ বুঝতে:
পুশ হলো রিসিভারের কাছে ডেটা পুশ করা, রিসিভারকে সেই ডেটার জন্য পোল করার বদলে। স্টক প্রাইস, চ্যাট অ্যাপ্লিকেশন, অথবা অপারেশন কনসোলের মতো রিয়েল-টাইম আপডেটের জন্য অবশ্যক।
Pub/Sub বা পাবলিশ এবং সাবস্ক্রাইব হলো ডেটা পুশ করার জন্য একটি ইন্টারঅ্যাকশন মডেল যা ১৯৯০-এর দশকে TIBCO দিয়ে 월 স্ট্রিটে জনপ্রিয় हुअ। একটি রিসিভার একটি সাবজেক্টে সাবস্ক্রাইব করে এবং একটি পাবলিশারের কাছে থেকে সেই সাবজেক্টে ডেটা পুশ করার জন্য অপেক্ষা করে। পাবলিশড মেসেজকে একটি লিসেনারের সাথে ম্যাচ করার জন্য 와াইল্ডকার্ড প্যাটার্ন ম্যাচিং অন্তর্ভুক্ত করা সাধারণ, যদিও কিছু সরল ইমপ্লিমেন্টেশন শুধু নামযুক্ত চ্যানেল ব্যবহার করে সাবজেক্টে 와াইল্ডকার্ডের বদলে। আমি সেই প্রায়শই দিনগুলোতে TIBCO-তে শুরু করলাম তাই আমি ওয়াইল্ডকার্ড প্যাটার্ন ম্যাচিং-এর ফ্লেক্সিবিলিটি পছন্দ করি।
WebSocket হলো ডেটা এক্সচেঞ্জ করার জন্য একটি প্রোটোকল - সাধারণত একটি ওয়েব ব্রাউজার এবং একটি অ্যাপ্লিকেশনের মধ্যে। একটি HTTP কানেকশন WebSocket কানেকশনে আপগ্রেড হয় এবং তারপর দুটি এন্ডপয়েন্টের মধ্যে দুদিকেই ডেটা পাঠানো যায়। WebSocket অ্যাপ্লিকেশন থেকে ব্রাউজারে ডেটা পুশ করতে পারে। এটি আপনার ব্রাউজারের জাভাস্ক্রিপ্ট কোড থেকে আবার অ্যাপ্লিকেশনে ডেটা পাঠানোর জন্য POST বা PUT ছাড়া আরেকটি মেকানিজম প্রদান করে। খুব ভালো, ঠিক নয়?
হাডের নিচে
চলুন দেখি একটি WebSocket সার্ভারের উদাহরণ কীভাবে কাজ করতে পারে। একটি ব্রাউজার থেকে, ক্লায়েন্ট জাভাস্ক্রিপ্ট কোড ব্যবহার করে একটি সার্ভারের সাথে WebSocket কানেকশন করার চেষ্টা করে।
var sock = new WebSocket("ws://" + document.URL.split("/")[2] + "/upgrade");
সার্ভার একটি HTTP রিকোয়েস্ট পায় একটি ইন্ডিকেটরসহ যে একটি আপগ্রেড রিকোয়েস্ট করা হয়েছে। সাধারণত, সার্ভার একটি অ্যাপ্লিকেশনকে সিদ্ধান্ত নেওয়ার জন্য দেয় আপগ্রেড করতে হবে কিনা। সেটি কীভাবে করে সেটা অ্যাপকে প্রদান করা API-র ওপর নির্ভর করে। Rack সাপোর্টকারী একটি সার্ভার সকেট হাইজ্যাক করার অপশন দেয় এবং ডেভেলপারকে সকল প্রোটোকল ডিটেইল হ্যান্ডল করতে দেয় অথবা, একটি প্রপোজড PR অনুযায়ী, আপগ্রেড করার জন্য একটি রেসপন্স যথেষ্ট।
আপগ্রেড হলো সার্ভার এবং ক্লায়েন্টের মধ্যে এক্সচেঞ্জের একটি সেট। সকল ব্রাউজার এবং কিছু সার্ভার জেম এই ডিটেইলগুলো লুকায়ে। একবার কানেকশন etablish হলে, মেসেজগুলো WebSocket প্রোটোকল অনুসরণ করে এক্সচেঞ্জ করা যায়।
হাডের নিচের জাদু এনকোডিং, ডিকোডিং, এবং মেসেজ এক্সচেঞ্জ প্রোটোকল হ্যান্ডল করে। মেসেজগুলো বাইনারি, ফিক্সড-উইডথ স্ট্রাকচার পেলোডসহ, SHA1 ব্যবহার করে এনক্রিপ্টেড। WebSocket প্রোটোকলে বেশ কিছু মেসেজ টাইপ এবং এক্সচেঞ্জ আছে, যেন পিং/পং হার্টবিট এবং ওপেনিং এবং ক্লোজিং মেসেজ এক্সচেঞ্জ। সেটাই সার্ভারগুলো পারফর্ম করে কানেকশন-হাইজ্যাক অ্যাপ্রোচ না ব্যবহার করে।
ডুব দিয়ে যাওয়া
আমরা একটি ক্লক থ্রেডের উদাহরণ ব্যবহার করব যা শুরু হয়ে সকল লিসেনিং ক্লায়েন্টকে বর্তমান টাইম পাবলিশ করে। আমরা Agoo ব্যবহার করব আমাদের সার্ভার বানাতে কারণ এটি দ্রুত এবং জটিলতা কম রাখে।
আমরা কিছু জাভাস্ক্রিপ্ট দিয়ে শুরু করব ক্লায়েন্ট হিসেবে বর্তমান টাইম একটি HTML পেজে দেখাতে। একটি নতুন WebSocket তৈরি করার পর একটি onopen কলব্যাক সেট করা হয় যা স্ট্যাটাস HTML এলিমেন্ট পরিবর্তন করে। onmessage কলব্যাক message HTML এলিমেন্ট আপডেট করে। কলব্যাকস পাবলিশ এবং সাবস্ক্রাইব এক্সচেঞ্জের মত অ্যাসিনক্রোনাস কলসের সাথে কাজ করার সময় একটি সাধারণ ডিজাইন প্যাটার্ন।
...
... waiting ...
আমাদের ক্লায়েন্ট হয়ে গেলে, চলুন সার্ভার ইমপ্লিমেন্ট করি, যা Rack API ব্যবহার করে একটি রুবি অ্যাপ্লিকেশন। Clock ক্লাস নিজে /upgrade পথে সকল HTTP রিকোয়েস্টের জন্য একটি হ্যান্ডলার হবে। যদি রিকোয়েস্ট আপগ্রেডের জন্য হয়, আমরা সাকসেস রিটার্ন করব HTTP স্ট্যাটাস কোড ২০০ সহ, অন্যথায়, পেজ নট ফাউন্ডের জন্য ৪০৪ রিটার্ন করব। #call মেথডে একমাত্র অন্য ধাপ হলো WebSocket হ্যান্ডলার অ্যাসাইন করা।
class Clock
def self.call(env)
unless env['rack.upgrade?'].nil?
env['rack.upgrade'] = Clock
[ 200, { }, [ ] ]
else
[ 404, { }, [ ] ]
end
end
end
API কলব্যাক-ভিত্তিক। আমাদের সার্ভারের জন্য একমাত্র কলব্যাক যা আমাদের কথা #on_open কলব্যাক যা আমাদের "time" সাবজেক্টে একটি সাবস্ক্রিপশন তৈরি করতে সক্ষম করে। মেসেজগুলো চ্যানেল এক্সচেঞ্জ হয় সাবজেক্ট বা টপিক দ্বারা চিহ্নিত। #on_open কল হয় যখন একটি WebSocket কানেকশন etablish হয়।
class Clock
# ...
def self.on_open(client)
client.subscribe('time')
end
end
এখন, চলুন পাবলিশিং শুরু করি একটি থ্রেড দিয়ে যা প্রতি সেকেন্ডে টাইম পাবলিশ করে। Agoo.publish কলটি "time" সাবজেক্টে একটি মেসেজ পাঠায়, তারপর সকল সাবস্ক্রাইবার মেসেজ পায়। সার্ভার সাবস্ক্রিপশন এবং কানেকশন ট্র্যাক করে এবং মেসেজটি জাভাস্ক্রিপ্ট ক্লায়েন্টে ডেলিভার করে যা HTML এলিমেন্ট আপডেট করে।
require 'agoo'
Thread.new {
loop do
now = Time.now
Agoo.publish('time', "%02d:%02d:%02d" % [now.hour, now.min, now.sec])
sleep(1)
end
}
আবশ্যক একমাত্র অন্য কোড হলো সার্ভার ইনিশিয়ালাইজ এবং শুরু করার কোড। Agoo::Server.handle(:GET, '/upgrade', Clock) কলটি সার্ভাকে বলে HTTP GET রিকোয়েস্ট /upgrade URL পথে লিসেন করতে এবং সেই রিকোয়েস্টগুলো Clock ক্লাসে পাস করতে। এটি রাউটিং রুবির বাইরে হোতে দেয় উন্নত পারফরম্যান্স এবং ফ্লেক্সিবিলিটির জন্য।
Agoo::Server.init(6464, '.', thread_count: 0)
Agoo::Server.handle(:GET, '/upgrade', Clock)
Agoo::Server.start
আমরা প্রায় শেষ। সার্ভার এই কমান্ড দিয়ে চালান।
$ ruby pubsub.rb
একটি লগ এন্ট্রি প্রদর্শিত হওয়া উচিত যেন নিচের মত কিছু, যা ইন্ডিকেট করে সার্ভার চলছে এবং পোর্ট ৬৪৬৪-এ লিসেন করছে।
I 2018/08/14 19:49:45.170618000 INFO: Agoo 2.5.0 with pid 40366 is listening on https://:6464.
দেখার সময় কাজ করছে কিনা
চলুন https://localhost:6464/websocket.html খুলি। একটি প্রাথমিক ফ্লিকারের পর কানেকশন estabeleish হলে, কানেকশন স্ট্যাটাস এবং টাইম প্রদর্শিত হবে। টাইম প্রতি সেকেন্ড ইনক্রিমেন্ট হবে যেন ক্লক টিক করছে।
connected
19:50:12
একটি পাবলিশ এবং সাবস্ক্রাইব ওয়েব অ্যাপ্লিকেশন বানাতে অভিনন্দন ;-)
আজকের épisod-এ, আমরা WebSocket ব্যবহারের ওপর নজর দিলাম। Server Side Events (SSE) একই কাজ করার আরেকটি অপশন অফার করে, এবং আমরা ফুল সোর্স কোড উদাহরণে SSE অন্তর্ভুক্ত করেছি। আপনি যদি আরও জানতে চান, আমাদের ব্যবহার করা Agoo সার্ভার বা Iodine WebSocket Server-এ এক নজর দিন।
আপনার কোনো প্রশ্ন বা মন্তব্য থাকলে, @AppSignal-এ আমাদের লাইন ড্রপ করতে সংকোচ করবেন না।