راهنمای جامع ساخت چت‌روم توزیع‌شده سطح Production با زبان Go

ایجاد شده توسط Admin در مقالات 14 فوریه 2026
اشتراک گذاری

معرفی چت روم توزیع شده



اگر تا به حال کنجکاو بوده‌اید که برنامه‌های چتی مانند Slack، Discord یا WhatsApp در پشت صحنه چگونه کار می‌کنند، این آموزش به شما نشان خواهد داد. یک سرور چت بلادرنگ را از پایه و با استفاده از زبان Go خواهید ساخت و مفاهیم بنیادینی که سیستم‌های ارتباطی مدرن را قدرتمند می‌کنند، یاد خواهید گرفت. در پایان این راهنما، شما یک چت روم عملی ساخته‌اید که از کاربران همزمان نامحدودی پشتیبانی می‌کند، پیام‌ها را حتی پس از crash سرور حفظ می‌کند، مدیریت session را برای اتصال مجدد کاربران پس از قطعی شبکه فراهم می‌کند و به طور ظریفی با کلاینت‌های کند یا قطع شده برخورد می‌کند.



چت روم توزیع شده چیست؟



یک چت روم، سروری است که به چندین کاربر اجازه می‌دهد به طور همزمان متصل شده و پیام‌ها را به صورت بلادرنگ مبادله کنند. وقتی از عبارت "در سطح production" استفاده می‌کنیم، منظورمان این است که شامل ویژگی‌هایی است که در یک برنامه واقعی انتظار دارید: داده‌ها را به گونه‌ای ذخیره می‌کند که پیام‌ها پس از راه‌اندازی مجدد سرور از بین نروند، قطعی‌های شبکه را به صورت ظریف مدیریت می‌کند و می‌تواند از کاربران همزمان بسیاری پشتیبانی کند بدون آنکه سرعت آن کاهش یابد. جنبه "توزیع شده" به نحوه مدیریت سیستم برای اتصال چندین کلاینت از مکان‌های مختلف اشاره دارد که همگی سعی دارند به طور همزمان پیام ارسال و دریافت کنند.



چالش‌های سیستم‌های توزیع شده



این معماری چالش‌های جالبی را معرفی می‌کند: چگونه اطمینان حاصل می‌کنید که همه پیام‌ها را به یک ترتیب مشاهده می‌کنند؟ چگونه با کلاینت‌هایی که اتصال اینترنتی کندی دارند برخورد می‌کنید؟ اگر کسی به طور غیرمنتظره قطع ارتباط کند چه اتفاقی می‌افتد؟ این‌ها فقط مشکلات تئوری نیستند. هر برنامه شبکه‌ای با همزمانی، مدیریت state و مدیریت خطا سر و کار دارد. چه در حال ساخت یک برنامه چت باشید، چه یک بازی چندنفره، یک ویرایشگر مشارکتی یا یک پلتفرم معاملاتی، با چالش‌های مشابهی روبرو خواهید شد. الگوهایی که در اینجا یاد می‌گیرید به طور گسترده در سیستم‌های توزیع شده کاربرد دارند.



چرا چت روم یک پروژه آموزشی عالی است؟



برنامه‌های چت پروژه‌های یادگیری عالی هستند زیرا چندین مشکل چالش‌برانگیز را در یک مکان ترکیب می‌کنند. شما نیاز دارید اتصالات همزمان را به صورت ایمن مدیریت کنید، پیام‌ها را به چندین کلاینت بدون مسدود کردن broadcast کنید، با شبکه‌های غیرقابل اعتماد برخورد کنید، داده‌ها را به صورت پایدار ذخیره کنید و اطمینان حاصل کنید که سیستم به طور ظریفی از crash بازیابی می‌شود. هر یک از این موضوعات می‌تواند آموزش جداگانه‌ای باشد، اما در اینجا خواهید دید که چگونه در یک برنامه واقعی با هم کار می‌کنند.



مفاهیم اصلی که یاد خواهید گرفت



