Skip to content

فصل ۸ — سرویس ایمیل توزیع‌شده (Distributed Email Service)

خلاصهٔ تحلیلی — این متن چکیدهٔ مفاهیم فصل به زبان فارسی است، نه ترجمهٔ واژه‌به‌واژه.

طراحی سرویس ایمیل در ابعاد Gmail/Outlook برای ۱ میلیارد کاربر. ایمیل ذاتاً storage-heavy است و برآوردها این را نشان می‌دهند.

گام ۱ — نیازمندی‌ها

  • قابلیت‌ها: ارسال/دریافت، واکشی ایمیل‌ها، فیلتر خوانده/نخوانده، جست‌وجو (موضوع/فرستنده/بدنه)، ضد-اسپم و ضد-ویروس؛ ارتباط کلاینت-سرور با HTTP؛ پیوست مجاز.
  • غیرکارکردی: پایایی (داده نباید گم شود)، دسترس‌پذیری (replication چندگانه)، مقیاس‌پذیری و انعطاف (پروتکل‌های قدیمی POP/IMAP محدودند).
  • برآورد: ارسال ~۱۰ ایمیل/نفر/روز ← ~۱۰۰ هزار QPS ارسال؛ دریافت ~۴۰ ایمیل/روز با متادیتای ~۵۰KB ← ~۷۳۰ پتابایت متادیتا در سال؛ پیوست‌ها (۲۰٪ ایمیل‌ها × ~۵۰۰KB) ← ~۱٬۴۶۰ پتابایت در سال.

دانش پایهٔ ایمیل (Email 101)

  • SMTP: ارسال بین سرورها. POP: دانلود و حذف از سرور (فقط یک دستگاه). IMAP: خواندن روی سرور، مناسب چند دستگاه و اتصال کند — رایج‌ترین. HTTPS برای webmail (مثل ActiveSync در Outlook).
  • رکورد MX در DNS: تعیین سرورهای ایمیل مقصد با اولویت (عدد کمتر = ترجیح بیشتر).
  • پیوست: با Base64/MIME؛ سقف حجم (Gmail ~25MB).

معماری سنتی و محدودیت‌هایش

جریان کلاسیک: کلاینت ← سرور SMTP فرستنده ← (جست‌وجوی MX) ← سرور مقصد ← ذخیره ← واکشی گیرنده با IMAP/POP. ذخیره‌سازی در فایل‌های محلی (مثل Maildir) با رشد حجم به گلوگاه disk I/O و نبود HA می‌خورد؛ پروتکل‌های دههٔ ۶۰-۸۰ برای قابلیت‌های مدرن (thread، جست‌وجو، برچسب) و میلیاردها کاربر طراحی نشده‌اند.

گام ۲ — معماری توزیع‌شده

API های webmail بر HTTP: POST /v1/messages (ارسال)، GET /v1/folders، GET /v1/folders/{id}/messages (با صفحه‌بندی)، GET /v1/messages/{id}.

اجزا: web server ها (request/response)، real-time server ها (stateful؛ push ایمیل جدید با WebSocket و fallback به long polling — نمونهٔ واقعی: JMAP روی WebSocket در Apache James)، دیتابیس متادیتا، object storage برای پیوست‌ها (Cassandra مناسب نیست: محدودیت عملی blob زیر ~1MB و اشغال row cache)، کش توزیع‌شده (Redis برای ایمیل‌های اخیر) و search store (ایندکس معکوس).

جریان ارسال

وب‌سرور اعتبارسنجی پایه را انجام می‌دهد (و اگر گیرنده هم‌دامنه باشد، بدون SMTP مستقیماً در Sent فرستنده و Inbox گیرنده می‌نویسد) ← صف خروجی (پیوست بزرگ به object store و ارجاعش در پیام) ← SMTP outgoing worker ها بررسی اسپم/ویروس، ذخیره در Sent و ارسال به سرور مقصد. صف خروجی باید پایش شود: انباشت یعنی سرور مقصد در دسترس نیست (retry با exponential backoff) یا consumer کم است.

جریان دریافت

Load balancer مخصوص SMTP (با سیاست پذیرش و bounce ایمیل نامعتبر) ← صف ورودی ← mail processing worker ها (اسپم/ویروس) ← ذخیره در storage/کش/object store ← اگر گیرنده آنلاین است push از راه real-time server؛ وگرنه واکشی بعدی با REST.

گام ۳ — بررسی عمیق

دیتابیس متادیتا

ویژگی‌های داده: header کوچک و پرمصرف؛ بدنه بزرگ و معمولاً یک‌بارخوان؛ عملیات‌ها منحصر به خود کاربر؛ ۸۲٪ خواندن‌ها برای دادهٔ زیر ۱۶ روز؛ از دست رفتن داده ممنوع. گزینه‌ها: رابطه‌ای (برای رکوردهای بزرگ و جست‌وجوی BLOB ضعیف)، object storage خام (قابلیت‌های read/search/thread سخت)، NoSQL (Bigtable در Gmail؛ بسته). غول‌ها دیتابیس اختصاصی می‌سازند؛ در مصاحبه ویژگی‌های لازم را بگویید: ستون تا چند MB، سازگاری قوی، کمینه‌سازی disk I/O، و HA و پشتیبان‌گیری افزایشی.

