Skip to Content
সিস্টেম ডিজাইনে স্বাগতম 🎉
DocumentationDesign A Chat SystemPropose High Level Design and Get Buy In

ধাপ ২ - উচ্চ-স্তরের (high-level) ডিজাইন প্রস্তাব করা এবং সম্মতি নেওয়া

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

  • অন্যান্য ক্লায়েন্টদের কাছ থেকে মেসেজ গ্রহণ করা।
  • প্রতিটি মেসেজের জন্য সঠিক প্রাপকদের খুঁজে বের করা এবং প্রাপকদের কাছে মেসেজটি পৌঁছে দেওয়া।
  • যদি কোনো প্রাপক অনলাইনে না থাকে, তবে সে অনলাইনে না আসা পর্যন্ত সার্ভারে তার মেসেজগুলো ধরে রাখা।

চিত্র ২-এ ক্লায়েন্ট (প্রেরক এবং প্রাপক) এবং চ্যাট সার্ভিসের মধ্যে সম্পর্ক দেখানো হয়েছে।

[চিত্র ২-এর বর্ণনা: ছবিটি একটি চ্যাট অ্যাপ্লিকেশনের আর্কিটেকচারের একটি সরলীকৃত মডেল উপস্থাপন করে। ডায়াগ্রামটি তিনটি প্রধান উপাদান দেখায়: একটি ‘প্রেরক’ (sender) যা হালকা নীল গোলাকার বর্গক্ষেত্রের ভেতরে একটি ল্যাপটপ আইকন দ্বারা উপস্থাপিত, একটি ‘চ্যাট সার্ভিস’ যা হালকা নীল গোলাকার আয়তক্ষেত্র দ্বারা উপস্থাপিত, এবং একটি ‘প্রাপক’ (receiver) যা হালকা নীল গোলাকার বর্গক্ষেত্রের ভেতরে একটি স্মার্টফোন আইকন দ্বারা উপস্থাপিত। প্রেরক চ্যাট সার্ভিসে একটি তীর চিহ্নের মাধ্যমে একটি ‘মেসেজ’ পাঠায়। চ্যাট সার্ভিস, যার দুটি ফাংশন রয়েছে: ‘১. মেসেজ সংরক্ষণ করা’ এবং ‘২. মেসেজ রিলে করা’, এরপর অন্য একটি তীর চিহ্নের মাধ্যমে প্রাপকের কাছে একই ‘মেসেজ’ পাঠায়। তীর চিহ্নগুলো প্রেরক থেকে চ্যাট সার্ভিসে এবং সেখান থেকে প্রাপকের কাছে মেসেজের একমুখী প্রবাহ নির্দেশ করে। পুরো সিস্টেমটি দুই ব্যবহারকারীর মধ্যে মেসেজ আদান-প্রদান এবং সংরক্ষণের ওপর কেন্দ্রীভূত।] চিত্র ২

যখন একটি ক্লায়েন্ট চ্যাট শুরু করার ইচ্ছা করে, তখন এটি এক বা একাধিক নেটওয়ার্ক প্রোটোকল ব্যবহার করে চ্যাট সার্ভিসের সাথে সংযোগ স্থাপন করে। একটি চ্যাট সার্ভিসের জন্য নেটওয়ার্ক প্রোটোকলের পছন্দ গুরুত্বপূর্ণ। আসুন এটি নিয়ে ইন্টারভিউয়ারের সাথে আলোচনা করি।

বেশিরভাগ ক্লায়েন্ট/সার্ভার অ্যাপ্লিকেশনের জন্য রিকোয়েস্ট ক্লায়েন্ট দ্বারা শুরু হয়। চ্যাট অ্যাপ্লিকেশনের প্রেরক অংশের জন্যও এটি সত্য। চিত্র ২-এ, যখন প্রেরক চ্যাট সার্ভিসের মাধ্যমে প্রাপকের কাছে একটি মেসেজ পাঠায়, তখন এটি সময়-পরীক্ষিত HTTP প্রোটোকল ব্যবহার করে, যা সবচেয়ে সাধারণ ওয়েব প্রোটোকল। এই পরিস্থিতিতে, ক্লায়েন্ট চ্যাট সার্ভিসের সাথে একটি HTTP সংযোগ খোলে এবং মেসেজটি পাঠায়, সার্ভিসকে মেসেজটি প্রাপকের কাছে পাঠানোর নির্দেশ দেয়। কিপ-অ্যালিভ (keep-alive) এই কাজের জন্য কার্যকর কারণ কিপ-অ্যালিভ হেডার ক্লায়েন্টকে চ্যাট সার্ভিসের সাথে একটি স্থায়ী সংযোগ বজায় রাখতে দেয়। এটি TCP হ্যান্ডশেকের সংখ্যাও কমিয়ে দেয়। প্রেরক পক্ষের জন্য HTTP একটি ভালো অপশন, এবং Facebook [1]-এর মতো অনেক জনপ্রিয় চ্যাট অ্যাপ্লিকেশন প্রাথমিকভাবে মেসেজ পাঠানোর জন্য HTTP ব্যবহার করত।