این آموزش چندین مفهوم مهم را نشان می‌دهد که برای ساخت سیستم‌های توزیع شده اساسی هستند:




  • برنامه‌نویسی شبکه با سوکت‌های TCP: نحوه پذیرش اتصالات TCP ورودی، خواندن و نوشتن داده از طریق سوکت‌های شبکه و مدیریت ظریف خطاهای اتصال را یاد خواهید گرفت.

  • همزمانی در Go: خواهید دید که چگونه از goroutineها برای مدیریت چندین کلاینت به طور همزمان بدون مسدود کردن استفاده کنید. از کانال‌ها برای هماهنگی ایمن بین goroutineها استفاده خواهید کرد.

  • مدیریت state مشترک: یاد خواهید گرفت که چه زمانی از mutexها در مقابل کانال‌ها استفاده کنید، چگونه granularity قفل را طراحی کنید تا از گلوگاه‌ها جلوگیری کنید و چگونه consistency داده را هنگام دسترسی چندین goroutine به داده یکسان تضمین کنید.

  • پایداری داده با Write-Ahead Logging (WAL): الگویی مشابه پایگاه‌های داده را پیاده‌سازی خواهید کرد و یاد خواهید گرفت که چگونه بین پایداری و عملکرد تعادل برقرار کنید.

  • مدیریت session برای اتصال مجدد: یک سیستم session مبتنی بر token خواهید ساخت که به کاربران اجازه می‌دهد به طور یکپارچه دوباره متصل شوند.

  • طراحی برای خطا (Graceful Degradation): یاد خواهید گرفت که چگونه از تأثیر کلاینت‌های کند بر کلاینت‌های سریع جلوگیری کنید، چگونه هنگام شکست persistence به کار خود ادامه دهید و چگونه منابع را هنگام بروز مشکل به درستی پاک کنید.



نمونه‌ای از معماری سیستم



این سیستم از یک معماری client-server پیروی می‌کند که در آن اجزای داخلی با هم کار می‌کنند تا یک تجربه چت robust ارائه دهند. قلب سیستم یک حلقه رویداد (Event Loop) است - یک goroutine که تمام تغییرات state را به صورت ترتیبی مدیریت می‌کند. این حلقه از چندین کانال برای هماهنگی استفاده می‌کند: یک کانال برای اتصال کلاینت‌های جدید، کانالی برای broadcast پیام‌ها، و کانال‌هایی برای مدیریت خروج و پیام‌های خصوصی. این طراحی تضمین می‌کند که هیچ شرایط رقابتی (Race Condition) روی state وجود ندارد و ترتیب کلی رویدادها حفظ می‌شود.



برای پایداری، از یک رویکرد دو مرحله‌ای استفاده می‌شود: یک Write-Ahead Log (WAL) که هر پیام را بلافاصله در یک فایل append-only ذخیره می‌کند، و Snapshots دوره‌ای که state کامل چت را در یک فایل جداگانه ذخیره می‌کنند. این سیستم بازیابی سریع پس از crash را ممکن می‌سازد. در نهایت، یک سیستم مدیریت session مبتنی بر token به کاربران اجازه می‌دهد پس از قطع ارتباط، با حفظ هویت و تاریخچه چت خود، دوباره متصل شوند.



با تکمیل این پروژه، نه تنها یک چت روم کاملاً عملی خواهید داشت، بلکه درک عمیقی از نحوه عملکرد سیستم‌های توزیع شده، مدیریت همزمانی، persistence و بازیابی از خطا به دست خواهید آورد.



مفاهیم و معماری سیستم



ساخت یک سرور چت توزیع‌شده با قابلیت استفاده در محیط‌های عملیاتی، مستلزم درک عمیق معماری و مفاهیم بنیادین سیستم‌های توزیع‌شده است. این سیستم‌ها باید قادر به مدیریت اتصالات همزمان نامحدود، حفظ پیام‌ها در صورت بروز خرابی سرور، مدیریت نشست برای اتصال مجدد کاربران پس از قطعی شبکه و رسیدگی به مشتریان کند یا قطع‌شده باشند. معماری چنین سیستمی بر پایه مدل کلاینت-سرور بنا شده و با استفاده از الگوهای همزمانی و پایداری داده، تجربه‌ی چتی روان و قابل اعتماد را فراهم می‌کند.



تعریف یک چت‌روم توزیع‌شده