مدل داده (سبک Cassandra): پارتیشن با user_id. جدول‌ها: folders_by_user؛ emails_by_folder با پارتیشن مرکب (user_id, folder_id) و کلید خوشه‌بندی email_id از نوع TIMEUUID (مرتب‌سازی زمانی)؛ emails_by_user + جدول attachments. برای «خوانده/نخوانده» چون is_read کلید نیست، NoSQL کوئری را رد می‌کند ← denormalization: دو جدول read_emails و unread_emails؛ علامت‌گذاری یعنی حذف از یکی و درج در دیگری — کد پیچیده‌تر، خواندن مقیاس‌پذیر.

Thread گفت‌وگو: با فیلدهای header یعنی Message-Id، In-Reply-To و References (الگوریتم JWZ) کلاینت زنجیرهٔ پاسخ‌ها را بازسازی می‌کند.

مبادلهٔ سازگاری: صحت مهم‌تر از دسترس‌پذیری است ← هر mailbox یک primary واحد دارد؛ در failover همگام‌سازی موقتاً متوقف می‌شود (سازگاری به بهای دسترس‌پذیری).

تحویل‌پذیری (deliverability) — سخت‌ترین بخش عملی

راه‌اندازی سرور آسان است؛ نرسیدن به پوشهٔ اسپم سخت است (بیش از ۵۰٪ کل ایمیل‌ها اسپم‌اند و IP تازه اعتباری ندارد):

  • IP اختصاصی برای ارسال؛ تفکیک IP بر اساس دستهٔ ایمیل (marketing جدا از transactional).
  • گرم‌کردن تدریجی IP برای اعتبارسازی (۲ تا ۶ هفته طبق Amazon SES) و مسدودسازی سریع اسپمرها.
  • حلقهٔ بازخورد با ISP ها: صف‌های جداگانه برای hard bounce (آدرس نامعتبر)، soft bounce (خطای موقت) و complaint (دکمهٔ report spam).
  • احراز هویت ایمیل: SPF، DKIM و DMARC در برابر phishing (عامل ~۹۳٪ نفوذها).

جست‌وجو

برخلاف جست‌وجوی گوگل (کل وب، مرتب‌سازی بر اساس ربط، ایندکس با تأخیر)، جست‌وجوی ایمیل محدود به صندوق خود کاربر، مرتب بر اساس ویژگی‌ها و نیازمند ایندکس تقریباً بلادرنگ و دقیق است — و نوشتن بسیار بیشتر از خواندن دارد (هر ارسال/دریافت/حذف = reindex؛ کوئری فقط هنگام فشردن دکمه).

  • Elasticsearch: پارتیشن با user_id؛ reindex ناهمگام از راه Kafka. یکپارچه‌سازی آسان اما همگام نگه‌داشتن با store اصلی و دو نسخهٔ داده چالش است. مناسب مقیاس کوچک/متوسط.
  • موتور اختصاصی: برای مقیاس Gmail؛ گلوگاه اصلی disk I/O است ← ساختار ایندکس با LSM tree (فقط نوشتن ترتیبی؛ همان قلب Bigtable/Cassandra/RocksDB) و جداسازی داده‌های کم‌تغییر (خود ایمیل) از پرتغییر (پوشه‌ها).

مقیاس‌پذیری و دسترس‌پذیری

الگوهای دسترسی کاربران مستقل‌اند ← مقیاس افقی طبیعی است؛ replication چند-دیتاسنتری، کاربر را به نزدیک‌ترین مرکز وصل می‌کند و در network partition مرکز دیگر پاسخ می‌دهد.

نکات تکمیلی مصاحبه

تحمل خطا، compliance (GDPR، legal intercept)، امنیت (ضد-phishing، رمزنگاری) و بهینه‌سازی de-dup پیوست‌های تکراری در ایمیل‌های گروهی.

نکات کلیدی فصل

  • ایمیل مسئلهٔ storage و deliverability است، نه صرفاً پیام‌رسانی؛ برآورد پتابایتی مسیر طراحی را تعیین می‌کند.
  • صف‌ها در هر دو جریان ارسال/دریافت، پردازش سنگین (اسپم/ویروس) را از مسیر همگام جدا می‌کنند.
  • در NoSQL «کوئری‌ها اسکیما را طراحی می‌کنند»: denormalization برای read/unread نمونهٔ کلاسیک است.
  • اعتبار فرستنده (IP گرم‌شده + SPF/DKIM/DMARC + حلقهٔ بازخورد) به اندازهٔ معماری اهمیت دارد.
  • انتخاب Elasticsearch یا موتور اختصاصی تابع مقیاس و توان تیمی است، نه مد روز.

یادداشت‌های مترجم

  • SMTP/IMAP/POP، MX record، LSM tree و SPF/DKIM/DMARC لاتین حفظ شده‌اند.