তবে, প্রাপক পক্ষটি কিছুটা জটিল। যেহেতু HTTP ক্লায়েন্ট-দ্বারা-শুরু (client-initiated), তাই সার্ভার থেকে মেসেজ পাঠানো সহজ নয়। বছরের পর বছর ধরে, সার্ভার-দ্বারা-শুরু সংযোগ অনুকরণ করতে অনেক কৌশল ব্যবহার করা হয়েছে: পোলিং (polling), লং পোলিং (long polling), এবং ওয়েবসকেট (WebSocket)। এগুলো সিস্টেম ডিজাইন ইন্টারভিউতে ব্যাপকভাবে ব্যবহৃত গুরুত্বপূর্ণ কৌশল, তাই আসুন এদের প্রতিটি পরীক্ষা করি।

পোলিং (Polling)

চিত্র ৩-এ দেখানো হয়েছে, পোলিং হলো এমন একটি কৌশল যেখানে ক্লায়েন্ট পর্যায়ক্রমে সার্ভারকে জিজ্ঞেস করে কোনো মেসেজ আছে কিনা। পোলিংয়ের ফ্রিকোয়েন্সির ওপর নির্ভর করে, এটি ব্যয়বহুল হতে পারে। এমন একটি প্রশ্নের উত্তর দিতে এটি মূল্যবান সার্ভার রিসোর্স নষ্ট করতে পারে, যার উত্তর বেশিরভাগ সময় ‘না’ হয়।

[চিত্র ৩-এর বর্ণনা: ছবিটি একটি সিকোয়েন্স ডায়াগ্রাম যা মেসেজ পুনরুদ্ধারের বিষয়ে ক্লায়েন্ট এবং সার্ভারের মধ্যে মিথস্ক্রিয়া চিত্রিত করে। ডায়াগ্রামটি চারটি পুনরাবৃত্তিমূলক চক্র দেখায়। প্রতিটি চক্রে, ক্লায়েন্ট সার্ভারের কাছে ‘নতুন মেসেজ?’ জিজ্ঞেস করে একটি রিকোয়েস্ট পাঠায়। চারবারের মধ্যে তিনবার, সার্ভার ‘না’ উত্তর দেয় এবং ক্লায়েন্টের দিকে একটি লাল তীর চিহ্ন পাঠায়, যা কোনো নতুন মেসেজ নেই তা নির্দেশ করে। প্রতিটি নেতিবাচক উত্তরের পরে, ‘সংযোগ বন্ধ’ লেবেলযুক্ত একটি ড্যাশ করা রেখা সেই যোগাযোগ চক্রের সমাপ্তি নির্দেশ করে। তবে, তৃতীয় চক্রে, সার্ভার ‘হ্যাঁ। নতুন… ফেরত দিচ্ছি’ উত্তর দেয়, যা একটি সবুজ তীর চিহ্ন দ্বারা নির্দেশিত, যা বোঝায় নতুন মেসেজ পাওয়া গেছে এবং সেগুলো পাঠানো হচ্ছে। এই ইতিবাচক উত্তরের পরে, আবার ‘সংযোগ বন্ধ’ লেবেলযুক্ত একটি ড্যাশ করা রেখা সেই চক্রের সংযোগ বন্ধ হওয়া দেখায়। উপবৃত্তাকার চিহ্ন (…) বোঝায় যে ক্লায়েন্টের রিকোয়েস্ট এবং সার্ভারের উত্তরের এই ধারা চলতে থাকে। সামগ্রিক ডায়াগ্রামটি একটি পোলিং মেকানিজম চিত্রিত করে যেখানে ক্লায়েন্ট বারবার সার্ভারের কাছ থেকে নতুন মেসেজ চেক করে।] চিত্র ৩

লং পোলিং (Long polling)

যেহেতু পোলিং অদক্ষ হতে পারে, তাই এর পরবর্তী ধাপ হলো লং পোলিং (চিত্র ৪)।

[চিত্র ৪-এর বর্ণনা: ছবিটি নতুন মেসেজ পুনরুদ্ধারের জন্য ক্লায়েন্ট-সার্ভার যোগাযোগের একটি সিকোয়েন্স ডায়াগ্রাম উপস্থাপন করে। দুটি উল্লম্ব রেখা যথাক্রমে ক্লায়েন্ট এবং সার্ভার নির্দেশ করে, এবং অনুভূমিক তীর চিহ্ন মেসেজ আদান-প্রদান নির্দেশ করে। ক্লায়েন্ট সার্ভারে ‘নতুন মেসেজ?’ রিকোয়েস্ট (মোটা কালো তীর) পাঠিয়ে মিথস্ক্রিয়া শুরু করে। নতুন মেসেজ পাওয়া গেলে সার্ভার ‘হ্যাঁ। নতুন মেসেজ ফেরত দিচ্ছি’ (সবুজ তীর) উত্তর দেয়, মেসেজগুলো ক্লায়েন্টে পাঠায় এবং সংযোগ বন্ধ করে দেয়। যদি কোনো নতুন মেসেজ না থাকে, তবে সার্ভার ততক্ষণ অপেক্ষা করে যতক্ষণ না একটি টাইমআউট ঘটে (সার্ভারের পাশে ‘নতুন মেসেজের জন্য অপেক্ষা করছে’ লেখা দ্বারা নির্দেশিত)। টাইমআউট হওয়ার পর, সার্ভার সংযোগ বন্ধ করে দেয় (লাল তীর)। তীর চিহ্নগুলোর মধ্যে ড্যাশ করা রেখা সংযোগ বন্ধ হওয়া নির্দেশ করে। ক্লায়েন্টের পরবর্তী ‘নতুন মেসেজ?’ রিকোয়েস্টের সাথে প্রক্রিয়াটি পুনরাবৃত্তি হয়। ডায়াগ্রামটি ক্লায়েন্ট দ্বারা ব্যবহৃত পোলিং মেকানিজম দৃশ্যত চিত্রিত করে, সফল মেসেজ পুনরুদ্ধার এবং টাইমআউট পরিচালনা উভয়ই দেখায়।] চিত্র ৪