یک چت‌روم توزیع‌شده سروری است که به چندین کاربر اجازه می‌دهد به صورت همزمان متصل شده و پیام‌ها را به صورت بلادرنگ مبادله کنند. ویژگی "قابل استفاده در محیط عملیاتی" به این معنی است که سیستم شامل قابلیت‌هایی است که در یک برنامه واقعی انتظار می‌رود: داده را به گونه‌ای حفظ می‌کند که پیام‌ها پس از راه‌اندازی مجدد سرور از بین نروند، خرابی‌های شبکه را به شکلی مناسب مدیریت می‌کند و می‌تواند از تعداد زیادی کاربر همزمان بدون کاهش سرعت پشتیبانی کند. جنبه "توزیع‌شده" به نحوه مدیریت سیستم از سوی چندین کلاینت متصل شده از مکان‌های مختلف اشاره دارد که همگی سعی در ارسال و دریافت پیام در یک زمان دارند.



بررسی اجمالی معماری سیستم



معماری این سیستم از اجزای داخلی تشکیل شده که برای ارائه یک تجربه چت قوی با یکدیگر همکاری می‌کنند. در هسته سیستم، یک حلقه رویداد قرار دارد که یک گوروتین تنها است و تمام تغییرات حالت را به صورت ترتیبی پردازش می‌کند. این حلقه از طریق کانال‌های مختلفی با دیگر بخش‌های سیستم ارتباط برقرار می‌کند. برای مدیریت اتصالات، یک شنونده TCP روی پورت 9000 به درخواست‌های اتصال ورودی گوش می‌دهد. برای هر اتصال کلاینت جدید، دو گوروتین مجزا ایجاد می‌شود: یکی برای خواندن پیام‌های ورودی از کلاینت و دیگری برای نوشتن پیام‌های خروجی به کلاینت. ساختارهای داده اصلی که حالت سیستم را نگهداری می‌کنند و توسط موتکس‌ها محافظت می‌شوند، شامل یک نگاشت از کلاینت‌های فعال، یک نگاشت از نشست‌های کاربر برای اتصال مجدد و یک برش از تاریخچه پیام‌ها هستند.



مدل همزمانی: ارتباط از طریق کانال‌ها



این چت‌روم از مدل CSP زبان گو استفاده می‌کند که رویکردی اساساً متفاوت نسبت به برنامه‌نویسی همزمان در سایر زبان‌ها ارائه می‌دهد. به جای محافظت از حافظه مشترک با قفل‌ها، گو رویکرد "به اشتراک گذاری حافظه با برقراری ارتباط" را ترویج می‌کند. در این مدل، داده‌ها از طریق کانال‌ها بین گوروتین‌ها منتقل می‌شوند و در هر زمان تنها یک گوروتین مالک داده است. این طراحی بسیاری از باگ‌های همزمانی را از بین می‌برد. کانال‌ها مزایای متعددی دارند: حذف شرایط رقابت، ارائه کنترل جریان طبیعی، تسهیل در ردیابی جریان پیام و ترکیب‌پذیری بهتر. در این پروژه از پنج کانال مجزا برای انواع رویدادها استفاده شده است. حلقه رویداد اصلی با استفاده از یک عبارت select از همه این کانال‌ها دریافت می‌کند، به این معنی که تمام تغییرات حالت به صورت ترتیبی و در یک مکان اتفاق می‌افتند که درک سیستم را بسیار آسان‌تر می‌کند.



استراتژی پایداری داده: WAL و اسنپ‌شات‌ها



