نگاهی به روش های مختلف Cache invalidation
Cache یکی از رایجترین راهکارا برای افزایش Performance توی سیستمهاست. به جای اینکه برای هر Request مستقیم به Database مراجعه کنیم، دادههایی که زیاد استفاده میشن رو توی یک لایهی سریعتر نگهداری میکنیم. این کار باعث میشه تعداد Queryها کمتر بشه، Latency کاهش پیدا کنه و فشار روی Database پایین بیاد. اما یک مشکل مهم وجود داره:
اگه دادهی اصلی تغییر کنه، چه اتفاقی برای نسخهای که داخل Cache داریم میفته؟
فرض کنیم اطلاعات یه کاربر داخل Cache ذخیره شده. حالا کاربر اسم خودش رو تغییر میده و این تغییر داخل Database ثبت میشه. اما اگه مقدار قدیمی هنوز داخل Cache وجود داشته باشه، درخواستهای بعدی ممکنه همون دادهی قدیمی رو دریافت کنن.
اینجاست که مفهوم Cache Invalidation اهمیت پیدا میکنه.
Cache Invalidation دقیقاً یعنی چی؟ 🧠
Cache Invalidation یعنی مدیریت منقضی شدن یا حذف دادههایی که دیگه معتبر نیستند. به زبان ساده، وقتی دادهی اصلی تغییر میکنه، باید یه تصمیمی بگیریم:
باید Cache رو حذف کنیم؟ یا مقدار جدید رو داخل Cache قرار بدیم؟ یا اجازه بدیم بعد از مدتی خودش منقضی بشه؟
این تصمیم یکی از چالشهای مهم طراحی سیستمهاست. چون Cache و Database معمولاً دو منبع جدا از هم هستن و همیشه باید بین سرعت بالا و Consistency تعادل ایجاد کنیم.
روش اول: Cache Aside (Lazy Loading) 📦
یکی از رایجترین روشها، Cache Aside هست. تو این مدل، Application خودش مسئول مدیریت Cache و Database هست. وقتی یه داده درخواست میشه، اول Cache بررسی میشه. اگه داده وجود داشته باشه، همون مقدار برگردونده میشه. اگه Cache Miss اتفاق بیفته، Application داده رو از Database میخونه، داخل Cache قرار میده و بعد جواب رو برمیگردونه.
اما بخش مهم این روش، زمان نوشتن هست. وقتی داده تغییر میکنه، معمولاً Application اول تغییر رو توی Database ذخیره میکنه و بعد مقدار مربوطه رو از Cache حذف میکنه. با این کار، درخواست بعدی مجبور میشه دادهی جدید رو از Database بخونه و دوباره Cache رو پر کنه. مزیت این روش سادگی و کنترل خوب روی Cache هست. به همین دلیل یکی از رایجترین الگوها توی سیستمهای واقعی محسوب میشه.
روش دوم: TTL (Time To Live) ⏳
یکی از سادهترین روشها برای مدیریت Cache، تعیین یک زمان انقضا برای دادههاست. هر داده ای فقط تا زمان مشخصی معتبره. بعد از تموم شدن این زمان، داده حذف میشه و درخواست بعدی مقدار جدید رو دریافت میکنه.
مزیت TTL اینه که نیاز نداریم تمام تغییرات Database رو دنبال کنیم. اما مشکلش اینه که ممکنه داده قبل از منقضی شدن، تغییر کرده باشه. یعنی تا زمانی که TTL تمام نشده، کاربرا ممکنه مقدار قدیمی رو ببینن. به همین دلیل TTL بیشتر برای دادههایی مناسبه که تغییراتشون حساس نیست یا Stale بودن کوتاهمدت براشون قابل قبوله.
روش سوم: Write Through 🔄
توی Write Through، موقع تغییر داده، Application همزمان Cache و Database رو بهروز میکنه.
یعنی هر Write باید در هر دو مکان انجام بشه.
مزیت این روش اینه که Cache همیشه مقدار جدید رو داره و احتمال Stale Data کمتر میشه. اما هزینهی بیشتری داره، چون هر تغییر باید چند عملیات مختلف انجام بده. همچنین اگه یکی از این عملیاتها شکست بخوره، باید مکانیزمی برای هماهنگ کردن دوبارهی دادهها وجود داشته باشه.
روش چهارم: Event-Based Invalidation 📡
تو سیستمهای بزرگتر، گاهی سرویسها از Event استفاده میکنن. یعنی وقتی تغییری داخل Database اتفاق میوفته، یه Event منتشر میشه. سرویسهای دیگه این Event رو دریافت میکنن و Cache مربوط به اون داده رو حذف یا بهروز میکنن.
این روش باعث میشه سرویسها وابستگی مستقیم به هم نداشته باشن. اما در عوض، معماری پیچیدهتر میشه و باید مواردی مثل Duplicate Event، ترتیب Eventها و خطاهای احتمالی مدیریت بشن.
پس بهترین روش کدومه؟💡
مثل بیشتر تصمیمهای معماری، یک جواب ثابت وجود نداره.
اگه دادهها زیاد تغییر نمیکنن و کمی قدیمی بودنشون مشکلی ایجاد نمیکنه، TTL میتونه کافی باشه.
اگه Performance و سادگی مهم باشه، Cache Aside معمولاً انتخاب مناسبیه.
اگه Consistency اهمیت زیادی داشته باشه، باید سراغ روشهایی مثل Write Through یا Event-Based Invalidation رفت.
انتخاب روش مناسب به این بستگی داره که سیستم ما بیشتر به چه چیزی نیاز داره:
Performance بیشتر یا دادهی همیشه بهروز؟
جمعبندی ✍️
Cache کردن دادهها بخش سادهی ماجراست. چالش واقعی زمانی شروع میشه که داده تغییر کنه و باید تصمیم بگیریم Cache چه زمانی و چطور بهروز بشه.
➖➖➖➖➖➖➖➖➖➖
Cache یکی از رایجترین راهکارا برای افزایش Performance توی سیستمهاست. به جای اینکه برای هر Request مستقیم به Database مراجعه کنیم، دادههایی که زیاد استفاده میشن رو توی یک لایهی سریعتر نگهداری میکنیم. این کار باعث میشه تعداد Queryها کمتر بشه، Latency کاهش پیدا کنه و فشار روی Database پایین بیاد. اما یک مشکل مهم وجود داره:
اگه دادهی اصلی تغییر کنه، چه اتفاقی برای نسخهای که داخل Cache داریم میفته؟
فرض کنیم اطلاعات یه کاربر داخل Cache ذخیره شده. حالا کاربر اسم خودش رو تغییر میده و این تغییر داخل Database ثبت میشه. اما اگه مقدار قدیمی هنوز داخل Cache وجود داشته باشه، درخواستهای بعدی ممکنه همون دادهی قدیمی رو دریافت کنن.
اینجاست که مفهوم Cache Invalidation اهمیت پیدا میکنه.
Cache Invalidation دقیقاً یعنی چی؟ 🧠
Cache Invalidation یعنی مدیریت منقضی شدن یا حذف دادههایی که دیگه معتبر نیستند. به زبان ساده، وقتی دادهی اصلی تغییر میکنه، باید یه تصمیمی بگیریم:
باید Cache رو حذف کنیم؟ یا مقدار جدید رو داخل Cache قرار بدیم؟ یا اجازه بدیم بعد از مدتی خودش منقضی بشه؟
این تصمیم یکی از چالشهای مهم طراحی سیستمهاست. چون Cache و Database معمولاً دو منبع جدا از هم هستن و همیشه باید بین سرعت بالا و Consistency تعادل ایجاد کنیم.
روش اول: Cache Aside (Lazy Loading) 📦
یکی از رایجترین روشها، Cache Aside هست. تو این مدل، Application خودش مسئول مدیریت Cache و Database هست. وقتی یه داده درخواست میشه، اول Cache بررسی میشه. اگه داده وجود داشته باشه، همون مقدار برگردونده میشه. اگه Cache Miss اتفاق بیفته، Application داده رو از Database میخونه، داخل Cache قرار میده و بعد جواب رو برمیگردونه.
اما بخش مهم این روش، زمان نوشتن هست. وقتی داده تغییر میکنه، معمولاً Application اول تغییر رو توی Database ذخیره میکنه و بعد مقدار مربوطه رو از Cache حذف میکنه. با این کار، درخواست بعدی مجبور میشه دادهی جدید رو از Database بخونه و دوباره Cache رو پر کنه. مزیت این روش سادگی و کنترل خوب روی Cache هست. به همین دلیل یکی از رایجترین الگوها توی سیستمهای واقعی محسوب میشه.
روش دوم: TTL (Time To Live) ⏳
یکی از سادهترین روشها برای مدیریت Cache، تعیین یک زمان انقضا برای دادههاست. هر داده ای فقط تا زمان مشخصی معتبره. بعد از تموم شدن این زمان، داده حذف میشه و درخواست بعدی مقدار جدید رو دریافت میکنه.
مزیت TTL اینه که نیاز نداریم تمام تغییرات Database رو دنبال کنیم. اما مشکلش اینه که ممکنه داده قبل از منقضی شدن، تغییر کرده باشه. یعنی تا زمانی که TTL تمام نشده، کاربرا ممکنه مقدار قدیمی رو ببینن. به همین دلیل TTL بیشتر برای دادههایی مناسبه که تغییراتشون حساس نیست یا Stale بودن کوتاهمدت براشون قابل قبوله.
روش سوم: Write Through 🔄
توی Write Through، موقع تغییر داده، Application همزمان Cache و Database رو بهروز میکنه.
یعنی هر Write باید در هر دو مکان انجام بشه.
مزیت این روش اینه که Cache همیشه مقدار جدید رو داره و احتمال Stale Data کمتر میشه. اما هزینهی بیشتری داره، چون هر تغییر باید چند عملیات مختلف انجام بده. همچنین اگه یکی از این عملیاتها شکست بخوره، باید مکانیزمی برای هماهنگ کردن دوبارهی دادهها وجود داشته باشه.
روش چهارم: Event-Based Invalidation 📡
تو سیستمهای بزرگتر، گاهی سرویسها از Event استفاده میکنن. یعنی وقتی تغییری داخل Database اتفاق میوفته، یه Event منتشر میشه. سرویسهای دیگه این Event رو دریافت میکنن و Cache مربوط به اون داده رو حذف یا بهروز میکنن.
این روش باعث میشه سرویسها وابستگی مستقیم به هم نداشته باشن. اما در عوض، معماری پیچیدهتر میشه و باید مواردی مثل Duplicate Event، ترتیب Eventها و خطاهای احتمالی مدیریت بشن.
پس بهترین روش کدومه؟💡
مثل بیشتر تصمیمهای معماری، یک جواب ثابت وجود نداره.
اگه دادهها زیاد تغییر نمیکنن و کمی قدیمی بودنشون مشکلی ایجاد نمیکنه، TTL میتونه کافی باشه.
اگه Performance و سادگی مهم باشه، Cache Aside معمولاً انتخاب مناسبیه.
اگه Consistency اهمیت زیادی داشته باشه، باید سراغ روشهایی مثل Write Through یا Event-Based Invalidation رفت.
انتخاب روش مناسب به این بستگی داره که سیستم ما بیشتر به چه چیزی نیاز داره:
Performance بیشتر یا دادهی همیشه بهروز؟
جمعبندی ✍️
Cache کردن دادهها بخش سادهی ماجراست. چالش واقعی زمانی شروع میشه که داده تغییر کنه و باید تصمیم بگیریم Cache چه زمانی و چطور بهروز بشه.
#️⃣ #system_design #cache #backend
➖➖➖➖➖➖➖➖➖➖
🌙 CHANNEL | GROUP
Switch که توی لایه ی دوم شبکه کار میکنه و فقط MAC Addressها رو میشناسه، چطوری میتونه با IP Address کار کنه؟🤔
در واقع Switch خودش کاری با IP Address نداره .وظیفهی Switch اینه که Frameها رو بر اساس MAC Address بین دستگاههای داخل شبکه جابهجا کنه. اما مشکل اینجاست که اکثر ارتباطات شبکهای با IP شروع میشن.
وقتی یک Application میخواد دادهای رو به یک IP مشخص ارسال کنه، سیستم عامل اول باید اون IP رو به یک MAC Address تبدیل کنه تا بتونه Frame مربوطه رو داخل شبکه ارسال کنه.
اینجاست که ARP (Address Resolution Protocol) وارد میشه.
ARP دقیقاً یعنی چی؟ 🧠
ARP یک پروتکل برای پیدا کردن MAC Address مربوط به یک IP Address داخل شبکهی Local هست. به زبان ساده، ARP این سؤال رو از شبکه میپرسه:
«کدوم دستگاه این IP رو داره؟ MAC Address خودش رو بهم بگه.»
برای مثال، فرض کنین یک سیستم با IP
ARP Request و ARP Reply چطور کار میکنن؟ 📡
اول سیستم فرستنده یه پیام ARP Request ارسال میکنه. این پیام به صورت Broadcast توی شبکه Local پخش میشه. یعنی تمام دستگاههای داخل همون شبکه این پیام رو دریافت میکنن. محتوای پیام یه همچین چیزیه:
«چه کسی IP 192.168.1.20 رو داره؟ MAC Address خودش رو بهم بگه.»
تمام دستگاهها این پیام رو دریافت میکنن، اما فقط دستگاهی که اون IP رو داره جواب میده. این جواب با نام ARP Reply شناخته میشه. دستگاه مقصد MAC Address خودش رو ارسال میکنه و سیستم فرستنده حالا میتونه Frame رو مستقیم به اون MAC Address ارسال کنه.
ARP Cache چرا وجود داره؟ 💾
اگر برای هر Packet نیاز بود دوباره ARP Request ارسال بشه، شبکه خیلی سریع شلوغ میشد. به همین دلیل سیستمها نتیجهی ARP Queryها رو برای مدتی ذخیره میکنن. این اطلاعات داخل چیزی به اسم ARP Cache نگهداری میشه.
مثلاً سیستم میتونه نگه داره:
دفعهی بعد که بخواد دادهای برای این IP ارسال کنه، مستقیم از همین اطلاعات استفاده میکنه و نیازی به Broadcast دوباره نیست. البته این اطلاعات دائمی نیستن و بعد از مدتی منقضی میشن تا تغییرات شبکه هم قابل تشخیص باشن.
ARP فقط برای ارتباط داخل شبکه است 🌐
یک نکتهی مهم اینه که ARP برای پیدا کردن MAC Address داخل همون شبکهی Local استفاده میشه. وقتی میخوایم به یک Server توی اینترنت وصل بشیم، سیستم ما MAC Address اون Server رو پیدا نمیکنه. چون مقصد توی یه شبکه ی دیگه هست.
توی این حالت، سیستم MAC Address مربوط به Router یا Gateway خودش رو با ARP پیدا میکنه و Packet رو به Router تحویل میده. بعد از اون، Router مسئول انتقال Packet در مسیر شبکه است.
ولی ARP یه مشکلی داره ⚠️
ARP یک پروتکل ساده است، یکی از مشکلاتش اینه که مکانیزم Authentication نداره. یعنی یک دستگاه میتونه خودش رو به عنوان صاحب یک IP معرفی کنه و پیام ARP جعلی ارسال کنه. به این حمله ARP Spoofing گفته میشه. توی این حالت، مهاجم میتونه ترافیک شبکه رو به سمت خودش هدایت کنه و اطلاعاتی مثل Packetها رو مشاهده یا تغییر بده. به همین دلیل تو شبکههای حساس، مکانیزمهای امنیتی بیشتری برای کنترل ARP استفاده میشن.
جمعبندی ✍️
ARP پلی بین دو دنیای مختلفه:
IP Address که برای پیدا کردن مقصد در لایهی Network استفاده میشه، و MAC Address که برای ارسال واقعی Frame داخل شبکهی Local استفاده میشه.
هر بار که یک Packet داخل یک شبکهی Ethernet حرکت میکنه، پشت صحنه ARP کمک میکنه سیستم بدونه باید این داده رو دقیقاً به کدوم دستگاه تحویل بده.
➖➖➖➖➖➖➖➖➖➖
در واقع Switch خودش کاری با IP Address نداره .وظیفهی Switch اینه که Frameها رو بر اساس MAC Address بین دستگاههای داخل شبکه جابهجا کنه. اما مشکل اینجاست که اکثر ارتباطات شبکهای با IP شروع میشن.
وقتی یک Application میخواد دادهای رو به یک IP مشخص ارسال کنه، سیستم عامل اول باید اون IP رو به یک MAC Address تبدیل کنه تا بتونه Frame مربوطه رو داخل شبکه ارسال کنه.
اینجاست که ARP (Address Resolution Protocol) وارد میشه.
ARP دقیقاً یعنی چی؟ 🧠
ARP یک پروتکل برای پیدا کردن MAC Address مربوط به یک IP Address داخل شبکهی Local هست. به زبان ساده، ARP این سؤال رو از شبکه میپرسه:
«کدوم دستگاه این IP رو داره؟ MAC Address خودش رو بهم بگه.»
برای مثال، فرض کنین یک سیستم با IP
192.168.1.10 میخواد برای یه سیستم دیگه با IP 192.168.1.20 داده ارسال کنه. سیستم اول میدونه مقصد کجاست، اما MAC Address مقصد رو نمیدونه. پس باید قبل از ارسال Frame، این اطلاعات رو پیدا کنه.ARP Request و ARP Reply چطور کار میکنن؟ 📡
اول سیستم فرستنده یه پیام ARP Request ارسال میکنه. این پیام به صورت Broadcast توی شبکه Local پخش میشه. یعنی تمام دستگاههای داخل همون شبکه این پیام رو دریافت میکنن. محتوای پیام یه همچین چیزیه:
«چه کسی IP 192.168.1.20 رو داره؟ MAC Address خودش رو بهم بگه.»
تمام دستگاهها این پیام رو دریافت میکنن، اما فقط دستگاهی که اون IP رو داره جواب میده. این جواب با نام ARP Reply شناخته میشه. دستگاه مقصد MAC Address خودش رو ارسال میکنه و سیستم فرستنده حالا میتونه Frame رو مستقیم به اون MAC Address ارسال کنه.
ARP Cache چرا وجود داره؟ 💾
اگر برای هر Packet نیاز بود دوباره ARP Request ارسال بشه، شبکه خیلی سریع شلوغ میشد. به همین دلیل سیستمها نتیجهی ARP Queryها رو برای مدتی ذخیره میکنن. این اطلاعات داخل چیزی به اسم ARP Cache نگهداری میشه.
مثلاً سیستم میتونه نگه داره:
192.168.1.20 → AA:BB:CC:DD:EE:FF
دفعهی بعد که بخواد دادهای برای این IP ارسال کنه، مستقیم از همین اطلاعات استفاده میکنه و نیازی به Broadcast دوباره نیست. البته این اطلاعات دائمی نیستن و بعد از مدتی منقضی میشن تا تغییرات شبکه هم قابل تشخیص باشن.
ARP فقط برای ارتباط داخل شبکه است 🌐
یک نکتهی مهم اینه که ARP برای پیدا کردن MAC Address داخل همون شبکهی Local استفاده میشه. وقتی میخوایم به یک Server توی اینترنت وصل بشیم، سیستم ما MAC Address اون Server رو پیدا نمیکنه. چون مقصد توی یه شبکه ی دیگه هست.
توی این حالت، سیستم MAC Address مربوط به Router یا Gateway خودش رو با ARP پیدا میکنه و Packet رو به Router تحویل میده. بعد از اون، Router مسئول انتقال Packet در مسیر شبکه است.
ولی ARP یه مشکلی داره ⚠️
ARP یک پروتکل ساده است، یکی از مشکلاتش اینه که مکانیزم Authentication نداره. یعنی یک دستگاه میتونه خودش رو به عنوان صاحب یک IP معرفی کنه و پیام ARP جعلی ارسال کنه. به این حمله ARP Spoofing گفته میشه. توی این حالت، مهاجم میتونه ترافیک شبکه رو به سمت خودش هدایت کنه و اطلاعاتی مثل Packetها رو مشاهده یا تغییر بده. به همین دلیل تو شبکههای حساس، مکانیزمهای امنیتی بیشتری برای کنترل ARP استفاده میشن.
جمعبندی ✍️
ARP پلی بین دو دنیای مختلفه:
IP Address که برای پیدا کردن مقصد در لایهی Network استفاده میشه، و MAC Address که برای ارسال واقعی Frame داخل شبکهی Local استفاده میشه.
هر بار که یک Packet داخل یک شبکهی Ethernet حرکت میکنه، پشت صحنه ARP کمک میکنه سیستم بدونه باید این داده رو دقیقاً به کدوم دستگاه تحویل بده.
#️⃣ #network #web #devops
➖➖➖➖➖➖➖➖➖➖
🌙 CHANNEL | GROUP
DNS Caching چطور کار میکنه؟ 🤔
وقتی توی مرورگر
DNS Cache دقیقاً چیه؟ 🧠
DNS Cache یعنی ذخیره کردن نتیجهی DNS Query برای یه مدت مشخص. مثلاً فرض میکنیم Resolver برای
DNS Cache کجا نگه داشته میشه؟ 📦
یک DNS Cache فقط در یک نقطه وجود نداره. ممکنه نتیجهی DNS Query توی چندین لایه Cache بشه. مثلاً مرورگر میتونه نتیجهی DNS رو نگه داره، سیستمعامل هم میتونه Cache خودش رو داشته باشه و DNS Resolver مورد استفادهی سیستم هم معمولاً Cache داره. حتی Resolverهای عمومی مثل DNSهای ISP یا سرویسهای عمومی هم میتونن نتیجهی Domainها رو برای مدتی Cache کنن. به همین دلیل وقتی یک Domain رو چندین بار Request میکنیم، ممکنه تمام مسیر DNS طی نشه.
TTL چه کاری انجام میده؟ ⏳
TTL مشخص میکنه یک DNS Record تا چه مدت میتونه Cache بشه. فرض کنیم TTL یک Record برابر با 300 ثانیه باشه. Resolver میتونه نتیجه رو تا 5 دقیقه Cache کنه. بعد از تموم شدن TTL، اون مقدار دیگه معتبر محسوب نمیشه و Resolver باید برای دریافت اطلاعات جدید دوباره Query انجام بده.
این مقدار خیلی مهمه، چون TTL پایین باعث میشه تغییرات DNS سریعتر منتشر بشن، اما تعداد Queryها بیشتر میشه. TTL بالاتر هم باعث کاهش Queryها و افزایش استفاده از Cache میشه، اما تغییرات DNS ممکنه دیرتر دیده بشن.
DNS Cache چه مشکلی رو حل میکنه؟ ⚡️
بدون DNS Caching، حتی یک درخواست ساده به یک Website میتونست نیازمند چندین DNS Query باشه. Resolver ممکن بود مجبور بشه از Root Server شروع کنه، بعد به TLD Server برسه و در نهایت Authoritative DNS Server رو پیدا کنه. اما وقتی نتیجه قبلاً Cache شده باشه، Resolver میتونه خیلی سریع جواب رو برگردونه.
این یعنی Latency کمتر، DNS Traffic کمتر و فشار کمتر روی DNS Infrastructure.
DNS Cache Poisoning ☠️
Cache کردن DNS فقط یک بحث Performance نیست و جنبهی امنیتی هم داره. اگه یک مهاجم بتونه یک DNS Resolver رو فریب بده و یک پاسخ جعلی داخل Cache اون قرار بده، درخواستهای بعدی ممکنه به IP اشتباه هدایت بشن.
به این حمله DNS Cache Poisoning گفته میشه. به همین دلیل مکانیزمهایی مثل تصادفیسازی Transaction ID و Source Port و همچنین پروتکلهایی مثل DNSSEC برای افزایش امنیت DNS استفاده میشن.
جمعبندی ✍️
DNS Caching باعث میشه نتیجهی DNS Query برای یه مدتی ذخیره بشه تا برای هر درخواست مجبور نباشیم دوباره مسیر Resolution رو طی کنیم.
TTL هم مشخص میکنه این نتیجه تا چه مدت معتبره و همین مقدار، بین سرعت انتشار تغییرات DNS و کاهش DNS Queryها یک Trade-off ایجاد میکنه.
➖➖➖➖➖➖➖➖➖➖
وقتی توی مرورگر
example.com رو وارد میکنیم، سیستم برای برقراری ارتباط باید IP مربوط به اون Domain رو پیدا کنه. اما اینطور نیست که برای هر درخواست، سیستم دوباره از Root DNS Server شروع کنه و تمام مسیر DNS رو طی کنه. اگه این اتفاق برای هر Request میافتاد، هم زمان زیادی صرف DNS Resolution میشد و هم فشار خیلی زیادی به DNS Serverها وارد میشد.DNS Cache دقیقاً چیه؟ 🧠
DNS Cache یعنی ذخیره کردن نتیجهی DNS Query برای یه مدت مشخص. مثلاً فرض میکنیم Resolver برای
example.com به IP 93.184.216.34 رسیده. به جای اینکه دفعهی بعد دوباره این Domain رو از اول Resolve کنه، نتیجه رو برای یه مدت نگه میداره. این مدت توسط TTL (Time To Live) مشخص میشه. تا وقتی TTL تموم نشده، Resolver میتونه از همان نتیجهی Cache شده استفاده کنه و نیازی به Query جدید نداره.DNS Cache کجا نگه داشته میشه؟ 📦
یک DNS Cache فقط در یک نقطه وجود نداره. ممکنه نتیجهی DNS Query توی چندین لایه Cache بشه. مثلاً مرورگر میتونه نتیجهی DNS رو نگه داره، سیستمعامل هم میتونه Cache خودش رو داشته باشه و DNS Resolver مورد استفادهی سیستم هم معمولاً Cache داره. حتی Resolverهای عمومی مثل DNSهای ISP یا سرویسهای عمومی هم میتونن نتیجهی Domainها رو برای مدتی Cache کنن. به همین دلیل وقتی یک Domain رو چندین بار Request میکنیم، ممکنه تمام مسیر DNS طی نشه.
TTL چه کاری انجام میده؟ ⏳
TTL مشخص میکنه یک DNS Record تا چه مدت میتونه Cache بشه. فرض کنیم TTL یک Record برابر با 300 ثانیه باشه. Resolver میتونه نتیجه رو تا 5 دقیقه Cache کنه. بعد از تموم شدن TTL، اون مقدار دیگه معتبر محسوب نمیشه و Resolver باید برای دریافت اطلاعات جدید دوباره Query انجام بده.
این مقدار خیلی مهمه، چون TTL پایین باعث میشه تغییرات DNS سریعتر منتشر بشن، اما تعداد Queryها بیشتر میشه. TTL بالاتر هم باعث کاهش Queryها و افزایش استفاده از Cache میشه، اما تغییرات DNS ممکنه دیرتر دیده بشن.
DNS Cache چه مشکلی رو حل میکنه؟ ⚡️
بدون DNS Caching، حتی یک درخواست ساده به یک Website میتونست نیازمند چندین DNS Query باشه. Resolver ممکن بود مجبور بشه از Root Server شروع کنه، بعد به TLD Server برسه و در نهایت Authoritative DNS Server رو پیدا کنه. اما وقتی نتیجه قبلاً Cache شده باشه، Resolver میتونه خیلی سریع جواب رو برگردونه.
این یعنی Latency کمتر، DNS Traffic کمتر و فشار کمتر روی DNS Infrastructure.
DNS Cache Poisoning ☠️
Cache کردن DNS فقط یک بحث Performance نیست و جنبهی امنیتی هم داره. اگه یک مهاجم بتونه یک DNS Resolver رو فریب بده و یک پاسخ جعلی داخل Cache اون قرار بده، درخواستهای بعدی ممکنه به IP اشتباه هدایت بشن.
به این حمله DNS Cache Poisoning گفته میشه. به همین دلیل مکانیزمهایی مثل تصادفیسازی Transaction ID و Source Port و همچنین پروتکلهایی مثل DNSSEC برای افزایش امنیت DNS استفاده میشن.
جمعبندی ✍️
DNS Caching باعث میشه نتیجهی DNS Query برای یه مدتی ذخیره بشه تا برای هر درخواست مجبور نباشیم دوباره مسیر Resolution رو طی کنیم.
TTL هم مشخص میکنه این نتیجه تا چه مدت معتبره و همین مقدار، بین سرعت انتشار تغییرات DNS و کاهش DNS Queryها یک Trade-off ایجاد میکنه.
#️⃣ #network #web #devops
➖➖➖➖➖➖➖➖➖➖
🌙 CHANNEL | GROUP
🔥2
Forwarded from DevTwitter | توییت برنامه نویسی
تولدت مبارک پیرمرد قابل اعتماد دنیای لینوکس!
از مرداد ۱۳۷۲ تا امروز Stable مثل همیشه
@DevTwitter | <MehrdadLinux/>
از مرداد ۱۳۷۲ تا امروز Stable مثل همیشه
@DevTwitter | <MehrdadLinux/>
Socket Lifecycle چطور کار میکنه؟ 🔌
وقتی یه Application میخواد با یه سیستم دیگه از طریق Network ارتباط برقرار کنه، در نهایت به چیزی به اسم Socket نیاز داره. Socket رو میتونیم به عنوان نقطهی ارتباطی بین Application و Network در نظر بگیریم. اما Socket از همون لحظهای که ساخته میشه تا زمانی که Connection بسته بشه، چند مرحلهی مشخص رو طی میکنه. این مراحل رو میتونیم به عنوان Socket Lifecycle بشناسیم.
ساختن Socket 🧠
اولین مرحله، ساختن خود Socket هست. Application به سیستمعامل اعلام میکنه که یه Socket میخواد و مشخص میکنه از چه Address Family و چه نوع پروتکلی قراره استفاده بشه، مثلاً
Bind کردن Socket 📍
اگه Application قراره روی یه Address مشخص به Connectionها گوش بده، باید Socket رو با استفاده از
Listen کردن 👂
بعد از
Accept کردن Connection 🤝
وقتی یه Client به Server وصل میشه، Server با
ارتباط با Server از سمت Client 📡
در سمت Client، معمولاً بعد از ساختن Socket، اپلیکیشن از
ارسال و دریافت داده 📦
بعد از برقرار شدن Connection، اپلیکیشن میتونه با استفاده از
بستن Socket 🔒
وقتی ارتباط دیگه مورد نیاز نیست، Application میتونه با
➖➖➖➖➖➖➖➖➖➖
وقتی یه Application میخواد با یه سیستم دیگه از طریق Network ارتباط برقرار کنه، در نهایت به چیزی به اسم Socket نیاز داره. Socket رو میتونیم به عنوان نقطهی ارتباطی بین Application و Network در نظر بگیریم. اما Socket از همون لحظهای که ساخته میشه تا زمانی که Connection بسته بشه، چند مرحلهی مشخص رو طی میکنه. این مراحل رو میتونیم به عنوان Socket Lifecycle بشناسیم.
ساختن Socket 🧠
اولین مرحله، ساختن خود Socket هست. Application به سیستمعامل اعلام میکنه که یه Socket میخواد و مشخص میکنه از چه Address Family و چه نوع پروتکلی قراره استفاده بشه، مثلاً
AF_INET برای IPv4 و SOCK_STREAM برای ارتباط TCP. سیستمعامل هم یه File Descriptor برمیگردونه که Application از طریق اون بعداً با Socket کار میکنه. از اینجا به بعد، Socket در اختیار Application هست ولی هنوز به یه Address یا Connection مشخصی وصل نشده.Bind کردن Socket 📍
اگه Application قراره روی یه Address مشخص به Connectionها گوش بده، باید Socket رو با استفاده از
bind به یه IP و Port مشخص متصل کنه. مثلاً یه Web Server میتونه Socket خودش رو روی 0.0.0.0:8000 بایند کنه تا روی پورت 8000 منتظر Connectionهای ورودی باشه. این مرحله بیشتر برای Serverها اهمیت داره، چون Client معمولاً لازم نیست خودش Port مشخصی رو انتخاب کنه و سیستمعامل میتونه یه Ephemeral Port براش اختصاص بده.Listen کردن 👂
بعد از
bind، یه TCP Server باید Socket رو وارد حالت Listening کنه. با listen به سیستمعامل میگه که این Socket قراره Connectionهای ورودی رو دریافت کنه. از اینجا به بعد، Connectionهای جدید میتونن به Server ارسال بشن و سیستمعامل اونها رو در ساختارهای مربوط به Listening Socket مدیریت میکنه. مقدار Backlog هم مشخص میکنه چه تعداد Connection میتونن در صف مربوط به پذیرش قرار بگیرن.Accept کردن Connection 🤝
وقتی یه Client به Server وصل میشه، Server با
accept یهی از Connectionهای آماده رو دریافت میکنه. نکتهی مهم اینه که accept یه Socket جدید برمیگردونه و Socket اصلی که روی اون listen انجام شده همچنان برای دریافت Connectionهای جدید باقی میمونه. بنابراین Server یه Listening Socket داره و برای هر Connection پذیرفتهشده یه Connected Socket جداگانه ایجاد میشه. از اینجا به بعد، Application میتونه روی Socket جدید داده ارسال و دریافت کنه.ارتباط با Server از سمت Client 📡
در سمت Client، معمولاً بعد از ساختن Socket، اپلیکیشن از
connect استفاده میکنه. در TCP، این عملیات باعث شروع TCP Three-Way Handshake میشه تا Connection بین Client و Server برقرار بشه. بعد از موفق شدن Handshake، هر دو طرف یه Socket متصل دارن که میتونن از طریق اون داده ارسال و دریافت کنن. سیستمعامل هم جزئیات مربوط به TCP Connection مثل Sequence Number و State Connection رو مدیریت میکنه.ارسال و دریافت داده 📦
بعد از برقرار شدن Connection، اپلیکیشن میتونه با استفاده از
send و recv یا APIهای مشابه داده ارسال و دریافت کنه. دادهای که Application میفرسته مستقیماً به شکل یه Packet واحد به مقصد نمیرسه، بلکه TCP اون رو به جریان Byteها تبدیل میکنه و خودش مسئول مواردی مثل ترتیب، Retransmission و کنترل جریان میشه. به همین دلیل TCP برای Application بیشتر شبیه یه Byte Stream دیده میشه تا مجموعهای از Messageهای جداگانه.بستن Socket 🔒
وقتی ارتباط دیگه مورد نیاز نیست، Application میتونه با
close سوکت رو ببنده. در TCP، بسته شدن Connection هم خودش بخشی از Lifecycle هست و معمولاً با تبادل پیامهای FIN و ACK بین دو طرف انجام میشه. بعد از بسته شدن Connection، سیستمعامل منابع مربوط به Socket و File Descriptor رو آزاد میکنه. البته بسته شدن TCP Connection لزوماً به معنی این نیست که هر دو طرف دقیقاً در یه لحظه Connection رو ببندن و ممکنه هر سمت Lifecycle خودش رو طی کنه.#️⃣ #network #web #devops
➖➖➖➖➖➖➖➖➖➖
🌙 CHANNEL | GROUP
Retry & Backoff چطور جلوی فشار بیشتر روی سیستم رو میگیرن؟ 🤔
وقتی یک درخواست به یک سرویس دیگه ارسال میکنیم، همیشه قرار نیست موفق بشه. ممکنه سرویس موقتاً در دسترس نباشه، شبکه مشکل داشته باشه یا درخواست به خاطر یک خطای موقتی شکست بخوره.
اینجاست که Retry وارد میشه. به جای اینکه با اولین خطا کار رو تمومشده در نظر بگیریم، بعد از یک مدت دوباره درخواست رو ارسال میکنیم. این کار برای خطاهای موقتی میتونه خیلی مفید باشه، اما یک مشکل مهم داره: اگه تعداد زیادی کلاینت همزمان Retry کنن، خود Retry میتونه فشار بیشتری به سرویسی که همین الان مشکل داره وارد کنه.
مشکل Retry ساده چیه؟ 🔄
فرض کنین یک سرویس برای چند ثانیه از دسترس خارج شده و 1000 کلاینت همزمان بهش درخواست میفرستن. حالا اگه همهی این کلاینت ها بلافاصله بعد از شکست دوباره درخواست خودشون رو ارسال کنن، تعداد زیادی درخواست جدید دقیقاً همون زمانی وارد سیستم میشه که سرویس هنوز درگیر مشکل قبلیه.
حتی بدتر از اون، اگه Retryها با فاصلهی ثابتی مثل یک ثانیه انجام بشن، کلاینت ها ممکنه دوباره دقیقاً همزمان Retry کنن. در نتیجه به جای اینکه مشکل برطرف بشه، یک موج جدید از درخواستها ایجاد میشه و فشار روی سرویس بیشتر میشه.
Backoff چه مشکلی رو حل میکنه؟ ⏳
Backoff یعنی بعد از شکست یک درخواست، قبل از Retry کردن کمی صبر کنیم. اما معمولاً این زمان ثابت نیست و با هر Retry بیشتر میشه. برای مثال میتونیم زمان انتظار رو به شکل Exponential Backoff افزایش بدیم:
با این روش، اگه سرویس برای مدت کوتاهی مشکل داشته باشه، کلاینت ها به جای اینکه مدام بهش درخواست بفرستن، کمکم فاصلهی Retryها رو بیشتر میکنن. این کار فرصت بیشتری به سرویس میده تا Recovery کنه و دوباره آمادهی دریافت درخواست بشه.
چرا Exponential Backoff؟ 📈
اگه زمان انتظار با هر Retry بیشتر بشه، تعداد درخواستهای اضافی خیلی بهتر کنترل میشه. فرض کنین اولین Retry بعد از یک ثانیه انجام بشه، Retry بعدی دو ثانیه بعد و بعدی چهار ثانیه بعد. اگه سرویس همچنان Down باشه، کلاینت به جای ارسال مداوم درخواست، به تدریج کمتر به اون سرویس فشار میاره.
البته معمولاً برای Backoff یک Maximum Delay هم تعیین میشه تا زمان انتظار بینهایت زیاد نشه. همچنین تعداد Retryها هم باید محدود باشه، چون Retry کردن یک درخواست برای همیشه نه تنها مفید نیست، بلکه میتونه منابع Application رو هم مصرف کنه.
Jitter چرا مهمه؟ 🎲
حتی Exponential Backoff هم یک مشکل داره. فرض کنین 1000 کلاینت تقریباً در یک زمان درخواست فرستادن و همزمان شکست خوردن. اگه همه دقیقاً از یک فرمول استفاده کنن، ممکنه همهی اونها بعد از 1 ثانیه Retry کنن، بعد 2 ثانیه و بعد 4 ثانیه.
یعنی با اینکه Backoff داریم، هنوز درخواستها میتونن به صورت گروهی و همزمان ارسال بشن. Jitter با اضافه کردن مقداری تصادفی به زمان انتظار، این کلاینتها رو از هم جدا میکنه. در نتیجه به جای اینکه همه با هم Retry کنن، درخواستها توی بازههای زمانی مختلف پخش میشن و فشار روی سرویس یکنواختتر میشه.
Retry برای هر خطایی مناسب نیست ⚠️
یک نکتهی خیلی مهم اینه که هر Errorای نباید باعث Retry بشه. اگه درخواست به خاطر یک خطای موقتی مثل Timeout یا بعضی خطاهای سمت Server شکست خورده باشه، Retry میتونه منطقی باشه.
اما اگه درخواست به خاطر یک خطای دائمی مثل Invalid Input شکست خورده، ارسال دوبارهی همون درخواست احتمالاً هیچ چیزی رو درست نمیکنه.
همچنین برای عملیاتهایی که Side Effect دارن باید دقت بیشتری داشته باشیم. مثلاً اگه یک درخواست باعث ایجاد Order یا پرداخت بشه و کلاینت بعد از Timeout دوباره همون درخواست رو ارسال کنه، ممکنه عملیات دوبار انجام بشه. اینجاست که مفاهیمی مثل Idempotency اهمیت پیدا میکنن.
Retry میتونه مشکل رو بدتر کنه 🔥
Retry در ظاهر یک مکانیزم ساده برای افزایش Reliabilityه، اما اگه درست طراحی نشه میتونه یک Failure رو به یک Failure بزرگتر تبدیل کنه.
وقتی یک سرویس تحت فشار قرار میگیره و کلاینت ها به جای کاهش درخواست، مرتب Retry میکنن، Load بیشتری ایجاد میشه و Recovery سختتر میشه.
به همین دلیل Retry معمولاً در کنار چیزهایی مثل Exponential Backoff، Jitter، Retry Limit و Timeout استفاده میشه تا سیستم هم شانس بیشتری برای Recovery داشته باشه و هم خودش باعث تشدید مشکل نشه.
➖➖➖➖➖➖➖➖➖➖
وقتی یک درخواست به یک سرویس دیگه ارسال میکنیم، همیشه قرار نیست موفق بشه. ممکنه سرویس موقتاً در دسترس نباشه، شبکه مشکل داشته باشه یا درخواست به خاطر یک خطای موقتی شکست بخوره.
اینجاست که Retry وارد میشه. به جای اینکه با اولین خطا کار رو تمومشده در نظر بگیریم، بعد از یک مدت دوباره درخواست رو ارسال میکنیم. این کار برای خطاهای موقتی میتونه خیلی مفید باشه، اما یک مشکل مهم داره: اگه تعداد زیادی کلاینت همزمان Retry کنن، خود Retry میتونه فشار بیشتری به سرویسی که همین الان مشکل داره وارد کنه.
مشکل Retry ساده چیه؟ 🔄
فرض کنین یک سرویس برای چند ثانیه از دسترس خارج شده و 1000 کلاینت همزمان بهش درخواست میفرستن. حالا اگه همهی این کلاینت ها بلافاصله بعد از شکست دوباره درخواست خودشون رو ارسال کنن، تعداد زیادی درخواست جدید دقیقاً همون زمانی وارد سیستم میشه که سرویس هنوز درگیر مشکل قبلیه.
حتی بدتر از اون، اگه Retryها با فاصلهی ثابتی مثل یک ثانیه انجام بشن، کلاینت ها ممکنه دوباره دقیقاً همزمان Retry کنن. در نتیجه به جای اینکه مشکل برطرف بشه، یک موج جدید از درخواستها ایجاد میشه و فشار روی سرویس بیشتر میشه.
Backoff چه مشکلی رو حل میکنه؟ ⏳
Backoff یعنی بعد از شکست یک درخواست، قبل از Retry کردن کمی صبر کنیم. اما معمولاً این زمان ثابت نیست و با هر Retry بیشتر میشه. برای مثال میتونیم زمان انتظار رو به شکل Exponential Backoff افزایش بدیم:
1s → 2s → 4s → 8s → 16sبا این روش، اگه سرویس برای مدت کوتاهی مشکل داشته باشه، کلاینت ها به جای اینکه مدام بهش درخواست بفرستن، کمکم فاصلهی Retryها رو بیشتر میکنن. این کار فرصت بیشتری به سرویس میده تا Recovery کنه و دوباره آمادهی دریافت درخواست بشه.
چرا Exponential Backoff؟ 📈
اگه زمان انتظار با هر Retry بیشتر بشه، تعداد درخواستهای اضافی خیلی بهتر کنترل میشه. فرض کنین اولین Retry بعد از یک ثانیه انجام بشه، Retry بعدی دو ثانیه بعد و بعدی چهار ثانیه بعد. اگه سرویس همچنان Down باشه، کلاینت به جای ارسال مداوم درخواست، به تدریج کمتر به اون سرویس فشار میاره.
البته معمولاً برای Backoff یک Maximum Delay هم تعیین میشه تا زمان انتظار بینهایت زیاد نشه. همچنین تعداد Retryها هم باید محدود باشه، چون Retry کردن یک درخواست برای همیشه نه تنها مفید نیست، بلکه میتونه منابع Application رو هم مصرف کنه.
Jitter چرا مهمه؟ 🎲
حتی Exponential Backoff هم یک مشکل داره. فرض کنین 1000 کلاینت تقریباً در یک زمان درخواست فرستادن و همزمان شکست خوردن. اگه همه دقیقاً از یک فرمول استفاده کنن، ممکنه همهی اونها بعد از 1 ثانیه Retry کنن، بعد 2 ثانیه و بعد 4 ثانیه.
یعنی با اینکه Backoff داریم، هنوز درخواستها میتونن به صورت گروهی و همزمان ارسال بشن. Jitter با اضافه کردن مقداری تصادفی به زمان انتظار، این کلاینتها رو از هم جدا میکنه. در نتیجه به جای اینکه همه با هم Retry کنن، درخواستها توی بازههای زمانی مختلف پخش میشن و فشار روی سرویس یکنواختتر میشه.
Retry برای هر خطایی مناسب نیست ⚠️
یک نکتهی خیلی مهم اینه که هر Errorای نباید باعث Retry بشه. اگه درخواست به خاطر یک خطای موقتی مثل Timeout یا بعضی خطاهای سمت Server شکست خورده باشه، Retry میتونه منطقی باشه.
اما اگه درخواست به خاطر یک خطای دائمی مثل Invalid Input شکست خورده، ارسال دوبارهی همون درخواست احتمالاً هیچ چیزی رو درست نمیکنه.
همچنین برای عملیاتهایی که Side Effect دارن باید دقت بیشتری داشته باشیم. مثلاً اگه یک درخواست باعث ایجاد Order یا پرداخت بشه و کلاینت بعد از Timeout دوباره همون درخواست رو ارسال کنه، ممکنه عملیات دوبار انجام بشه. اینجاست که مفاهیمی مثل Idempotency اهمیت پیدا میکنن.
Retry میتونه مشکل رو بدتر کنه 🔥
Retry در ظاهر یک مکانیزم ساده برای افزایش Reliabilityه، اما اگه درست طراحی نشه میتونه یک Failure رو به یک Failure بزرگتر تبدیل کنه.
وقتی یک سرویس تحت فشار قرار میگیره و کلاینت ها به جای کاهش درخواست، مرتب Retry میکنن، Load بیشتری ایجاد میشه و Recovery سختتر میشه.
به همین دلیل Retry معمولاً در کنار چیزهایی مثل Exponential Backoff، Jitter، Retry Limit و Timeout استفاده میشه تا سیستم هم شانس بیشتری برای Recovery داشته باشه و هم خودش باعث تشدید مشکل نشه.
#️⃣ #network #web #devops
➖➖➖➖➖➖➖➖➖➖
🌙 CHANNEL | GROUP
پروتکل ICMP چطور کار میکنه؟ 🤔
وقتی اسم شبکه میاد، معمولاً سریع میریم سراغ TCP و UDP و IP. اما یک پروتکل دیگه هم وجود داره که برای خودِ انتقال معمولی Data ساخته نشده، بلکه بیشتر برای گزارش وضعیت و خطاهای شبکه استفاده میشه.
اسمش ICMP (Internet Control Message Protocol) هست. یکی از معروفترین جاهایی که ICMP رو میبینیم،
ICMP دقیقاً برای چیه؟ 🧠
ICMP یک پروتکل در کنار IP هست که برای ارسال پیامهای کنترلی و خطاهای مربوط به ارتباطات IP استفاده میشه. مثلاً فرض کنین یک Router یک Packet دریافت میکنه، اما مقصد اون Packet قابل دسترسی نیست. Router نمیتونه Packet رو به مقصد برسونه و در بعضی شرایط میتونه با استفاده از ICMP به فرستنده اطلاع بده که چه اتفاقی افتاده.
پس ICMP قرار نیست مثل TCP یا UDP دادهی Application رو از یک نقطه به نقطهی دیگه منتقل کنه. بیشتر وظیفهش اینه که اطلاعاتی دربارهی وضعیت ارتباط IP در اختیار سیستمها قرار بده.
Ping چطور با ICMP کار میکنه؟ 📡
وقتی دستور
از روی این رفت و برگشت میتونیم بفهمیم مقصد قابل دسترسی هست یا نه و همچنین زمان رفت و برگشت Packet رو اندازه بگیریم که بهش Round-Trip Time (RTT) میگیم.
پس وقتی
ICMP فقط برای Ping نیست ⚠️
یکی از اشتباهات رایج اینه که ICMP رو با Ping یکی بدونیم. Ping فقط یکی از کاربردهای ICMP هست. ICMP پیامهای مختلفی برای شرایط مختلف داره. مثلاً اگه یک Packet به مقصد نرسه یا یک Router نتونه اون رو Forward کنه، ممکنه یک ICMP Error Message برای فرستنده ارسال بشه تا اون رو از مشکل مطلع کنه.
یکی از نمونههای معروفش Destination Unreachable هست که میتونه نشون بده مقصد یا سرویس موردنظر قابل دسترسی نیست.
یکی دیگه از پیامهای معروف این پروتکل، Time Exceeded هست. هر IP Packet یک مقدار به اسم TTL (Time To Live) داره. هر بار که Packet از یک Router عبور میکنه، TTL کاهش پیدا میکنه. اگه TTL قبل از رسیدن Packet به مقصد به صفر برسه، Router اون Packet رو Drop میکنه و معمولاً یک ICMP Time Exceeded برای فرستنده ارسال میکنه.
این دقیقاً یکی از چیزهاییه که ابزارهایی مثل
ICMP روی TCP و UDP قرار نمیگیره 🧩
یک نکته ی مهم دربارهی ICMP اینه که مثل HTTP که روی TCP اجرا میشه یا DNS که میتونه از UDP و TCP استفاده کنه، ICMP روی TCP یا UDP قرار نگرفته. ICMP بخشی از مجموعهی پروتکلهای IP محسوب میشه و پیامهای اون مستقیماً داخل IP Packet قرار میگیرن. به همین دلیل وقتی یک ICMP Echo Request ارسال میشه، خبری از Port Number مثل TCP یا UDP وجود نداره. ICMP ساختار پیام و Type و Code خودش رو داره.
حالا ICMP چه نقشی توی شبکه داره؟ 🌐
ICMP کمک میکنه سیستمها و تجهیزات شبکه دربارهی وضعیت ارتباطات IP اطلاعات بیشتری داشته باشن. از بررسی اینکه یک مقصد قابل دسترسی هست یا نه گرفته تا گزارش بعضی خطاهای مربوط به Routing و انتقال Packetها، ICMP یک کانال مهم برای این نوع اطلاعات فراهم میکنه.
البته ICMP خودش تضمین نمیکنه که یک Host حتماً به یک Application خاص پاسخ میده. مثلاً ممکنه یک Server به Ping جواب نده، ولی سرویس HTTP اون کاملاً در دسترس باشه، چون ممکنه ICMP توسط Firewall فیلتر شده باشه.
جمعبندی ✍️
ICMP یه پروتکل برای انتقال معمولی دادههای Application نیست. وظیفهی اصلیش ارسال Control Message و Error Message مربوط به ارتباطات IP هست.
➖➖➖➖➖➖➖➖➖➖
وقتی اسم شبکه میاد، معمولاً سریع میریم سراغ TCP و UDP و IP. اما یک پروتکل دیگه هم وجود داره که برای خودِ انتقال معمولی Data ساخته نشده، بلکه بیشتر برای گزارش وضعیت و خطاهای شبکه استفاده میشه.
اسمش ICMP (Internet Control Message Protocol) هست. یکی از معروفترین جاهایی که ICMP رو میبینیم،
ping هست. اما ICMP خیلی بیشتر از این حرفهاست و بخش مهمی از نحوهی عیبیابی و مدیریت ارتباطات IP رو تشکیل میده.ICMP دقیقاً برای چیه؟ 🧠
ICMP یک پروتکل در کنار IP هست که برای ارسال پیامهای کنترلی و خطاهای مربوط به ارتباطات IP استفاده میشه. مثلاً فرض کنین یک Router یک Packet دریافت میکنه، اما مقصد اون Packet قابل دسترسی نیست. Router نمیتونه Packet رو به مقصد برسونه و در بعضی شرایط میتونه با استفاده از ICMP به فرستنده اطلاع بده که چه اتفاقی افتاده.
پس ICMP قرار نیست مثل TCP یا UDP دادهی Application رو از یک نقطه به نقطهی دیگه منتقل کنه. بیشتر وظیفهش اینه که اطلاعاتی دربارهی وضعیت ارتباط IP در اختیار سیستمها قرار بده.
Ping چطور با ICMP کار میکنه؟ 📡
وقتی دستور
ping رو اجرا میکنیم، معمولاً یک ICMP Echo Request به مقصد ارسال میشه. اگه مقصد در دسترس باشه و Echo Request رو قبول کنه، یک ICMP Echo Reply برمیگردونه.از روی این رفت و برگشت میتونیم بفهمیم مقصد قابل دسترسی هست یا نه و همچنین زمان رفت و برگشت Packet رو اندازه بگیریم که بهش Round-Trip Time (RTT) میگیم.
پس وقتی
ping میزنیم، در حالت معمول خبری از TCP Connection یا UDP Port نیست. داریم از ICMP برای بررسی Reachability و اندازهگیری زمان رفتوبرگشت استفاده میکنیم.ICMP فقط برای Ping نیست ⚠️
یکی از اشتباهات رایج اینه که ICMP رو با Ping یکی بدونیم. Ping فقط یکی از کاربردهای ICMP هست. ICMP پیامهای مختلفی برای شرایط مختلف داره. مثلاً اگه یک Packet به مقصد نرسه یا یک Router نتونه اون رو Forward کنه، ممکنه یک ICMP Error Message برای فرستنده ارسال بشه تا اون رو از مشکل مطلع کنه.
یکی از نمونههای معروفش Destination Unreachable هست که میتونه نشون بده مقصد یا سرویس موردنظر قابل دسترسی نیست.
یکی دیگه از پیامهای معروف این پروتکل، Time Exceeded هست. هر IP Packet یک مقدار به اسم TTL (Time To Live) داره. هر بار که Packet از یک Router عبور میکنه، TTL کاهش پیدا میکنه. اگه TTL قبل از رسیدن Packet به مقصد به صفر برسه، Router اون Packet رو Drop میکنه و معمولاً یک ICMP Time Exceeded برای فرستنده ارسال میکنه.
این دقیقاً یکی از چیزهاییه که ابزارهایی مثل
traceroute ازش استفاده میکنن تا بفهمن Packet برای رسیدن به مقصد از چه Routerهایی عبور میکنه.ICMP روی TCP و UDP قرار نمیگیره 🧩
یک نکته ی مهم دربارهی ICMP اینه که مثل HTTP که روی TCP اجرا میشه یا DNS که میتونه از UDP و TCP استفاده کنه، ICMP روی TCP یا UDP قرار نگرفته. ICMP بخشی از مجموعهی پروتکلهای IP محسوب میشه و پیامهای اون مستقیماً داخل IP Packet قرار میگیرن. به همین دلیل وقتی یک ICMP Echo Request ارسال میشه، خبری از Port Number مثل TCP یا UDP وجود نداره. ICMP ساختار پیام و Type و Code خودش رو داره.
حالا ICMP چه نقشی توی شبکه داره؟ 🌐
ICMP کمک میکنه سیستمها و تجهیزات شبکه دربارهی وضعیت ارتباطات IP اطلاعات بیشتری داشته باشن. از بررسی اینکه یک مقصد قابل دسترسی هست یا نه گرفته تا گزارش بعضی خطاهای مربوط به Routing و انتقال Packetها، ICMP یک کانال مهم برای این نوع اطلاعات فراهم میکنه.
البته ICMP خودش تضمین نمیکنه که یک Host حتماً به یک Application خاص پاسخ میده. مثلاً ممکنه یک Server به Ping جواب نده، ولی سرویس HTTP اون کاملاً در دسترس باشه، چون ممکنه ICMP توسط Firewall فیلتر شده باشه.
جمعبندی ✍️
ICMP یه پروتکل برای انتقال معمولی دادههای Application نیست. وظیفهی اصلیش ارسال Control Message و Error Message مربوط به ارتباطات IP هست.
ping از Echo Request و Echo Reply استفاده میکنه، traceroute از پیامهای Time Exceeded کمک میگیره و Routerها هم میتونن برای گزارش بعضی خطاهای شبکه از ICMP استفاده کنن.#️⃣ #network
➖➖➖➖➖➖➖➖➖➖
🌙 CHANNEL | GROUP
Circuit Breaker چطور جلوی Chain Failure رو میگیره؟ 🤔
توی یه سیستم ساده، اگه یه سرویس Down بشه، معمولاً فقط همون سرویس دچار مشکل میشه. اما توی یه سیستم توزیع شده و بزرگ که چندین سرویس به هم وابستهان، قضیه میتونه خیلی سریع پیچیده بشه.
فرض کنین Service A برای انجام یه Request به Service B نیاز داره و Service B هم به Service C وابسته است. حالا اگه Service C دچار مشکل بشه، Requestهای Service B شروع به شکست خوردن میکنن و این شکستها میتونن به Service A هم منتقل بشن.
اگه Service A همچنان به Service B درخواست بفرسته و منتظر Timeout بمونه، کمکم Connectionها و منابعش هم درگیر میشن و ممکنه در نهایت خود Service A هم از کار بیفته.
اینجاست که Circuit Breaker وارد میشه.
Circuit Breaker دقیقاً چیه؟ 🧠
Circuit Breaker یه الگوی طراحی برای جلوگیری از ادامه پیدا کردن درخواستها به یه سرویس خراب یا غیرقابل دسترسه. ایدهی اصلیش شبیه Circuit Breakerهای برق هست. وقتی سیستم تشخیص بده تعداد زیادی از درخواستها دارن شکست میخورن، به جای اینکه همچنان درخواستهای جدید رو ارسال کنه، مسیر ارتباط رو موقتاً قطع میکنه.
توی این حالت Requestها دیگه به سرویس مشکلدار ارسال نمیشن و Application میتونه سریعتر یه Error یا Fallback برگردونه. این کار باعث میشه یه Failure محدود، به Failure بزرگتری در کل سیستم تبدیل نشه.
Closed State 🔵
در حالت عادی، Circuit در وضعیت Closed قرار داره. توی این حالت Requestها مثل همیشه به سرویس مقصد ارسال میشن و Circuit Breaker نتیجهی اونها رو بررسی میکنه.
اگه Requestها موفق باشن، اتفاق خاصی نمیفته. اما اگه تعداد خطاها یا Timeoutها از یه Threshold مشخص بیشتر بشه، Circuit Breaker متوجه میشه که احتمالاً سرویس مقصد دچار مشکل شده و Circuit رو باز میکنه.
Open State 🔴
وقتی Circuit وارد حالت Open میشه، Requestهای جدید دیگه به سرویس مقصد ارسال نمیشن. به جای اینکه Application هر بار یه Connection ایجاد کنه، منتظر Timeout بمونه و دوباره شکست بخوره، Circuit Breaker خیلی سریع Request رو Fail میکنه.
این موضوع هم از ارسال Requestهای اضافی جلوگیری میکنه و هم باعث میشه منابع Application برای مدت طولانی درگیر سرویس خراب نشن. در همین مدت، سرویس مقصد هم فرصت پیدا میکنه بدون دریافت حجم زیادی از Requestهای جدید، Recovery کنه.
Half-Open State 🟡
اما Circuit نمیتونه برای همیشه Open باقی بمونه. بعد از گذشت یه مدت مشخص، Circuit وارد وضعیت Half-Open میشه تا بررسی کنه سرویس مقصد دوباره سالم شده یا نه. توی این حالت معمولاً تعداد محدودی Request اجازه پیدا میکنن به سرویس مقصد ارسال بشن.
اگه این Requestها موفق باشن، Circuit دوباره به حالت Closed برمیگرده و ارتباط عادی ادامه پیدا میکنه. اما اگه دوباره Failure اتفاق بیفته، Circuit دوباره Open میشه و مدتی دیگه Requestها رو متوقف میکنه.
Circuit Breaker و Timeout چه فرقی دارن؟ ⏱️
Timeout فقط مشخص میکنه Application تا چه مدت برای دریافت جواب یه Request صبر کنه. اما Circuit Breaker یه قدم جلوتر میره.
اگه یه سرویس مرتب Timeout بشه، بدون Circuit Breaker همچنان Requestهای جدید ارسال میشن و هرکدوم ممکنه تا زمان Timeout منابع Application رو درگیر کنن.
Circuit Breaker بعد از تشخیص این الگوی شکست، جلوی Requestهای جدید رو میگیره و اجازه نمیده منابع سیستم به خاطر یه سرویس خراب بیدلیل مصرف بشن.
Fallback همیشه جواب نیست 💡
وقتی Circuit باز میشه، Application معمولاً باید یه رفتار جایگزین داشته باشه. مثلاً ممکنه به جای دریافت اطلاعات از سرویس اصلی، دادهی Cache شده رو برگردونه یا یه جواب سادهتر به کاربر بده.
اما Fallback نباید فقط برای مخفی کردن Error استفاده بشه. اگه Fallback خودش وابسته به یه سرویس خراب دیگه باشه، ممکنه مشکل فقط از یه سرویس به سرویس دیگه منتقل بشه. پس باید دقت کنیم مسیر جایگزین واقعاً مستقل و قابل استفاده باشه.
جمعبندی ✍️
Circuit Breaker قرار نیست یه سرویس خراب رو درست کنه. کاری که انجام میده اینه که Failure رو محدود میکنه و نمیذاره مشکل یه سرویس به بخشهای دیگهی سیستم سرایت کنه.
با سه وضعیت اصلی Closed، Open و Half-Open، تشخیص میده چه زمانی درخواستها عادی باشن، چه زمانی متوقف بشن و چه زمانی دوباره سلامت سرویس رو بررسی کنه.
➖➖➖➖➖➖➖➖➖➖
توی یه سیستم ساده، اگه یه سرویس Down بشه، معمولاً فقط همون سرویس دچار مشکل میشه. اما توی یه سیستم توزیع شده و بزرگ که چندین سرویس به هم وابستهان، قضیه میتونه خیلی سریع پیچیده بشه.
فرض کنین Service A برای انجام یه Request به Service B نیاز داره و Service B هم به Service C وابسته است. حالا اگه Service C دچار مشکل بشه، Requestهای Service B شروع به شکست خوردن میکنن و این شکستها میتونن به Service A هم منتقل بشن.
اگه Service A همچنان به Service B درخواست بفرسته و منتظر Timeout بمونه، کمکم Connectionها و منابعش هم درگیر میشن و ممکنه در نهایت خود Service A هم از کار بیفته.
اینجاست که Circuit Breaker وارد میشه.
Circuit Breaker دقیقاً چیه؟ 🧠
Circuit Breaker یه الگوی طراحی برای جلوگیری از ادامه پیدا کردن درخواستها به یه سرویس خراب یا غیرقابل دسترسه. ایدهی اصلیش شبیه Circuit Breakerهای برق هست. وقتی سیستم تشخیص بده تعداد زیادی از درخواستها دارن شکست میخورن، به جای اینکه همچنان درخواستهای جدید رو ارسال کنه، مسیر ارتباط رو موقتاً قطع میکنه.
توی این حالت Requestها دیگه به سرویس مشکلدار ارسال نمیشن و Application میتونه سریعتر یه Error یا Fallback برگردونه. این کار باعث میشه یه Failure محدود، به Failure بزرگتری در کل سیستم تبدیل نشه.
Closed State 🔵
در حالت عادی، Circuit در وضعیت Closed قرار داره. توی این حالت Requestها مثل همیشه به سرویس مقصد ارسال میشن و Circuit Breaker نتیجهی اونها رو بررسی میکنه.
اگه Requestها موفق باشن، اتفاق خاصی نمیفته. اما اگه تعداد خطاها یا Timeoutها از یه Threshold مشخص بیشتر بشه، Circuit Breaker متوجه میشه که احتمالاً سرویس مقصد دچار مشکل شده و Circuit رو باز میکنه.
Open State 🔴
وقتی Circuit وارد حالت Open میشه، Requestهای جدید دیگه به سرویس مقصد ارسال نمیشن. به جای اینکه Application هر بار یه Connection ایجاد کنه، منتظر Timeout بمونه و دوباره شکست بخوره، Circuit Breaker خیلی سریع Request رو Fail میکنه.
این موضوع هم از ارسال Requestهای اضافی جلوگیری میکنه و هم باعث میشه منابع Application برای مدت طولانی درگیر سرویس خراب نشن. در همین مدت، سرویس مقصد هم فرصت پیدا میکنه بدون دریافت حجم زیادی از Requestهای جدید، Recovery کنه.
Half-Open State 🟡
اما Circuit نمیتونه برای همیشه Open باقی بمونه. بعد از گذشت یه مدت مشخص، Circuit وارد وضعیت Half-Open میشه تا بررسی کنه سرویس مقصد دوباره سالم شده یا نه. توی این حالت معمولاً تعداد محدودی Request اجازه پیدا میکنن به سرویس مقصد ارسال بشن.
اگه این Requestها موفق باشن، Circuit دوباره به حالت Closed برمیگرده و ارتباط عادی ادامه پیدا میکنه. اما اگه دوباره Failure اتفاق بیفته، Circuit دوباره Open میشه و مدتی دیگه Requestها رو متوقف میکنه.
Circuit Breaker و Timeout چه فرقی دارن؟ ⏱️
Timeout فقط مشخص میکنه Application تا چه مدت برای دریافت جواب یه Request صبر کنه. اما Circuit Breaker یه قدم جلوتر میره.
اگه یه سرویس مرتب Timeout بشه، بدون Circuit Breaker همچنان Requestهای جدید ارسال میشن و هرکدوم ممکنه تا زمان Timeout منابع Application رو درگیر کنن.
Circuit Breaker بعد از تشخیص این الگوی شکست، جلوی Requestهای جدید رو میگیره و اجازه نمیده منابع سیستم به خاطر یه سرویس خراب بیدلیل مصرف بشن.
Fallback همیشه جواب نیست 💡
وقتی Circuit باز میشه، Application معمولاً باید یه رفتار جایگزین داشته باشه. مثلاً ممکنه به جای دریافت اطلاعات از سرویس اصلی، دادهی Cache شده رو برگردونه یا یه جواب سادهتر به کاربر بده.
اما Fallback نباید فقط برای مخفی کردن Error استفاده بشه. اگه Fallback خودش وابسته به یه سرویس خراب دیگه باشه، ممکنه مشکل فقط از یه سرویس به سرویس دیگه منتقل بشه. پس باید دقت کنیم مسیر جایگزین واقعاً مستقل و قابل استفاده باشه.
جمعبندی ✍️
Circuit Breaker قرار نیست یه سرویس خراب رو درست کنه. کاری که انجام میده اینه که Failure رو محدود میکنه و نمیذاره مشکل یه سرویس به بخشهای دیگهی سیستم سرایت کنه.
با سه وضعیت اصلی Closed، Open و Half-Open، تشخیص میده چه زمانی درخواستها عادی باشن، چه زمانی متوقف بشن و چه زمانی دوباره سلامت سرویس رو بررسی کنه.
#️⃣ #system_design #backend #programming
➖➖➖➖➖➖➖➖➖➖
🌙 CHANNEL | GROUP
❤4
Reverse Proxy چطور کار میکنه؟ 🤔
وقتی یه Client میخواد به یه Server وصل بشه، لزوماً قرار نیست مستقیماً با خود Application Server ارتباط داشته باشه. ممکنه بین Client و Server یه سرویس دیگه قرار گرفته باشه که Requestها رو دریافت کنه و بعد اونها رو به Serverهای پشت خودش Forward کنه.
به این سرویس Reverse Proxy میگیم. اما چرا اصلاً باید چنین چیزی وسط مسیر قرار بگیره و چه تفاوتی با حالتی داره که Client مستقیماً به Application Server وصل بشه؟
Reverse Proxy دقیقاً چیه؟ 🧠
Reverse Proxy سرویسیه که جلوی Application Server قرار میگیره و Requestهای Client رو دریافت میکنه. Client در واقع با Reverse Proxy ارتباط برقرار میکنه و Reverse Proxy تصمیم میگیره Request رو به کدوم Server یا Service پشت خودش Forward کنه.
مثلاً ممکنه Client به
چرا Reverse Proxy استفاده میکنیم؟ 🚦
یکی از مهمترین دلایل استفاده از Reverse Proxy اینه که میتونیم یه نقطهی ورودی مشترک برای چندین Service داشته باشیم. مثلاً ممکنه یه سیستم همزمان یه Frontend، یه API، یه سرویس Authentication و یه سرویس Media داشته باشه. Reverse Proxy میتونه بر اساس Domain یا مسیر Request تشخیص بده هر Request باید به کدوم سرویس ارسال بشه.
علاوه بر Routing میشه کارهایی مثل TLS Termination، Load Balancing، Compression و Caching رو هم انجام داد. در نتیجه Application Server لازم نیست همهی این وظایف رو خودش مدیریت کنه.
Reverse Proxy چه فرقی با Forward Proxy داره؟ 🔄
تفاوت اصلی این دو تا توی اینه که Proxy به نمایندگی از چه کسی Request رو ارسال میکنه.
توی Proxy, Forward Proxyمعمولاً بین Client و Internet قرار میگیره و به نمایندگی از Client به Serverهای مختلف Request میفرسته. خود Server مقصد ممکنه Client واقعی رو نبینه و فقط Forward Proxy رو ببینه.
اما تو Proxy, Reverse Proxy جلوی Serverها قرار میگیره و به نمایندگی از اونها Requestهای Client رو دریافت و Forward میکنه.
پس میتونیم یه جورایی بگیم Forward Proxy نمایندهی Client و Reverse Proxy نمایندهی Server هستن.
Reverse Proxy و Load Balancing ⚖️
Reverse Proxy الزاماً Load Balancer نیست، اما میتونه نقش Load Balancer رو هم داشته باشه. فرض کنین سه Instance از یه Application Server داریم. Reverse Proxy میتونه Requestهای ورودی رو بین این سه Instance تقسیم کنه.
تو این حالت Client همچنان فقط یه Endpoint میبینه، ولی پشت اون Endpoint چندین Server وجود دارن و Reverse Proxy مسئول انتخاب مقصد هر Requestه.
این موضوع باعث میشه بتونیم تعداد Instanceها رو افزایش بدیم و Load رو بین اونها پخش کنیم، بدون اینکه Client نیاز داشته باشه چیزی دربارهی ساختار داخلی سیستم بدونه.
چرا Application Server رو مستقیم در معرض اینترنت نذاریم؟ 🛡
قرار دادن Reverse Proxy جلوی Application Server فقط برای Routing نیست. این لایه میتونه به عنوان یه نقطهی کنترل برای Requestهای ورودی هم عمل کنه.
مثلاً میتونیم محدودیتهایی برای اندازهی Request، تعداد Connectionها یا بعضی الگوهای Request تعریف کنیم و TLS رو هم همینجا Terminate کنیم.
در نتیجه Application Server بیشتر روی اجرای Logic مربوط به Application تمرکز میکنه و Reverse Proxy بخشی از وظایف مربوط به Traffic ورودی رو بر عهده میگیره.
جمعبندی ✍️
Reverse Proxy یه لایه بین Client و Application Server قرار میده و Requestهای ورودی رو به Service یا Server مناسب Forward میکنه. این لایه میتونه علاوه بر Routing، وظایفی مثل TLS Termination، Load Balancing، Caching و کنترل Traffic رو هم انجام بده.
به همین دلیل توی خیلی از معماریهای Backend، چیزی مثل NGINX جلوی Application Server قرار میگیره و Client به جای ارتباط مستقیم با Application، اول با Reverse Proxy صحبت میکنه.
➖➖➖➖➖➖➖➖➖➖
وقتی یه Client میخواد به یه Server وصل بشه، لزوماً قرار نیست مستقیماً با خود Application Server ارتباط داشته باشه. ممکنه بین Client و Server یه سرویس دیگه قرار گرفته باشه که Requestها رو دریافت کنه و بعد اونها رو به Serverهای پشت خودش Forward کنه.
به این سرویس Reverse Proxy میگیم. اما چرا اصلاً باید چنین چیزی وسط مسیر قرار بگیره و چه تفاوتی با حالتی داره که Client مستقیماً به Application Server وصل بشه؟
Reverse Proxy دقیقاً چیه؟ 🧠
Reverse Proxy سرویسیه که جلوی Application Server قرار میگیره و Requestهای Client رو دریافت میکنه. Client در واقع با Reverse Proxy ارتباط برقرار میکنه و Reverse Proxy تصمیم میگیره Request رو به کدوم Server یا Service پشت خودش Forward کنه.
مثلاً ممکنه Client به
example.com درخواست بفرسته، اما به جای اینکه مستقیماً به Django یا FastAPI وصل بشه، Request اول به NGINX برسه و NGINX اون رو به Application Server منتقل کنه. در این حالت Client لازم نیست بدونه پشت Reverse Proxy چه Serverهایی وجود دارن.چرا Reverse Proxy استفاده میکنیم؟ 🚦
یکی از مهمترین دلایل استفاده از Reverse Proxy اینه که میتونیم یه نقطهی ورودی مشترک برای چندین Service داشته باشیم. مثلاً ممکنه یه سیستم همزمان یه Frontend، یه API، یه سرویس Authentication و یه سرویس Media داشته باشه. Reverse Proxy میتونه بر اساس Domain یا مسیر Request تشخیص بده هر Request باید به کدوم سرویس ارسال بشه.
علاوه بر Routing میشه کارهایی مثل TLS Termination، Load Balancing، Compression و Caching رو هم انجام داد. در نتیجه Application Server لازم نیست همهی این وظایف رو خودش مدیریت کنه.
Reverse Proxy چه فرقی با Forward Proxy داره؟ 🔄
تفاوت اصلی این دو تا توی اینه که Proxy به نمایندگی از چه کسی Request رو ارسال میکنه.
توی Proxy, Forward Proxyمعمولاً بین Client و Internet قرار میگیره و به نمایندگی از Client به Serverهای مختلف Request میفرسته. خود Server مقصد ممکنه Client واقعی رو نبینه و فقط Forward Proxy رو ببینه.
اما تو Proxy, Reverse Proxy جلوی Serverها قرار میگیره و به نمایندگی از اونها Requestهای Client رو دریافت و Forward میکنه.
پس میتونیم یه جورایی بگیم Forward Proxy نمایندهی Client و Reverse Proxy نمایندهی Server هستن.
Reverse Proxy و Load Balancing ⚖️
Reverse Proxy الزاماً Load Balancer نیست، اما میتونه نقش Load Balancer رو هم داشته باشه. فرض کنین سه Instance از یه Application Server داریم. Reverse Proxy میتونه Requestهای ورودی رو بین این سه Instance تقسیم کنه.
تو این حالت Client همچنان فقط یه Endpoint میبینه، ولی پشت اون Endpoint چندین Server وجود دارن و Reverse Proxy مسئول انتخاب مقصد هر Requestه.
این موضوع باعث میشه بتونیم تعداد Instanceها رو افزایش بدیم و Load رو بین اونها پخش کنیم، بدون اینکه Client نیاز داشته باشه چیزی دربارهی ساختار داخلی سیستم بدونه.
چرا Application Server رو مستقیم در معرض اینترنت نذاریم؟ 🛡
قرار دادن Reverse Proxy جلوی Application Server فقط برای Routing نیست. این لایه میتونه به عنوان یه نقطهی کنترل برای Requestهای ورودی هم عمل کنه.
مثلاً میتونیم محدودیتهایی برای اندازهی Request، تعداد Connectionها یا بعضی الگوهای Request تعریف کنیم و TLS رو هم همینجا Terminate کنیم.
در نتیجه Application Server بیشتر روی اجرای Logic مربوط به Application تمرکز میکنه و Reverse Proxy بخشی از وظایف مربوط به Traffic ورودی رو بر عهده میگیره.
جمعبندی ✍️
Reverse Proxy یه لایه بین Client و Application Server قرار میده و Requestهای ورودی رو به Service یا Server مناسب Forward میکنه. این لایه میتونه علاوه بر Routing، وظایفی مثل TLS Termination، Load Balancing، Caching و کنترل Traffic رو هم انجام بده.
به همین دلیل توی خیلی از معماریهای Backend، چیزی مثل NGINX جلوی Application Server قرار میگیره و Client به جای ارتباط مستقیم با Application، اول با Reverse Proxy صحبت میکنه.
#️⃣ #system_design #backend #network
➖➖➖➖➖➖➖➖➖➖
🌙 CHANNEL | GROUP
❤2
Python Memory Management 🧠
وقتی توی Python یه Object میسازیم، مثلاً یه List یا یه Instance از یک Class، این Object باید جایی توی Memory قرار بگیره. Python خودش مسئول مدیریت این Memory هست و برخلاف زبانهایی مثل C، معمولاً لازم نیست ما دستی مشخص کنیم چه زمانی Memory Allocate یا Free بشه.
اما اینکه Python خودش این کارها رو انجام میده به این معنی نیست که Memory Management ساده هست یا یسری مفاهیم رو از داده. پشت این رفتار، مفاهیمی مثل Reference Counting، Garbage Collection، Memory Allocator و Heap وجود دارن که با هم مشخص میکنن Objectها کجا قرار بگیرن، چه زمانی دیگه مورد استفاده نیستن و چه زمانی Memory مربوط به اونها آزاد بشه.
Python Objectها کجا قرار میگیرن؟ 📦
توی CPython، آبجکت که توی برنامه میسازیم معمولاً روی Heap قرار میگیرن. وقتی مثلاً مینویسیم
این Memory با Stack اشتباه گرفته نشه. Variableای که اسم
Reference Counting 🔢
یکی از مهمترین بخشهای Memory Management تو پایتون، Reference Counting هست. هر Object یک شمارنده داره که تعداد Referenceهایی که به اون Object اشاره میکنن رو دنبال میکنه. وقتی یه Reference جدید به Object اضافه میشه، این Count افزایش پیدا میکنه و وقتی Reference از بین میره، Count کاهش پیدا میکنه.
وقتی Reference Count یه Object به صفر برسه، یعنی دیگه هیچ چیزی از طریق Reference به اون Object دسترسی نداره و CPython میتونه Memory مربوط به اون Object رو آزاد کنه.
پس Garbage Collector برای چیه؟ ♻️
اگه Reference Counting داریم، شاید این سؤال پیش بیاد که پس Garbage Collector دیگه چه کاری انجام میده؟
مشکل زمانی ایجاد میشه که Objectها به صورت Circular به هم Reference داشته باشن. مثلاً فرض کنین آبجکت
در نتیجه Reference Counting به تنهایی نمیتونه بفهمه این Objectها دیگه قابل استفاده نیستن. Garbage Collector مخصوصاً برای پیدا کردن و جمع کردن همین Reference Cycleها وارد عمل میشه.
Python Memory Allocator ⚙️
Python هم برای گرفتن و آزاد کردن Memory مستقیماً برای تکتک Objectها با سیستمعامل درگیر نمیشه. CPython یک Memory Allocator داره که مدیریت Memory مورد نیاز Objectهای Python رو بهینهتر انجام میده. برای Objectهای کوچیک، CPython از مکانیزمهایی مثل pymalloc استفاده میکنه تا به جای اینکه برای هر Allocation مستقیماً سراغ سیستمعامل بره، Memory رو تو بلوکهای بزرگتر دریافت و خودش مدیریت کنه.
این کار باعث میشه Allocation و Deallocation تعداد زیادی Object کوچیک سریع تر انجام بشه و هزینهی تعامل مستقیم با سیستمعامل کمتر بشه.
چرا Memory همیشه به سیستمعامل برنمیگرده؟ 🤔
یکی از چیزهایی که ممکنه موقع بررسی Memory یک برنامهی Python گیجکننده باشه اینه که وقتی یک Object رو حذف میکنیم، ممکنه مقدار Memory مصرفی Process بلافاصله به همون اندازه کاهش پیدا نکنه.
دلیلش اینه که آزاد شدن یک Object لزوماً به معنی پس دادن فوری اون Memory به سیستمعامل نیست. Python ممکنه Memory آزاد شده رو داخل Allocator خودش نگه داره تا بعداً برای Objectهای جدید ازش استفاده کنه.
پس ممکنه Object از بین رفته باشه، اما Process همچنان مقدار زیادی Memory از سیستمعامل گرفته باشه. این دو موضوع با هم تناقضی ندارن.
➖➖➖➖➖➖➖➖➖➖
➖➖➖➖➖➖➖➖➖➖
وقتی توی Python یه Object میسازیم، مثلاً یه List یا یه Instance از یک Class، این Object باید جایی توی Memory قرار بگیره. Python خودش مسئول مدیریت این Memory هست و برخلاف زبانهایی مثل C، معمولاً لازم نیست ما دستی مشخص کنیم چه زمانی Memory Allocate یا Free بشه.
اما اینکه Python خودش این کارها رو انجام میده به این معنی نیست که Memory Management ساده هست یا یسری مفاهیم رو از داده. پشت این رفتار، مفاهیمی مثل Reference Counting، Garbage Collection، Memory Allocator و Heap وجود دارن که با هم مشخص میکنن Objectها کجا قرار بگیرن، چه زمانی دیگه مورد استفاده نیستن و چه زمانی Memory مربوط به اونها آزاد بشه.
Python Objectها کجا قرار میگیرن؟ 📦
توی CPython، آبجکت که توی برنامه میسازیم معمولاً روی Heap قرار میگیرن. وقتی مثلاً مینویسیم
users = []، پایتون برای ساختن List یک Object ایجاد میکنه و Memory مورد نیازش رو از Allocator خودش دریافت میکنه.این Memory با Stack اشتباه گرفته نشه. Variableای که اسم
users رو نگه داشته، در واقع یک Reference به Objectهست و خود List یک Object مستقل روی Heap محسوب میشه. به همین دلیل چند Variable میتونن همزمان به یک Object اشاره کنن.Reference Counting 🔢
یکی از مهمترین بخشهای Memory Management تو پایتون، Reference Counting هست. هر Object یک شمارنده داره که تعداد Referenceهایی که به اون Object اشاره میکنن رو دنبال میکنه. وقتی یه Reference جدید به Object اضافه میشه، این Count افزایش پیدا میکنه و وقتی Reference از بین میره، Count کاهش پیدا میکنه.
وقتی Reference Count یه Object به صفر برسه، یعنی دیگه هیچ چیزی از طریق Reference به اون Object دسترسی نداره و CPython میتونه Memory مربوط به اون Object رو آزاد کنه.
پس Garbage Collector برای چیه؟ ♻️
اگه Reference Counting داریم، شاید این سؤال پیش بیاد که پس Garbage Collector دیگه چه کاری انجام میده؟
مشکل زمانی ایجاد میشه که Objectها به صورت Circular به هم Reference داشته باشن. مثلاً فرض کنین آبجکت
A به B اشاره کنه و B هم به A. حالا ممکنه هیچ Reference خارجی به این دو Object وجود نداشته باشه، اما Reference Count هر دو همچنان بیشتر از صفر باقی بمونه.در نتیجه Reference Counting به تنهایی نمیتونه بفهمه این Objectها دیگه قابل استفاده نیستن. Garbage Collector مخصوصاً برای پیدا کردن و جمع کردن همین Reference Cycleها وارد عمل میشه.
Python Memory Allocator ⚙️
Python هم برای گرفتن و آزاد کردن Memory مستقیماً برای تکتک Objectها با سیستمعامل درگیر نمیشه. CPython یک Memory Allocator داره که مدیریت Memory مورد نیاز Objectهای Python رو بهینهتر انجام میده. برای Objectهای کوچیک، CPython از مکانیزمهایی مثل pymalloc استفاده میکنه تا به جای اینکه برای هر Allocation مستقیماً سراغ سیستمعامل بره، Memory رو تو بلوکهای بزرگتر دریافت و خودش مدیریت کنه.
این کار باعث میشه Allocation و Deallocation تعداد زیادی Object کوچیک سریع تر انجام بشه و هزینهی تعامل مستقیم با سیستمعامل کمتر بشه.
چرا Memory همیشه به سیستمعامل برنمیگرده؟ 🤔
یکی از چیزهایی که ممکنه موقع بررسی Memory یک برنامهی Python گیجکننده باشه اینه که وقتی یک Object رو حذف میکنیم، ممکنه مقدار Memory مصرفی Process بلافاصله به همون اندازه کاهش پیدا نکنه.
دلیلش اینه که آزاد شدن یک Object لزوماً به معنی پس دادن فوری اون Memory به سیستمعامل نیست. Python ممکنه Memory آزاد شده رو داخل Allocator خودش نگه داره تا بعداً برای Objectهای جدید ازش استفاده کنه.
پس ممکنه Object از بین رفته باشه، اما Process همچنان مقدار زیادی Memory از سیستمعامل گرفته باشه. این دو موضوع با هم تناقضی ندارن.
🧩Part01
➖➖➖➖➖➖➖➖➖➖
#️⃣ #programming #backend #python
➖➖➖➖➖➖➖➖➖➖
🌙 CHANNEL | GROUP
حالا
در واقع Reference با اسم
اگه این آخرین Reference باشه، در CPython معمولاً Reference Count به صفر میرسه و Object قابل Deallocation میشه. اما همونطور که گفتیم، Memory آزادشده ممکنه همچنان توسط Python Allocator نگه داشته بشه و فوراً به سیستمعامل برنگرده.
Memory Leak توی Python هم داریم؟ ⚠️
پایتون Garbage Collector داره، اما این به معنی غیرممکن بودن Memory Leak نیست. اگه برنامه به Objectهایی Reference نگه داره که دیگه واقعاً بهشون نیاز نداره، Garbage Collector نمیتونه اونها رو حذف کنه، چون از دید Python هنوز قابل دسترس هستن.
مثلاً یک Cache بدون محدودیت، یک Global Collection که دائماً بزرگتر میشه یا نگه داشتن Reference به Objectهای قدیمی میتونه باعث بشه Memory مصرفی برنامه به مرور افزایش پیدا کنه.
پس Garbage Collector فقط Objectهایی رو جمع میکنه که واقعاً دیگه قابل دسترسی نیستن. اگه خود برنامه همچنان Reference رو نگه داشته باشه، GC نمیتونه تشخیص بده که ما دیگه به اون Object احتیاج نداریم.
جمعبندی ✍️
Memory Management تو Python ترکیبی از چند بخش مختلفه. Objectها روی Python Heap قرار میگیرن، Reference Counting بیشتر Objectهای بدون Reference رو مدیریت میکنه و Garbage Collector برای پیدا کردن Reference Cycleها وارد عمل میشه.
از طرف دیگه، CPython با Memory Allocator خودش Allocationهای کوچک رو مدیریت میکنه و Memory آزادشده رو لزوماً بلافاصله به سیستمعامل برنمیگردونه.
به همین دلیل وقتی دربارهی Memory حرف میزنیم، فقط اینکه «Python خودش Garbage Collection داره» تصویر کاملی بهمون نمیده. باید بدونیم Referenceها، Objectها، Allocator و Garbage Collector چطور کنار هم کار میکنن.
➖➖➖➖➖➖➖➖➖➖
➖➖➖➖➖➖➖➖➖➖
del دقیقاً چی کار میکنه؟ 🗑
del مستقیماً به معنی «این Object رو از Memory پاک کن» نیست. وقتی مینویسیم:del users
در واقع Reference با اسم
users رو حذف میکنیم. اگه Reference دیگهای به Object وجود داشته باشه، Object همچنان باقی میمونه.اگه این آخرین Reference باشه، در CPython معمولاً Reference Count به صفر میرسه و Object قابل Deallocation میشه. اما همونطور که گفتیم، Memory آزادشده ممکنه همچنان توسط Python Allocator نگه داشته بشه و فوراً به سیستمعامل برنگرده.
Memory Leak توی Python هم داریم؟ ⚠️
پایتون Garbage Collector داره، اما این به معنی غیرممکن بودن Memory Leak نیست. اگه برنامه به Objectهایی Reference نگه داره که دیگه واقعاً بهشون نیاز نداره، Garbage Collector نمیتونه اونها رو حذف کنه، چون از دید Python هنوز قابل دسترس هستن.
مثلاً یک Cache بدون محدودیت، یک Global Collection که دائماً بزرگتر میشه یا نگه داشتن Reference به Objectهای قدیمی میتونه باعث بشه Memory مصرفی برنامه به مرور افزایش پیدا کنه.
پس Garbage Collector فقط Objectهایی رو جمع میکنه که واقعاً دیگه قابل دسترسی نیستن. اگه خود برنامه همچنان Reference رو نگه داشته باشه، GC نمیتونه تشخیص بده که ما دیگه به اون Object احتیاج نداریم.
جمعبندی ✍️
Memory Management تو Python ترکیبی از چند بخش مختلفه. Objectها روی Python Heap قرار میگیرن، Reference Counting بیشتر Objectهای بدون Reference رو مدیریت میکنه و Garbage Collector برای پیدا کردن Reference Cycleها وارد عمل میشه.
از طرف دیگه، CPython با Memory Allocator خودش Allocationهای کوچک رو مدیریت میکنه و Memory آزادشده رو لزوماً بلافاصله به سیستمعامل برنمیگردونه.
به همین دلیل وقتی دربارهی Memory حرف میزنیم، فقط اینکه «Python خودش Garbage Collection داره» تصویر کاملی بهمون نمیده. باید بدونیم Referenceها، Objectها، Allocator و Garbage Collector چطور کنار هم کار میکنن.
🧩Part02
➖➖➖➖➖➖➖➖➖➖
#️⃣ #programming #backend #python
➖➖➖➖➖➖➖➖➖➖
🌙 CHANNEL | GROUP