লং পোলিংয়ে, একটি ক্লায়েন্ট সংযোগটি খোলা রাখে যতক্ষণ না আসলে নতুন মেসেজ পাওয়া যায় অথবা একটি টাইমআউট সীমায় পৌঁছায়। ক্লায়েন্ট নতুন মেসেজ পেলে, এটি অবিলম্বে সার্ভারে অন্য একটি রিকোয়েস্ট পাঠায়, প্রক্রিয়াটি পুনরায় শুরু করে। লং পোলিংয়ের কিছু অসুবিধা রয়েছে:

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

ওয়েবসকেট (WebSocket)

সার্ভার থেকে ক্লায়েন্টে অ্যাসিঙ্ক্রোনাস আপডেট পাঠানোর জন্য ওয়েবসকেট সবচেয়ে সাধারণ সমাধান। চিত্র ৫ দেখায় এটি কীভাবে কাজ করে।

[চিত্র ৫-এর বর্ণনা: ছবিটি ক্লায়েন্ট এবং সার্ভারের মধ্যে ওয়েবসকেট হ্যান্ডশেক এবং যোগাযোগের একটি সিকোয়েন্স ডায়াগ্রাম উপস্থাপন করে। ডায়াগ্রামটিতে ‘ক্লায়েন্ট’ এবং ‘সার্ভার’ লেবেলযুক্ত দুটি আয়তক্ষেত্রাকার বাক্স দেখানো হয়েছে, যা তাদের স্থায়ী সংযোগ নির্দেশকারী উল্লম্ব রেখা দ্বারা সংযুক্ত। ক্লায়েন্ট বাক্সের পাশে একটি ছোট আয়তক্ষেত্রাকার বাক্সে ‘GET /ws’ লেখা আছে, যা ওয়েবসকেট সংযোগ স্থাপনের জন্য ক্লায়েন্টের প্রাথমিক রিকোয়েস্ট নির্দেশ করে। ক্লায়েন্ট থেকে সার্ভারে একটি মোটা কালো তীর চিহ্ন ‘HTTP হ্যান্ডশেক’ লেবেলযুক্ত, যা প্রাথমিক সংযোগের রিকোয়েস্ট চিত্রিত করে। এরপর সার্ভার থেকে ক্লায়েন্টে একটি সবুজ তীর চিহ্ন ‘স্বীকৃতি’ লেবেলযুক্ত ফিরে যায়, যা সংযোগে সার্ভারের সম্মতি নির্দেশ করে। অবশেষে, একটি ড্যাশ করা কালো দ্বিমুখী তীর চিহ্ন ‘দ্বিমুখী মেসেজ’ লেবেলযুক্ত, যা সফল হ্যান্ডশেকের পরে ক্লায়েন্ট এবং সার্ভারের মধ্যে মেসেজের চলমান আদান-প্রদান দেখায়, যা ওয়েবসকেটের দ্বিমুখী যোগাযোগের বৈশিষ্ট্যকে উপস্থাপন করে।] চিত্র ৫

ওয়েবসকেট সংযোগ ক্লায়েন্ট দ্বারা শুরু হয়। এটি দ্বিমুখী এবং স্থায়ী। এটি একটি HTTP সংযোগ হিসাবে তার জীবন শুরু করে এবং কিছু সুসংজ্ঞায়িত হ্যান্ডশেকের মাধ্যমে একটি ওয়েবসকেট সংযোগে “আপগ্রেড” করা যেতে পারে। এই স্থায়ী সংযোগের মাধ্যমে, একটি সার্ভার ক্লায়েন্টে আপডেট পাঠাতে পারে। ফায়ারওয়াল থাকলেও ওয়েবসকেট সংযোগ সাধারণত কাজ করে। কারণ এগুলো পোর্ট ৮০ বা ৪৪৩ ব্যবহার করে যা HTTP/HTTPS সংযোগের জন্যও ব্যবহৃত হয়।

আগে আমরা বলেছি যে প্রেরক পক্ষের জন্য HTTP একটি ভালো প্রোটোকল, কিন্তু যেহেতু ওয়েবসকেট দ্বিমুখী, তাই পাঠানোর জন্যও এটি ব্যবহার না করার কোনো শক্তিশালী প্রযুক্তিগত কারণ নেই। চিত্র ৬ দেখায় কীভাবে ওয়েবসকেট (ws) প্রেরক এবং প্রাপক উভয় পক্ষের জন্য ব্যবহার করা হয়।