برای اطمینان از بقای تاریخچه چت پس از خرابی سرور، از یک رویکرد دو مرحله‌ای مشابه پایگاه‌های داده واقعی استفاده می‌شود: ثبت پیش‌نویس و اسنپ‌شات. Wال مکانیزم اصلی پایداری است. هر پیام بلافاصله به یک فایل append-only به نام messages.wal اضافه می‌شود. پس از نوشتن هر پیام، فراخوانی fsync تضمین می‌کند که داده بلافاصله روی دیسک فیزیکی نوشته شود. مشکل ثبت پیش‌نویس این است که برای همیشه رشد می‌کند. اسنپ‌شات‌ها این مشکل را حل می‌کنند. هر 5 دقیقه، اگر بیش از 100 پیام جدید وجود داشته باشد، کل تاریخچه پیام در یک فایل جداگانه به نام snapshot.json ذخیره می‌شود. پس از ایجاد اسنپ‌شات، WAL خالی می‌شود. هنگام راه‌اندازی سرور، ابتدا فایل اسنپ‌شات بارگیری می‌شود و سپس مدخل‌های WAL که پس از آخرین اسنپ‌شات نوشته شده‌اند، بازپخش می‌شوند. این سیستم دو مرحله‌ای بهترین هر دو جهان را ارائه می‌دهد: نوشتن سریع در حین عملکرد عادی با WAL، بازیابی سریع پس از خرابی با اسنپ‌شات به علاوه بازپخش WAL کوچک، پایداری تضمین‌شده از طریق fsync و زمان بازیابی محدود.



مدیریت نشست برای اتصال مجدد بی‌درنگ



شبکه‌ها غیرقابل اعتماد هستند. کاربران قطع ارتباط می‌کنند، وای‌فای قطع می‌شود و اتصالات موبایل بین دکل‌ها جابجا می‌شوند. یک سیستم مدیریت نشست مبتنی بر توکن به کاربران اجازه می‌دهد پس از قطعی‌های شبکه به طور بی‌درنگ به سرور متصل شوند، بدون اینکه نیاز به ایجاد حساب کاربری جدید یا وارد کردن مجدد اطلاعات داشته باشند. این سیستم یک توکن منحصربه‌فرد برای هر کاربر تولید می‌کند که زمان‌دار بوده و معمولاً پس از یک ساعت منقضی می‌شود. این توکن تاریخچه چت و هویت کاربر را بدون نیاز به رمزهای عبور یا احراز هویت پیچیده حفظ می‌کند.



سفر یک پیام در سیستم



برای درک نحوه تعامل این اجزا، مسیر یک پیام را در سیستم دنبال می‌کنیم. هنگامی که یک کاربر پیامی را ارسال می‌کند، ابتدا توسط گوروتین خواندن کلاینت دریافت شده و به کانال broadcast ارسال می‌شود. حلقه رویداد اصلی این پیام را از کانال دریافت کرده و آن را به Wال می‌نویسد تا پایداری آن تضمین شود. سپس پیام به تاریخچه پیام در حافظه اضافه می‌شود. در نهایت، حلقه رویداد پیام را به کانال‌های outgoing هر یک از کلاینت‌های فعال می‌فرستد. هر گوروتین نوشتن کلاینت مسئولیت ارسال پیام از طریق اتصال TCP مربوطه را بر عهده دارد. کانال broadcast به عنوان یک نقطه همگام‌سازی عمل می‌کند و ترتیب کامل پیام را تضمین می‌کند.



جمع‌بندی معماری



این معماری با ترکیب حلقه رویداد تک‌نخی برای هماهنگی، گوروتین‌های متعدد برای مدیریت اتصالات، کانال‌ها برای انتقال ایمن داده و موتکس‌ها برای محافظت از داده‌های حالت مشترک، تعادلی بین سادگی، عملکرد و قابلیت اطمینان برقرار می‌کند. استراتژی پایداری دوگانه اطمینان حاصل می‌کند که داده از دست نرود، در حالی که مدیریت نشست انعطاف‌پذیری لازم در برابر مشکلات شبکه را فراهم می‌آورد. الگوها و مفاهیم به کار رفته در این معماری، از برنامه‌نویسی همزمان گرفته تا مدیریت حالت و تحمل خطا، به طور گسترده در سیستم‌های توزیع‌شده از پایگاه‌های داده و صف‌های پیام تا سرورهای وب قابل اعمال هستند.



پیاده‌سازی سرور و اتصال کلاینت



