dotNet full-stack web developer
آموزش طراحی سیستم: سه مفهوم سیستم های توزیع شده که باید بدانید
در این پست، سه مفهوم طراحی سیستم را مورد بحث قرار خواهیم داد که می توان از آنها برای حل مشکلات طراحی مربوط به سیستم های توزیع شده استفاده کرد. از آنجایی که این مفاهیم را می توان برای انواع سیستم های توزیع شده به کار برد، در مصاحبه های طراحی سیستم بسیار مفید هستند.
در اینجا لیستی از مفاهیمی است که در مورد آنها بحث خواهیم کرد:
Heartbeat
Split Brain
Merkle Tree
1. تپش قلب
زمینه:
در یک محیط توزیع شده، داده ها (یا کار) بین سرورها توزیع می شود. چنین تنظیماتی به سرورها نیاز دارد که بدانند چه سرورهای دیگری بخشی از سیستم هستند تا درخواستها را به طور مؤثر هدایت کنند. علاوه بر این، سرورها باید بتوانند تشخیص دهند که آیا سرورهای دیگر آماده و در حال اجرا هستند یا خیر. در یک محیط غیرمتمرکز، هر زمان که درخواستی به یک سرور می رسد، سرور باید بتواند تصمیم بگیرد که کدام سرور مسئول رسیدگی به آن درخواست است. به این ترتیب، تشخیص به موقع خرابی سرور بسیار مهم است، و سیستم را قادر می سازد تا اقدامات اصلاحی انجام دهد و داده ها (یا کار) را به سرور سالم دیگری منتقل کند و از خراب شدن بیشتر محیط جلوگیری کند.
تعریف:
در یک محیط توزیع شده، هر سرور به طور دوره ای یک پیام Heartbeat را به یک سرور مانیتورینگ مرکزی یا سرورهای دیگر در سیستم ارسال می کند تا نشان دهد که هنوز زنده و کار می کند.
راه حل:
ضربان قلب یکی از مکانیسم های تشخیص خرابی در یک سیستم توزیع شده است. اگر سرور مرکزی وجود داشته باشد، همه سرورها به صورت دوره ای پیام ضربان قلب را به آن ارسال می کنند. اگر سرور مرکزی وجود نداشته باشد، همه سرورها به طور تصادفی مجموعه ای از سرورها را انتخاب می کنند و هر چند ثانیه یک پیام ضربان قلب برای آنها ارسال می کنند. به این ترتیب، اگر برای مدتی پیام ضربان قلب از یک سرور دریافت نشود، سیستم می تواند شک کند که سرور ممکن است از کار افتاده باشد. اگر در یک بازه زمانی تنظیمشده ضربان قلب وجود نداشته باشد، سیستم میتواند به این نتیجه برسد که سرور دیگر زنده نیست و ارسال درخواست به آن را متوقف کند و شروع به کار بر روی جایگزینی آن کند.
مثال:
سیستمهای Google File System (GFS) و HDFS از Heartbeating برای برقراری ارتباط با سرورهای دیگر در سیستم برای ارائه دستورالعملها و جمعآوری وضعیت استفاده میکنند.
2. تقسیم مغز
زمینه:
در یک محیط توزیع شده با یک سرور مرکزی (یا رهبر)، اگر سرور مرکزی از بین برود، سیستم باید به سرعت یک جایگزین پیدا کند. در غیر این صورت، سیستم می تواند به سرعت خراب شود.
یکی از مشکلات این است که ما واقعاً نمیتوانیم بفهمیم که آیا رهبر برای همیشه متوقف شده است یا یک شکست متناوب مانند توقف GC یا یک اختلال موقت شبکه را تجربه کرده است. با این وجود، این cluster باید پیش برود و یک رهبر جدید انتخاب کند. اگر رهبر اصلی یک شکست متناوب(intermittent failure) داشت، اکنون خود را با یک رهبر به اصطلاح زامبی می یابیم. رهبر زامبی را می توان به عنوان یک گره رهبر تعریف کرد که توسط سیستم مرده تلقی شده و از آن زمان دوباره آنلاین شده است. گره دیگری جای آن را گرفته است، اما رهبر زامبی ها ممکن است هنوز این را ندانند. این سیستم اکنون دارای دو رهبر فعال است که می توانند دستورات متناقضی را صادر کنند. چگونه یک سیستم می تواند چنین سناریویی را شناسایی کند تا همه گره های سیستم بتوانند درخواست های رهبر قدیمی را نادیده بگیرند و خود رهبر قدیمی تشخیص دهد که دیگر رهبر نیست؟
تعریف:
سناریوی رایجی که در آن یک سیستم توزیع شده دارای دو یا چند رهبر فعال است، تقسیم مغز نامیده می شود.
تقسیم مغز با استفاده از Generation Clock حل می شود، که به سادگی یک monotonically increasing number برای نشان دادن نسل سرور است.
راه حل:
هر بار که یک رهبر جدید انتخاب می شود، generation number افزایش می یابد. این بدان معناست که اگر رهبر قدیمی شماره نسل "1" داشته باشد، نسل جدید "2" خواهد داشت. این شماره نسل در هر درخواستی که از رهبر به گره های دیگر ارسال می شود گنجانده می شود. به این ترتیب، گره ها اکنون می توانند به راحتی رهبر واقعی را با اعتماد به رهبر با بیشترین تعداد متمایز کنند. شماره تولید باید روی دیسک ثابت بماند تا پس از راهاندازی مجدد سرور در دسترس باقی بماند. یک راه این است که آن را با هر ورودی در گزارش Write-Ahead ذخیره کنید.
مثال ها:
کافکا: برای مدیریت Split-Brain (جایی که میتوانیم چندین کارگزار کنترلکننده فعال داشته باشیم)، کافکا از «Epoch number» استفاده میکند که صرفاً یک عدد یکنواخت در حال افزایش برای نشان دادن نسل یک سرور است.
در HDFS: برای اطمینان از فعال بودن, ZooKeeper تنها یک NameNode در هر زمان استفاده می شود. یک epoch number به عنوان بخشی از هر transaction ID نگهداری می شود تا تولید NameNode را منعکس کند.
3. درختان مرکل
زمینه:
سیستم های توزیع شده چندین نسخه از داده ها را روی سرورهای مختلف (به نام replicas) برای تحمل خطا و در دسترس بودن بالاتر نگهداری می کنند. برای همگام نگه داشتن داده ها در بین همه سرورهای مشابه، سیستم به مکانیزمی کارآمد برای مقایسه داده ها بین دو نسخه نیاز دارد. در یک محیط توزیعشده، چگونه میتوانیم به سرعت دو نسخه از طیف وسیعی از دادههای موجود در دو کپی مختلف را با هم مقایسه کنیم و دقیقا بفهمیم که کدام قسمتها متفاوت هستند؟
تعریف:
یک replica می تواند حاوی داده های زیادی باشد. تقسیم ساده لوحانه کل محدوده برای محاسبه checksums برای مقایسه چندان امکان پذیر نیست. به سادگی داده های زیادی برای انتقال وجود دارد. در عوض، میتوانیم از درختان مرکل برای مقایسه کپیهای یک محدوده استفاده کنیم.
راه حل:
درخت مرکل یک درخت باینری از هش ها است که هر گره داخلی هش دو فرزندش است و هر گره برگ هش بخشی از داده های اصلی است.
مقایسه درختان مرکل از نظر مفهومی ساده است:
- هش ریشه هر دو درخت را مقایسه کنید.
- اگر مساوی هستند، متوقف شوید.
- تکرار در فرزندان چپ و راست.
در نهایت، این بدان معنی است که کپی ها دقیقاً می دانند که کدام بخش از محدوده متفاوت است، اما میزان داده های مبادله شده به حداقل می رسد. مزیت اصلی درخت مرکل این است که هر شاخه از درخت را می توان به طور مستقل بدون نیاز به گره ها برای دانلود کل درخت یا کل مجموعه داده بررسی کرد. از این رو، درختان Merkle مقدار دادههایی را که برای همگامسازی باید منتقل شوند به حداقل میرسانند و تعداد خواندن دیسک را کاهش میدهند.
عیب استفاده از درختان مرکل این است که بسیاری از محدوده های کلیدی می توانند با پیوستن یا خروج یک گره تغییر کنند، در این مرحله درختان باید دوباره محاسبه شوند.
مثال ها
برای ضد آنتروپی و حل تعارضات در پس زمینه، Dynamo آمازون از درختان مرکل استفاده می کند.
نتیجه:
➡ این مفاهیم طراحی سیستم را تمرین کنید تا خود را از دیگران متمایز کنید!
➡ درباره این رویکردها در "مصاحبه طراحی سیستم" و "مصاحبه طراحی سیستم پیشرفته" بیشتر بیاموزید.
➡ برای راهنمایی در مورد طراحی سیستم و مصاحبه کدنویسی، من را در Linkedin دنبال کنید.
مطلبی دیگر از این انتشارات
معماری Clean Architecture در .NET Core : یک بررسی اجمالی
مطلبی دیگر از این انتشارات
چرا می خواهید پایگاه داده خواندن/نوشتن جداگانه داشته باشید؟
مطلبی دیگر از این انتشارات
پیاده سازی response caching در dotNET Core