[চিত্র ৬-এর বর্ণনা: ছবিটি একটি চ্যাট অ্যাপ্লিকেশনের একটি সরলীকৃত আর্কিটেকচার উপস্থাপন করে। হালকা নীল রঙের আউটলাইনযুক্ত দুটি আয়তক্ষেত্রাকার বাক্স ‘প্রেরক’ এবং ‘প্রাপক’ ক্লায়েন্টদের উপস্থাপন করে। প্রেরককে একটি ল্যাপটপ কম্পিউটার হিসাবে দেখানো হয়েছে, এবং প্রাপককে একটি স্মার্টফোন হিসাবে দেখানো হয়েছে। কেন্দ্রস্থলে অবস্থিত একটি বড়, হালকা নীল আয়তক্ষেত্রাকার বাক্স যার লেবেল ‘চ্যাট সার্ভিস’, অ্যাপ্লিকেশনের সার্ভার-সাইডের উপাদানকে উপস্থাপন করে। তীর চিহ্নগুলো তথ্যের প্রবাহ নির্দেশ করে, যার লেবেল ‘ws’ (সম্ভবত ওয়েবসকেট নির্দেশ করছে), যা দেখায় যে প্রেরক এবং প্রাপক উভয়েই এই প্রোটোকল ব্যবহার করে ‘চ্যাট সার্ভিস’-এর সাথে যোগাযোগ করে। তীর চিহ্নগুলো ক্লায়েন্টদের থেকে সার্ভারে মেসেজ পাঠানো নির্দেশ করতে ‘চ্যাট সার্ভিস’-এর দিকে নির্দেশ করে, এবং সার্ভার থেকে ক্লায়েন্টদের কাছে মেসেজ পাঠানো নির্দেশ করতে ‘চ্যাট সার্ভিস’ থেকে বেরিয়ে আসে, যা ক্লায়েন্ট এবং কেন্দ্রীয় চ্যাট সার্ভিসের মধ্যে দ্বিমুখী যোগাযোগ সক্রিয় করে।] চিত্র ৬

পাঠানো এবং গ্রহণ উভয়ের জন্য ওয়েবসকেট ব্যবহার করে, এটি ডিজাইনকে সরল করে এবং ক্লায়েন্ট এবং সার্ভার উভয়ের বাস্তবায়নকে আরও সহজ করে তোলে। যেহেতু ওয়েবসকেট সংযোগ স্থায়ী, তাই সার্ভার-সাইডে দক্ষ সংযোগ ব্যবস্থাপনা অত্যন্ত গুরুত্বপূর্ণ।

উচ্চ-স্তরের ডিজাইন (High-level design)

এইমাত্র আমরা উল্লেখ করেছি যে ক্লায়েন্ট এবং সার্ভারের মধ্যে প্রধান যোগাযোগ প্রোটোকল হিসাবে ওয়েবসকেট বেছে নেওয়া হয়েছে এর দ্বিমুখী যোগাযোগের জন্য, এটি লক্ষ্য করা গুরুত্বপূর্ণ যে বাকি সবকিছুর জন্য ওয়েবসকেট ব্যবহার করার প্রয়োজন নেই। প্রকৃতপক্ষে, একটি চ্যাট অ্যাপ্লিকেশনের বেশিরভাগ ফিচার (সাইন আপ, লগইন, ইউজার প্রোফাইল ইত্যাদি) HTTP-এর মাধ্যমে ঐতিহ্যবাহী রিকোয়েস্ট/রেসপন্স পদ্ধতি ব্যবহার করতে পারে। আসুন একটু গভীরে গিয়ে সিস্টেমের উচ্চ-স্তরের উপাদানগুলোর দিকে তাকাই।

চিত্র ৭-এ দেখানো হয়েছে, চ্যাট সিস্টেমটিকে তিনটি প্রধান বিভাগে ভাগ করা হয়েছে: স্টেটলেস সার্ভিস, স্টেটফুল সার্ভিস, এবং থার্ড-পার্টি ইন্টিগ্রেশন।