پیاده‌سازی سرور و مدیریت اتصالات کلاینت‌ها، هسته اصلی یک چت‌روم توزیع‌شده را تشکیل می‌دهد. این فرآیند شامل ایجاد یک سرور TCP که بتواند به صورت همزمان به تعداد نامحدودی از کاربران سرویس دهد، مدیریت جریان پیام‌ها بین کلاینت‌ها، و تضمین پایداری داده‌ها حتی در صورت بروز خرابی می‌شود. در این بخش، مراحل کلیدی راه‌اندازی سرور و نحوه برقراری ارتباط ایمن و کارآمد کلاینت‌ها را بررسی می‌کنیم.



معماری سرور و حلقه رویداد (Event Loop)



سرور چت بر اساس معماری client-server و با استفاده از قابلیت‌های همزمانی زبان Go ساخته می‌شود. یک listener TCP روی پورت 9000 برای پذیرش اتصالات ورودی تنظیم می‌شود. زمانی که یک کلاینت متصل می‌شود، سرور برای مدیریت آن دو goroutine مجزا ایجاد می‌کند: یکی برای خواندن پیام‌های ارسالی از سمت کلاینت و دیگری برای ارسال پیام‌ها به کلاینت. قلب این سیستم، یک حلقه رویداد (Event Loop) است که در یک goroutine واحد اجرا می‌شود. این حلقه از طریق کانال‌های مختلفی مانند join، leave، و broadcast، تمامی رویدادهای سیستم (اتصال جدید، خروج کاربر، پیام جدید و ...) را به ترتیب دریافت و پردازش می‌کند. این طراحی تضمین می‌کند که تمام تغییرات حالت به صورت ترتیبی و بدون شرایط رقابت (Race Condition) انجام می‌شود، زیرا تنها یک goroutine مجاز به تغییر داده‌های اشتراکی مانند نگاشت کلاینت‌های فعال و تاریخچه پیام‌ها است.



مدیریت اتصال کلاینت‌ها و گوروتین‌ها



فرآیند مدیریت هر اتصال کلاینت با تابع handleClient آغاز می‌شود. این تابع ابتدا یک مهلت 30 ثانیه‌ای برای وارد کردن نام کاربری توسط کلاینت تعیین می‌کند تا از اشغال بی‌جهت منابع توسط اتصالات غیرفعال جلوگیری شود. پس از تأیید نام کاربری، یک ساختار داده Client ایجاد می‌شود که حاوی اتصال TCP و یک کانال خروجی بافر شده با ظرفیت 10 پیام است. وجود این بافر بسیار حیاتی است؛ زیرا امکان می‌دهد تا در صورت کند بودن سرعت ارسال پیام به یک کلاینت خاص (مثلاً به دلیل مشکل شبکه)، پیام‌های بعدی در صف قرار گیرند و از مسدود شدن فرآیند broadcast برای سایر کلاینت‌های سریع جلوگیری شود. سپس، دو گوروتین مجزا برای مدیریت خواندن و نوشتن به طور همزمان راه‌اندازی می‌گردند. گوروتین خواننده به طور مداوم پیام‌های ورودی از کلاینت را می‌خواند و به کانال broadcast سرور ارسال می‌کند. گوروتین نویسنده نیز به طور پیوسته پیام‌های موجود در کانال خروجی کلاینت را از بافر خوانده و از طریق اتصال TCP برای کلاینت ارسال می‌نماید. این جداسازی تضمین می‌کند که عمل ارسال پیام به یک کلاینت کند، مانع از دریافت پیام‌های جدید از همان کلاینت نشود.



برقراری‌سازی broadcast و مدیریت پیام‌ها



هنگامی که یک پیام از طریق کانال broadcast به حلقه رویداد سرور می‌رسد، چندین عمل مهم به صورت ترتیبی انجام می‌شود. ابتدا پیام برای ماندگاری بلندمدت در فایل Write-Ahead Log یا WAL نوشته می‌شود. این فایل به صورت append-only است و پس از هر نوشتن، با فراخوانی fsync، داده بلافاصله روی دیسک فیزیکی ذخیره می‌شود تا در صورت crash کردن سرور، پیام از دست نرود. سپس، پیام به لیست تاریخچه پیام‌های موجود در حافظه اضافه می‌شود. در نهایت، پیام به تمام کلاینت‌های متصل ارسال می‌گردد. برای انجام این ارسال، سرور از یک الگوی non-blocking استفاده می‌کند. به این صورت که برای هر کلاینت، پیام جدید را به کانال خروجی آن کلاینت می‌فرستد. اگر این کانال به دلیل پر بودن بافر (به علت کندی کلاینت) قادر به پذیرش پیام جدید نباشد، عمل ارسال برای آن کلاینت خاص نادیده گرفته می‌شود تا فرآیند برای دیگران متوقف نشود. این رویکرد که "تخریب graceful" نامیده می‌شود، باعث می‌شود سیستم حتی در صورت وجود مشکلات جزئی در برخی اجزا، به کار خود ادامه دهد.