[চিত্র ৭-এর বর্ণনা: ছবিটি একটি সিস্টেম আর্কিটেকচার ডায়াগ্রাম উপস্থাপন করে যা দুটি প্রধান বিভাগে বিভক্ত: ‘স্টেটলেস’ এবং ‘স্টেটফুল’। ‘স্টেটলেস’ বিভাগটি দেখায় একজন ব্যবহারকারী (ল্যাপটপ এবং মোবাইল ফোন আইকন দ্বারা উপস্থাপিত) একটি লোড ব্যালেন্সারে HTTP রিকোয়েস্ট পাঠাচ্ছে। লোড ব্যালেন্সার রিকোয়েস্টগুলো চারটি সার্ভিসে বিতরণ করে: সার্ভিস ডিসকভারি, অথেন্টিকেশন সার্ভিস, গ্রুপ ম্যানেজমেন্ট, এবং ইউজার প্রোফাইল। এই সার্ভিসগুলোকে আয়তক্ষেত্রাকার বাক্স হিসাবে দেখানো হয়েছে, এবং তীর চিহ্নগুলো লোড ব্যালেন্সার থেকে প্রতিটি সার্ভিসে রিকোয়েস্টের প্রবাহ নির্দেশ করে। পুরো ‘স্টেটলেস’ বিভাগটি একটি ড্যাশ-লাইনের বাক্সে ঘেরা। ‘স্টেটফুল’ বিভাগটি দুইজন ব্যবহারকারীকে (ব্যবহারকারী ১ ল্যাপটপ সহ এবং ব্যবহারকারী ২ মোবাইল ফোন সহ) ওয়েবসকেটের (লেবেল ‘ws’) মাধ্যমে একটি ‘চ্যাট সার্ভিস’-এর সাথে যোগাযোগ করতে দেখায়। তীর চিহ্নগুলো ব্যবহারকারী এবং চ্যাট সার্ভিসের মধ্যে দ্বিমুখী যোগাযোগ দেখায়। এই ‘স্টেটফুল’ বিভাগটিও একটি ড্যাশ-লাইনের বাক্সে ঘেরা। পরিশেষে, ‘থার্ড পার্টি’ লেবেলযুক্ত একটি পৃথক ড্যাশ-লাইনের বাক্সে একটি ‘পুশ নোটিফিকেশন’ সার্ভিস রয়েছে, যা বোঝায় যে চ্যাট সার্ভিস পুশ নোটিফিকেশনের জন্য একটি থার্ড-পার্টি সার্ভিসের সাথে মিথস্ক্রিয়া করে। সামগ্রিক ডায়াগ্রামটি ক্লায়েন্ট-সার্ভার আর্কিটেকচার চিত্রিত করে যাতে রিকোয়েস্ট বিতরণের জন্য একটি লোড ব্যালেন্সার এবং স্টেটলেস এবং স্টেটফুল উপাদানের মধ্যে পৃথকীকরণ রয়েছে।] চিত্র ৭

স্টেটলেস সার্ভিস (Stateless Services)

স্টেটলেস সার্ভিসগুলো হলো ঐতিহ্যবাহী পাবলিক-ফেসিং রিকোয়েস্ট/রেসপন্স সার্ভিস, যা লগইন, সাইনআপ, ইউজার প্রোফাইল ইত্যাদি পরিচালনা করতে ব্যবহৃত হয়। এগুলো অনেক ওয়েবসাইট এবং অ্যাপের মধ্যে সাধারণ ফিচার।

স্টেটলেস সার্ভিসগুলো একটি লোড ব্যালেন্সারের পিছনে থাকে যার কাজ রিকোয়েস্ট পাথের ওপর ভিত্তি করে সঠিক সার্ভিসে রিকোয়েস্ট রুট করা। এই সার্ভিসগুলো মনোলিথিক বা পৃথক মাইক্রোসার্ভিস হতে পারে। আমাদের এই স্টেটলেস সার্ভিসগুলোর অনেকগুলো নিজে থেকে তৈরি করার দরকার নেই কারণ বাজারে এমন সার্ভিস আছে যা সহজেই ইন্টিগ্রেট করা যায়। যে সার্ভিসটি নিয়ে আমরা ডিপ ডাইভে আরও আলোচনা করব তা হলো সার্ভিস ডিসকভারি। এর প্রাথমিক কাজ হলো ক্লায়েন্টকে চ্যাট সার্ভারগুলোর DNS হোস্টনেমের একটি তালিকা দেওয়া যার সাথে ক্লায়েন্ট সংযোগ স্থাপন করতে পারে।

স্টেটফুল সার্ভিস (Stateful Service)

একমাত্র স্টেটফুল সার্ভিস হলো চ্যাট সার্ভিস। সার্ভিসটি স্টেটফুল কারণ প্রতিটি ক্লায়েন্ট একটি চ্যাট সার্ভারের সাথে একটি স্থায়ী নেটওয়ার্ক সংযোগ বজায় রাখে। এই সার্ভিসে, একটি ক্লায়েন্ট সাধারণত অন্য চ্যাট সার্ভারে সুইচ করে না যতক্ষণ না সার্ভারটি এখনও উপলব্ধ থাকে। সার্ভার ওভারলোডিং এড়াতে সার্ভিস ডিসকভারি চ্যাট সার্ভিসের সাথে ঘনিষ্ঠভাবে সমন্বয় করে। আমরা ডিপ ডাইভে বিস্তারিত আলোচনা করব।

থার্ড-পার্টি ইন্টিগ্রেশন (Third-party integration)

একটি চ্যাট অ্যাপের জন্য, পুশ নোটিফিকেশন হলো সবচেয়ে গুরুত্বপূর্ণ থার্ড-পার্টি ইন্টিগ্রেশন। যখন নতুন মেসেজ আসে তখন ব্যবহারকারীদের জানানোর এটি একটি উপায়, এমনকি যখন অ্যাপটি চলছে না। পুশ নোটিফিকেশনের সঠিক ইন্টিগ্রেশন অত্যন্ত গুরুত্বপূর্ণ। আরও তথ্যের জন্য “ডিজাইন আ নোটিফিকেশন সিস্টেম” অধ্যায়টি দেখুন।

স্কেলেবিলিটি (Scalability)