نکات کلیدی در پیاده‌سازی




  • استفاده از کانال‌های unbuffered برای هماهنگی: کانال‌های اصلی رویداد (مانند join و broadcast) unbuffered هستند تا ارسال‌کننده تا زمان پردازش رویداد توسط حلقه اصلی منتظر بماند و ترتیب رویدادها کاملاً حفظ شود.

  • مدیریت منابع: یک گوروتین جداگانه هر 30 ثانیه کلاینت‌های غیرفعال (مثلاً کاربرانی که بیش از 5 دقیقه هیچ فعالیتی نداشته‌اند) را شناسایی و قطع ارتباط می‌کند.

  • برخورد صحیح با قطع ارتباط: در صورت قطع غیرمنتظره اتصال یک کلاینت، سرور به درستی کانال خروجی آن را می‌بندد و با ارسال یک پیام به کانال leave، سایر کاربران را از خروج آن مطلع می‌سازد.

  • بازیابی حالت اولیه: هنگام راه‌اندازی سرور، ابتدا snapshot آخرین state بارگذاری شده و سپس رکوردهای WAL که پس از آن snapshot نوشته شده‌اند، replay می‌شوند تا هیچ پیامی از دست نرود.



در نهایت، با پیاده‌سازی این مراحل، یک سرور چت‌روم پایدار و scalable خواهید داشت که می‌تواند پایه‌ای برای سیستم‌های توزیع‌شده پیچیده‌تر باشد. درک این مفاهیم نه تنها برای ساخت چت‌روم، بلکه برای توسعه هرگونه سرویس شبکه‌ای که با چالش‌های همزمانی، persistence و تحمل خطا روبرو است، ضروری می‌باشد.



مدیریت پیام‌ها و دستورات



قلب تپنده چت: حلقه رویداد و پخش پیام


در هسته مرکزی سرور چت، یک حلقه رویداد (Event Loop) قرار دارد که تمامی تغییرات حالت سیستم را به صورت ترتیبی و در یک مکان مدیریت می‌کند. این حلقه، که در یک گوروتین (Goroutine) واحد اجرا می‌شود، بر روی پنج کانال مختلف نظارت می‌کند: پیوستن کاربر جدید (join)، ترک کردن (leave)، پخش پیام (broadcast)، درخواست لیست کاربران (listUsers) و پیام خصوصی (directMessage). زمانی که یک پیام از طرف یک کلاینت ارسال می‌شود، این پیام به کانال broadcast ارسال شده و توسط حلقه رویداد دریافت می‌شود. سپس تابع handleBroadcast فراخوانی می‌شود. این تابع ابتدا پیام را در Write-Ahead Log (WAL) برای ماندگاری داده می‌نویسد و سپس آن را به کانال‌های outgoing تمام کلاینت‌های متصل فعال ارسال می‌کند. استفاده از این معماری مبتنی بر کانال‌ها تضمین می‌کند که تمامی پیام‌ها به ترتیب رسیدنشان به همه کاربران ارسال می‌شوند و از شرایط رقابت (Race Condition) جلوگیری می‌شود.



سیستم دستورات: تعامل فراتر از پیام‌های عمومی