ছোট স্কেলে, উপরে তালিকাভুক্ত সমস্ত সার্ভিস একটি সার্ভারে ফিট করা যেতে পারে। এমনকি আমরা যে স্কেলের জন্য ডিজাইন করছি, তাত্ত্বিকভাবে একটি আধুনিক ক্লাউড সার্ভারে সমস্ত ব্যবহারকারীর সংযোগ ফিট করা সম্ভব। একটি সার্ভার যে সংখ্যক কনকারেন্ট সংযোগ (concurrent connections) পরিচালনা করতে পারে তা সম্ভবত সীমাবদ্ধতার প্রধান কারণ হবে। আমাদের পরিস্থিতিতে, ১ মিলিয়ন কনকারেন্ট ব্যবহারকারীর ক্ষেত্রে, ধরে নিচ্ছি প্রতিটি ব্যবহারকারীর সংযোগের জন্য সার্ভারে ১০কে (10K) মেমরি প্রয়োজন (এটি একটি খুবই আনুমানিক চিত্র এবং ভাষার পছন্দের ওপর অনেকটা নির্ভর করে), একটি বক্সে সমস্ত সংযোগ ধরে রাখতে এটির মাত্র ১০ জিবি মেমরি প্রয়োজন।

যদি আমরা এমন একটি ডিজাইন প্রস্তাব করি যেখানে সবকিছু একটি সার্ভারে ফিট করে, তবে এটি ইন্টারভিউয়ারের মনে একটি বড় লাল পতাকা (red flag) তুলতে পারে। কোনো প্রযুক্তিবিদই একক সার্ভারে এই ধরনের স্কেল ডিজাইন করবেন না। একক সার্ভার ডিজাইন অনেকগুলি কারণে একটি ডিল ব্রেকার। এর মধ্যে সিঙ্গেল পয়েন্ট অফ ফেইলিউর (single point of failure) সবচেয়ে বড়।

তবে, একটি একক সার্ভার ডিজাইন দিয়ে শুরু করা সম্পূর্ণ ঠিক আছে। শুধু নিশ্চিত করবেন যে ইন্টারভিউয়ার জানেন এটি একটি শুরুর বিন্দু। আমরা যা কিছু উল্লেখ করেছি তা একত্রিত করে, চিত্র ৮ সমন্বিত উচ্চ-স্তরের ডিজাইন দেখায়।

[চিত্র ৮-এর বর্ণনা: ছবিটি একটি রিয়েল-টাইম যোগাযোগ অ্যাপ্লিকেশনের জন্য একটি সিস্টেম আর্কিটেকচার ডায়াগ্রাম উপস্থাপন করে। উপরের দিকে, একটি ‘ইউজার’ উপাদান, একটি ল্যাপটপ এবং একটি মোবাইল ফোন হিসাবে চিত্রিত, ‘http’ এবং ‘ws’ প্রোটোকলের মাধ্যমে একটি ‘লোড ব্যালেন্সার’-এর সাথে সংযুক্ত। লোড ব্যালেন্সার রিকোয়েস্টগুলো ‘API সার্ভার’-এ বিতরণ করে, যা সবুজ বাক্সের একটি ক্লাস্টার হিসাবে দেখানো হয়েছে। API সার্ভারগুলো ‘নোটিফিকেশন সার্ভার’ (সবুজ বাক্স)-এর সাথে মিথস্ক্রিয়া করে এবং একটি ‘রিয়েল টাইম সার্ভিস’ উপাদানের সাথে যোগাযোগ করে। এই ‘রিয়েল টাইম সার্ভিস’-এ ‘চ্যাট সার্ভার’ এবং ‘প্রেজেন্স সার্ভার’ রয়েছে, উভয়ই বেগুনি বাক্সের ক্লাস্টার হিসাবে উপস্থাপিত। ‘রিয়েল টাইম সার্ভিস’ এবং ‘API সার্ভার’ তিনটি পৃথক ‘KV স্টোর’ ডাটাবেসের (ডাটাবেস আইকন সহ নীল বাক্স) সাথে সংযুক্ত, যা প্রতিটি সার্ভিসের জন্য ডেটা স্থায়িত্বের ইঙ্গিত দেয়। তীর চিহ্নগুলো তথ্য প্রবাহের দিক নির্দেশ করে, দেখায় কীভাবে ইউজার রিকোয়েস্ট প্রসেস করা হয়, ডেটা সংরক্ষণ করা হয় এবং সিস্টেমের মধ্যে নোটিফিকেশন পরিচালনা করা হয়। ‘রিয়েল টাইম সার্ভিস’ এবং ‘KV স্টোর’ ডাটাবেসের চারপাশের ড্যাশ করা রেখাগুলো বোঝায় যে এগুলো সামগ্রিক আর্কিটেকচারের মধ্যে পৃথক লজিক্যাল গ্রুপিং।] চিত্র ৮

চিত্র ৮-এ, ক্লায়েন্ট রিয়েল-টাইম মেসেজিংয়ের জন্য একটি চ্যাট সার্ভারের সাথে একটি স্থায়ী ওয়েবসকেট সংযোগ বজায় রাখে।

  • চ্যাট সার্ভারগুলো মেসেজ পাঠানো/গ্রহণে সহায়তা করে।
  • প্রেজেন্স সার্ভারগুলো অনলাইন/অফলাইন স্ট্যাটাস পরিচালনা করে।
  • API সার্ভারগুলো সবকিছু পরিচালনা করে যার মধ্যে ইউজার লগইন, সাইনআপ, প্রোফাইল পরিবর্তন ইত্যাদি রয়েছে।
  • নোটিফিকেশন সার্ভারগুলো পুশ নোটিফিকেশন পাঠায়।
  • পরিশেষে, কী-ভ্যালু স্টোর (key-value store) চ্যাট হিস্ট্রি সংরক্ষণ করতে ব্যবহৃত হয়। যখন একজন অফলাইন ব্যবহারকারী অনলাইনে আসেন, তখন তিনি তার আগের সমস্ত চ্যাট হিস্ট্রি দেখতে পাবেন।