برای غنی‌تر کردن تجربه کاربری، یک سیستم دستورات پیاده‌سازی شده است. دستورات، پیام‌هایی هستند که با یک اسلش (/) شروع می‌شوند و به جای پخش عمومی، یک عمل خاص را انجام می‌دهند. این سیستم در تابع handleClient و در گوروتین readMessages پردازش می‌شود. هنگامی که یک خط ورودی از کلاینت با '/' شروع شود، به عنوان یک دستور تفسیر شده و به جای ارسال به کانال broadcast، به صورت منطقی پردازش می‌شود. این الگو مشابه چیزی است که در برنامه‌هایی مانند Slack و Discord مشاهده می‌کنید و امکان عملکردهای پیشرفته را فراهم می‌کند.



  • دستور /users: لیستی از تمام کاربران متصل به همراه وضعیت آن‌ها (مانند "غیرفعال" در صورت عدم فعالیت بیش از یک دقیقه) و آمار کلی سرور را نمایش می‌دهد.

  • دستور /msg [username] [message]: امکان ارسال پیام خصوصی به یک کاربر خاص را فراهم می‌کند. این پیام فقط برای فرستنده و گیرنده مشخص شده ارسال می‌شود.

  • دستور /history [number]: امکان مشاهده تاریخچه پیام‌ها را می‌دهد. به طور پیش‌فرض، کاربران هنگام پیوستن، ۱۰ پیام آخر را دریافت می‌کنند.

  • دستور /stats: آمار شخصی کاربر، شامل تعداد پیام‌های ارسالی و دریافتی و زمان آخرین فعالیت را نشان می‌دهد.

  • دستور /token: یک توکن امن برای اتصال مجدد (Reconnection) ایجاد می‌کند تا کاربر پس از قطع ارتباط بتواند بدون از دست دادن هویت خود به چت بازگردد.

  • دستور /quit: اتصال کلاینت را به صورت graceful می‌بندد.



مدیریت ترافیک و کلاینت‌های کند: تخریب graceful


در دنیای واقعی، سرعت اتصال کلاینت‌ها یکسان نیست. یک چالش اساسی در هر سیستم چندکاربره، مدیریت کلاینت‌های کند بدون تأثیرگذاری بر تجربه دیگر کاربران است. برای حل این مشکل از دو تکنیک اصلی استفاده شده است. اولاً، هر کلاینت یک کانال outgoing بافر شده با ظرفیت ۱۰ پیام دارد. این بافر امکان صف‌بندی پیام‌ها را فراهم می‌کند و اگر یک کلاینت موقتاً کند شود، سیستم می‌تواند تا ۱۰ پیام را برای او ذخیره کند بدون اینکه ارسال پیام برای دیگران مسدود شود. ثانیاً، در الگوریتم پخش پیام، از ارسال non-blocking استفاده می‌شود. اگر بافر یک کلاینت به دلیل کندی مداوم پر باشد، سیستم به جای مسدود کردن و انتظار، پیام را برای آن کلاینت نادیده گرفته و به ارسال برای دیگران ادامه می‌دهد. این به معنای از دست رفتن برخی پیام‌ها برای کلاینت کند است، اما از توقف کامل سیستم جلوگیری می‌کند. این رویکرد "تخریب graceful" نامیده می‌شود: سیستم حتی زمانی که بخشی از آن با مشکل مواجه می‌شود به کار خود ادامه می‌دهد.



نمونه سناریو: مسیر یک پیام از فرستنده به گیرندگان


برای درک بهتر همکاری این مؤلفه‌ها، مسیر یک پیام نمونه را دنبال می‌کنیم. فرض کنید کاربر "Alice" پیام "Hello everyone!" را ارسال می‌کند. ۱) پیام از طریق اتصال TCP کلاینت Alice به سرور می‌رسد. ۲) گوروتین readMessages مربوط به Alice این خط را می‌خواند. ۳) از آنجا که با '/' شروع نمی‌شود، به عنوان یک پیام عادی شناسایی شده و به کانال broadcast سرور ارسال می‌شود. ۴) حلقه رویداد اصلی سرور این پیام را از کانال broadcast دریافت می‌کند. ۵) حلقه رویداد تابع handleBroadcast را فراخوانی می‌کند. ۶) این تابع ابتدا پیام را در فایل Wال می‌نویسد و fsync می‌کند تا از ماندگاری آن روی دیسک اطمینان حاصل شود. ۷) سپس پیام را به کانال outgoing هر یک از کلاینت‌های فعال موجود در نقشه clients ارسال می‌کند. ۸) گوروتین writeMessages هر کلاینت (مثلاً Bob و John)، پیام را از کانال outgoing خود خوانده و از طریق اتصال شبکه به کلاینت مربوطه ارسال می‌کند. تمامی این مراحل به لطف هماهنگی через کانال‌ها و موتکس‌ها، به صورت همزمان اما بدون تداخل انجام می‌شود.



تست، استقرار و توسعه

روش‌های تست سیستم چت‌روم

تست‌کردن یک سیستم همزمان مانند چت‌روم نیازمند رویکردی متفاوت نسبت به کدهای ترتیبی معمولی است. شما باید مطمئن شوید که گوروتین‌ها به‌درستی هماهنگ می‌شوند، پیام‌ها به ترتیب صحیح می‌رسند و سیستم موارد حاشیه‌ای مانند قطع‌اتصال را به خوبی مدیریت می‌کند. تست‌های واحد، کامپوننت‌های مجزا را در انزوا تأیید می‌کنند. برای چت‌روم شما، مهم‌ترین تست، تأیید صحت ارسال پیام به تمام کلاینت‌های متصل است.

تست‌های یکپارچه‌سازی

تست‌های یکپارچه‌سازی، عملکرد کل سیستم در کنار هم را تأیید می‌کنند - سرور واقعی، کلاینت‌های واقعی و اتصالات شبکه واقعی. برخلاف تست‌های واحد که کامپوننت‌ها را mock می‌کنند، تست‌های یکپارچه‌سازی تمام stack را تمرین می‌دهند. این تست‌ها مشکلاتی را شناسایی می‌کنند که تست‌های واحد از قلم می‌اندازند، مانند timeoutهای شبکه، مسائل ترتیب پیام در بین چندین کلاینت، یا مشکلات مربوط به ایجاد و قفل‌شدن فایل WAL.

استقرار با Systemd و Docker

استقرار چت‌روم شما به معنای اجرای آن روی سروری است که 24/7 فعال می‌ماند، در صورت crash به‌طور خودکار restart می‌شود و هنگام boot سرور شروع به کار می‌کند. Systemd سیستم init استاندارد در اکثر توزیع‌های لینوکس است. این سرویس‌ها را مدیریت می‌کند، restartها را کنترل می‌کند و مطمئن می‌شود که چت‌روم شما هنگام boot شروع به کار کند. Docker برنامه شما را با تمام وابستگی‌های آن بسته‌بندی می‌کند و استقرار آن را در هر جایی که Docker اجرا می‌شود، آسان می‌سازد. این از یک build چندمرحله‌ای استفاده می‌کند که image نهایی را کوچک و بهینه نگه می‌دارد.

جمع‌بندی و توصیه‌های نهایی

شما اکنون یک چت‌روم توزیع‌شده آماده production را از ابتدا ساخته‌اید. این پروژه مفاهیم مهم سیستم‌های توزیع‌شده از جمله الگوهای همزمانی، برنامه‌نویسی شبکه، مدیریت state، persistence و تحمل خطا را نشان می‌دهد. برای توسعه بیشتر، می‌توانید قابلیت‌هایی مانند کانال‌های چندگانه، احراز هویت پیشرفته، آپلود فایل و پشتیبانی از WebSocket را اضافه کنید. برای مقیاس‌پذیری عظیم، می‌توانید چت‌روم را بین چندین سرور shard کنید. کد کامل این پروژه در GitHub در دسترس است. این راهنما پایه‌ای محکم برای درک و ساخت سیستم‌های توزیع‌شده واقعی با استفاده از قدرت همزمانی Go فراهم می‌کند.

نظرات (0)

اشتراک گذاری

این پست را با دیگران به اشتراک بگذارید

تنظیمات GDPR

When you visit any of our websites, it may store or retrieve information on your browser, mostly in the form of cookies. This information might be about you, your preferences or your device and is mostly used to make the site work as you expect it to. The information does not usually directly identify you, but it can give you a more personalized web experience. Because we respect your right to privacy, you can choose not to allow some types of cookies. Click on the different category headings to find out more and manage your preferences. Please note, that blocking some types of cookies may impact your experience of the site and the services we are able to offer.