স্টোরেজ (Storage)

এই মুহূর্তে, আমাদের কাছে সার্ভার প্রস্তুত, সার্ভিসগুলো চলছে এবং থার্ড-পার্টি ইন্টিগ্রেশন সম্পূর্ণ। টেকনিক্যাল স্ট্যাকের গভীরে রয়েছে ডেটা লেয়ার। ডেটা লেয়ারকে সঠিকভাবে পেতে সাধারণত কিছু প্রচেষ্টার প্রয়োজন হয়। আমাদের অবশ্যই একটি গুরুত্বপূর্ণ সিদ্ধান্ত নিতে হবে কোন ধরনের ডাটাবেস ব্যবহার করব তা: রিলেশনাল ডাটাবেস নাকি NoSQL ডাটাবেস? একটি অবগত সিদ্ধান্ত নিতে, আমরা ডেটার ধরন এবং রিড/রাইট প্যাটার্ন পরীক্ষা করব।

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

দ্বিতীয়টি চ্যাট সিস্টেমের জন্য অনন্য: চ্যাট হিস্ট্রি ডেটা। রিড/রাইট প্যাটার্ন বোঝা গুরুত্বপূর্ণ।

চ্যাট সিস্টেমের জন্য ডেটার পরিমাণ বিশাল। একটি আগের গবেষণা [2] প্রকাশ করে যে Facebook messenger এবং Whatsapp প্রতিদিন ৬০ বিলিয়ন মেসেজ প্রসেস করে।

শুধুমাত্র সাম্প্রতিক চ্যাটগুলো ঘন ঘন অ্যাক্সেস করা হয়। ব্যবহারকারীরা সাধারণত পুরানো চ্যাটগুলো খুঁজে দেখেন না।

যদিও খুব সাম্প্রতিক চ্যাট হিস্ট্রি বেশিরভাগ ক্ষেত্রে দেখা হয়, ব্যবহারকারীরা এমন ফিচার ব্যবহার করতে পারেন যার জন্য ডেটার র‍্যান্ডম অ্যাক্সেস প্রয়োজন, যেমন সার্চ, আপনার উল্লেখগুলো (mentions) দেখা, নির্দিষ্ট মেসেজে যাওয়া ইত্যাদি। এই ক্ষেত্রগুলো ডেটা অ্যাক্সেস লেয়ার দ্বারা সমর্থিত হওয়া উচিত।

১-এর-সাথে-১ চ্যাট অ্যাপের জন্য রিড থেকে রাইটের অনুপাত প্রায় ১:১।

আমাদের সমস্ত ইউজ কেস সমর্থন করে এমন সঠিক স্টোরেজ সিস্টেম নির্বাচন করা অত্যন্ত গুরুত্বপূর্ণ। আমরা নিচের কারণগুলোতে কী-ভ্যালু স্টোর সুপারিশ করি:

  • কী-ভ্যালু স্টোরগুলো সহজেই হরাইজন্টাল স্কেলিং (horizontal scaling) করতে দেয়।
  • কী-ভ্যালু স্টোরগুলো ডেটা অ্যাক্সেস করতে খুব কম লেটেন্সি প্রদান করে।
  • রিলেশনাল ডাটাবেস ডেটার লং টেল (long tail) [3] ভালোভাবে পরিচালনা করে না। যখন ইনডেক্সগুলো বড় হয়ে যায়, তখন র‍্যান্ডম অ্যাক্সেস ব্যয়বহুল হয়।
  • কী-ভ্যালু স্টোরগুলো অন্য প্রমাণিত নির্ভরযোগ্য চ্যাট অ্যাপ্লিকেশন দ্বারা গৃহীত হয়েছে। উদাহরণস্বরূপ, Facebook messenger এবং Discord উভয়েই কী-ভ্যালু স্টোর ব্যবহার করে। Facebook messenger HBase [4] ব্যবহার করে, এবং Discord Cassandra [5] ব্যবহার করে।

ডেটা মডেল (Data models)

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

১-এর-সাথে-১ চ্যাটের জন্য মেসেজ টেবিল

চিত্র ৯ ১-এর-সাথে-১ চ্যাটের জন্য মেসেজ টেবিল দেখায়। প্রাইমারি কী হলো message_id, যা মেসেজের ক্রম ঠিক করতে সাহায্য করে। আমরা মেসেজের ক্রম ঠিক করতে created_at-এর ওপর নির্ভর করতে পারি না কারণ দুটি মেসেজ একই সময়ে তৈরি হতে পারে।

[চিত্র ৯-এর বর্ণনা: ছবিটি ‘message’ নামের একটি ডাটাবেস টেবিলের একটি টেবিল স্কিমা উপস্থাপন করে। টেবিলে পাঁচটি কলাম আছে: message_id (একটি bigint ডেটা টাইপ, সম্ভবত প্রাইমারি কী হিসাবে কাজ করে), message_from (একটি bigint, সম্ভবত একটি ইউজার আইডি নির্দেশ করে), message_to (একটি bitint, সম্ভবত একক প্রাপক বা গ্রুপ আইডি নির্দেশ করে), content (মেসেজ বডি সংরক্ষণকারী একটি text ফিল্ড), এবং created_at (মেসেজ তৈরির সময় রেকর্ডকারী একটি timestamp)। স্কিমাটি একটি ট্যাবুলার ফরম্যাটে উপস্থাপন করা হয়েছে বাম দিকে কলামের নাম এবং ডান দিকে তাদের সংশ্লিষ্ট ডেটা টাইপ সহ, সবগুলোই একটি বাক্সের ভেতরে যার হেডারে গাঢ় নীল রঙে ‘message’ টেবিলের নাম নির্দেশ করা হয়েছে। কোনো সংযোগ বা তথ্য প্রবাহ চিত্রিত করা হয়নি; এটি কেবল ‘message’ টেবিলের কাঠামো সংজ্ঞায়িত করে।] চিত্র ৯

গ্রুপ চ্যাটের জন্য মেসেজ টেবিল

চিত্র ১০ গ্রুপ চ্যাটের জন্য মেসেজ টেবিল দেখায়। কম্পোজিট প্রাইমারি কী হলো (channel_id, message_id)। চ্যানেল এবং গ্রুপ এখানে একই অর্থ বহন করে। channel_id হলো পার্টিশন কী কারণ গ্রুপ চ্যাটের সমস্ত কুয়েরি একটি চ্যানেলে অপারেট করে।

[চিত্র ১০-এর বর্ণনা: ছবিটি group_message নামের একটি ডাটাবেস টেবিলের একটি টেবিল স্কিমা উপস্থাপন করে। টেবিলে পাঁচটি কলাম আছে: channel_id (bigint টাইপের), message_id (এছাড়াও bigint), user_id (bigint), content (text), এবং created_at (timestamp)। টেবিলটি গ্রুপের ভেতরে মেসেজের তথ্য সংরক্ষণ করে বলে মনে হয়, যেখানে প্রতিটি সারি একটি একক মেসেজ নির্দেশ করে। channel_id সম্ভবত গ্রুপ চ্যাট বা চ্যানেল শনাক্ত করে যেখানে মেসেজটি পাঠানো হয়েছিল, message_id মেসেজটিকে অনন্যভাবে শনাক্ত করে, user_id মেসেজের প্রেরক নির্দিষ্ট করে, content মেসেজের টেক্সট ধারণ করে, এবং created_at মেসেজের টাইমস্ট্যাম্প রেকর্ড করে। টেবিলের কাঠামোর মধ্যে কলামের সংজ্ঞা ছাড়া অন্য কোনো সংযোগ বা তথ্য প্রবাহ চিত্রিত করা হয়নি।] চিত্র ১০

মেসেজ আইডি (Message ID)

কীভাবে message_id তৈরি করা হবে তা একটি আকর্ষণীয় বিষয় যা অন্বেষণের যোগ্য। Message_id মেসেজের ক্রম নিশ্চিত করার দায়িত্ব বহন করে। মেসেজের ক্রম নিশ্চিত করতে, message_id-কে নিচের দুটি শর্ত পূরণ করতে হবে:

  • আইডিগুলো অবশ্যই অনন্য (unique) হতে হবে।
  • আইডিগুলো সময় অনুযায়ী সাজানোযোগ্য হতে হবে, অর্থাৎ নতুন সারিগুলোর আইডি পুরানোগুলোর চেয়ে বেশি হতে হবে।

আমরা কীভাবে এই দুটি নিশ্চয়তা অর্জন করতে পারি? প্রথমেই মাথায় আসে MySql-এর “auto_increment” কীওয়ার্ডের কথা। তবে, NoSQL ডাটাবেসগুলো সাধারণত এমন ফিচার প্রদান করে না।

দ্বিতীয় পদ্ধতি হলো Snowflake [6]-এর মতো একটি গ্লোবাল ৬৪-বিট সিকোয়েন্স নম্বর জেনারেটর ব্যবহার করা। এটি “ডিজাইন আ ইউনিক আইডি জেনারেটর ইন আ ডিস্ট্রিবিউটেড সিস্টেম” অধ্যায়ে আলোচনা করা হয়েছে।

চূড়ান্ত পদ্ধতি হলো লোকাল সিকোয়েন্স নম্বর জেনারেটর ব্যবহার করা। লোকাল এর অর্থ হলো আইডিগুলো শুধুমাত্র একটি গ্রুপের মধ্যে অনন্য। লোকাল আইডি কাজ করার কারণ হলো এক-এর-সাথে-এক চ্যানেল বা গ্রুপ চ্যানেলের মধ্যে মেসেজের ক্রম বজায় রাখাই যথেষ্ট। গ্লোবাল আইডি বাস্তবায়নের তুলনায় এই পদ্ধতিটি বাস্তবায়ন করা সহজ।