نکات کلیدی
-
راهبرد تکثیر جهانی نتفلیکس تضمین میکند دادهها در چهار منطقه در دسترس باشند، تأخیر را کمینه میکند و در زمان قطعی منطقهای یا فیلاور، قابلیت اتکای سامانه را افزایش میدهد.
-
EVCache، یک مخزن توزیعشدهٔ کلید-مقدار که پشتوانهٔ آن SSD است، محور راهبرد کشکردن نتفلیکس است و مقیاسپذیری خطی و تابآوری قدرتمندی برای مدیریت حجم عظیم داده فراهم میکند.
-
زیرساخت EVCache نتفلیکس شامل ۲۰۰ کلاستر Memcached و ۲۲,۰۰۰ نمونهٔ سرور است که در سطح جهانی ۳۰ میلیون رویداد تکثیر و ۴۰۰ میلیون عملیات در ثانیه را مدیریت میکند. این سامانه حدود ۲ تریلیون آیتم را با مجموع ۱۴.۳ پتابایت مدیریت میکند که ظرفیت و مقیاسپذیری عظیم آن را نشان میدهد.
-
کلاینت EVCache آگاه از توپولوژی با استفاده از تکثیر آغازشده توسط کلاینت (client-initiated replication)، مسیردهی داده را بهینه میکند، بار سرور را کاهش میدهد و امکان راهبردهای تکثیر انعطافپذیر را فراهم میسازد.
-
پیادهسازی فشردهسازی دستهای و مهاجرت به Eureka DNS برای کشف سرویس، مصرف پهنای باند شبکه و هزینههای انتقال را بهطور قابل توجهی کاهش داده و کارایی کلی را افزایش داده است.
مقدمه
در دنیای امروز که بیشازحد به هم متصل است، ارائهٔ تجربهٔ کاربری روان و واکنشپذیر حیاتی است؛ بهخصوص برای سرویسهای سرگرمی جهانی مانند نتفلیکس که میخواهند برای میلیونها نفر در سراسر جهان شادی ایجاد کنند. یکی از چالشهای کلیدی برای رسیدن به این هدف، تضمین در دسترس بودن دادهها در چندین منطقه است. اینجا همان جایی است که کش توزیعشده وارد عمل میشود.
کش توزیعشده روشی برای ذخیرهٔ داده در چندین سرور است تا دسترسی سریع و دسترسپذیری بالا تضمین شود. این کار بار روی پایگاهدادههای بکاند را کاهش میدهد و بازیابی داده را سریعتر میکند، در نتیجه تجربهٔ کاربری روانتری فراهم میشود.
در این مقاله بررسی میکنیم نتفلیکس چگونه از EVCache، یک راهکار کش توزیعشده، برای مدیریت پیچیدگیهای تکثیر جهانی استفاده میکند. معماری، اصول طراحی و راهبردهای نوآورانهای را مرور میکنیم که به نتفلیکس اجازه میدهند در مقیاسی عظیم کار کند و همزمان استانداردهای سختگیرانهٔ عملکرد را حفظ کند.
EVCache: ستون فقرات راهکار کشِ نتفلیکس
EVCache مخفف Ephemeral Volatile Cache است. این یک مخزن توزیعشدهٔ کلید-مقدار مبتنی بر Memcached است که برای ارائهٔ مقیاسپذیری خطی و تابآوری قدرتمند طراحی شده است. با وجود نامش، EVCache از پشتوانهٔ SSD بهره میبرد و ذخیرهسازی قابل اتکا و پایدار را تضمین میکند.
هرچند Memcached بهصورت پیشفرض برای پایدارسازی از SSD استفاده نمیکند، اما افزونهٔ extstore آن اجازه میدهد دادههایی که کمتر مورد دسترسی قرار میگیرند به SSD منتقل شوند و RAM برای دادههای پرتکرار آزاد بماند. نتفلیکس در EVCache از این قابلیت بهره میگیرد و سرعت RAM را با ظرفیت SSD ترکیب میکند تا عملکرد و کارایی ذخیرهسازی را بهینه کند.
EVCache نتفلیکس در چهار منطقه مستقر است و شامل ۲۰۰ کلاستر Memcached است که برای پشتیبانی از موارد استفادهٔ مختلف تنظیم شدهاند. هر کلاستر مسئول سرویسدهی به یک اپ یا گروهی از اپهاست. این زیرساخت گسترده شامل ۲۲,۰۰۰ نمونهٔ سرور است که به سامانه اجازه میدهد در سطح جهانی ۳۰ میلیون رویداد تکثیر و ۴۰۰ میلیون عملیات کلی در ثانیه را مدیریت کند. از نظر داده، EVCache حدود ۲ تریلیون آیتم را با مجموع ۱۴.۳ پتابایت مدیریت میکند که ظرفیت و مقیاسپذیری عظیم آن را نشان میدهد.
ویژگیهای کلیدی EVCache:
-
تکثیر جهانی: EVCache تکثیر جهانی روان و یکپارچه را در کلاینت ادغام میکند و دسترسی کارآمد بینمنطقهای به داده را ممکن میسازد.
-
کلاینت آگاه از توپولوژی: کلاینت EVCache از مکانهای فیزیکی و منطقی سرورها آگاه است و بازیابی و ذخیرهسازی داده را بهینه میکند.
-
تابآوری: EVCache برای تحمل خرابی در سطوح مختلف طراحی شده است، از جمله سطح نمونه (instance)، ناحیهٔ دسترسپذیری و سطح منطقهای.
-
استقرارهای بدون وقفه: EVCache از استقرارهای بدون وقفه پشتیبانی میکند و امکان بهروزرسانی و تغییرات را بدون downtime یا اختلال سرویس فراهم میسازد.
چرا دادهٔ کش را تکثیر کنیم؟
در دسترس بودن سریع داده برای حفظ تجربهٔ کاربری روان و واکنشپذیر حیاتی است. با در دسترس نگه داشتن دادهٔ کش، سامانه از پرسوجوهای زمانبر و پرهزینهٔ پایگاهداده جلوگیری میکند. این دسترسی فوری باعث میشود کاربران درگیر و راضی بمانند. در صورت رخ دادن فیلاور منطقهای، داشتن دادهٔ کش در منطقهٔ فیلاور باعث میشود سرویس بدون وقفه و با کمترین تأخیر ادامه پیدا کند و هیچ اختلال محسوسی برای کاربران ایجاد نشود. این سطح از عملکرد و قابلیت اتکا برای برآورده کردن انتظارات کاربر و حفظ تعامل بالا ضروری است.
ساخت پیشنهادهای شخصیسازیشده به منابع محاسباتی قابل توجهی نیاز دارد؛ بهخصوص برای نتفلیکس که الگوریتمهای یادگیری ماشین نقش کلیدی دارند. محاسبهٔ دوبارهٔ داده در صورت cache miss میتواند بسیار CPU-محور و پرهزینه باشد. هر cache miss به توان محاسباتی زیادی برای بازیابی و محاسبهٔ مجدد داده نیاز دارد، از جمله اجرای مدلهای پیچیدهٔ یادگیری ماشین. با تکثیر دادهٔ کش در مناطق مختلف، نتفلیکس این محاسبههای مجددِ گران را کمینه میکند، هزینههای عملیاتی را کاهش میدهد و تجربهٔ کاربری روانتری تضمین میکند. این کار دسترسی سریع به داده و ارائهٔ کارآمد توصیههای شخصیسازیشده را ممکن میسازد و سامانه را مقرونبهصرفهتر و پاسخگوتر میکند.
طراحی سرویس تکثیر جهانی

شکل ۱: تکثیر داده بین دو منطقه
نمودار بالا نشان میدهد EVCache چگونه داده را میان مناطق تکثیر میکند. این فرایند از مراحل زیر تشکیل شده است:
-
ارسال جهش (Send Mutation): برنامه با استفاده از کلاینت EVCache انواع فراخوانیهای جهش مانند set، add، delete و touch را به سرورهای محلی EVCache ارسال میکند.
-
ارسال فراداده (Send Metadata): EVCache یک رویداد ناهمگام شامل فراداده را به Kafka ارسال میکند. این فراداده شامل اطلاعات حیاتی مانند کلید، TTL (زمان ماندگاری) و زمانمُهر ایجاد است. اما بهطور مشخص مقدار (value) را شامل نمیشود تا Kafka با بارهای دادهای بالقوه بزرگ بیش از حد سنگین نشود.
-
پولینگ پیامها (Poll Messages): سرویس خوانندهٔ تکثیر (replication reader service) بهصورت پیوسته پیامها را از Kafka میخواند. این سرویس مسئول پردازش و آمادهسازی رویدادهای فراداده برای مرحلهٔ بعد است.
-
خواندن محلی (Local Read): پس از خواندن فراداده از Kafka، سرویس خوانندهٔ تکثیر یک فراخوانی خواندن به سرور EVCache در منطقهٔ محلی میفرستد تا جدیدترین داده برای آن کلید را بازیابی کند. این مرحله تضمین میکند تازهترین مقدار دریافت شود بدون اینکه بار بیموردی روی Kafka قرار بگیرد. همچنین خواننده میتواند پیامهای ناخواسته را فیلتر کند، مثل پیامهایی با TTL بسیار کوتاه (مثلاً ۵ ثانیه)، تا فرایند تکثیر بر اساس نیازهای کسبوکار بهینه شود.
-
ترافیک بینمنطقهای (Cross-Region Traffic): سپس سرویس خوانندهٔ تکثیر یک فراخوانی بینمنطقهای به سرویس نویسندهٔ تکثیر (replication writer service) در منطقهٔ مقصد انجام میدهد. این فراخوانی شامل همهٔ اطلاعات مرتبط، از جمله فراداده و مقدار بازیابیشده است تا داده با دقت تکثیر شود.
-
نوشتن در مقصد (Destination Write): سرویس نویسندهٔ تکثیر فراخوانی بینمنطقهای را دریافت میکند و جفت کلید-مقدار را در سرور EVCache در منطقهٔ مقصد مینویسد. این مرحله تضمین میکند داده بهصورت سازگار بین مناطق تکثیر شود.
-
خواندن داده (Read Data): وقتی داده در منطقهٔ فیلاور خوانده میشود، به دلیل فرایند تکثیر از قبل در دسترس است. این موضوع تأخیر را به حداقل میرساند و تجربهٔ کاربری روانی فراهم میکند.
مدیریت خطا در سامانهٔ تکثیر
شکل ۲: مدیریت خطا در سامانهٔ تکثیر نتفلیکس
در یک سامانهٔ توزیعشده، خرابی میتواند در هر مرحله از فرایند تکثیر رخ دهد. برای تضمین قابلیت اتکای داده و حفظ یکپارچگی فرایند تکثیر، نتفلیکس از Amazon Simple Queue Service (SQS) برای مدیریت خطای قدرتمند استفاده میکند؛ به دلیل قابلیتهای قابل اعتماد صف پیام آن.
وقتی در هر مرحله از فرایند تکثیر خطایی رخ میدهد، جهشِ ناموفق (failed mutation) ثبت میشود و به یک صف SQS ارسال میگردد تا هیچ جهشِ ناموفقی از دست نرود و بعداً قابل تلاش مجدد باشد. سرویس تکثیر صف SQS را برای جهشهای ناموفق پایش میکند و پس از شناسایی، آنها را دوباره از طریق جریان کاری تکثیر پردازش میکند. این سازوکار retry تضمین میکند همهٔ جهشها در نهایت پردازش شوند، قابلیت اتکای داده بین مناطق حفظ شود و ریسک از دست رفتن داده به حداقل برسد.
نگاه دقیقتر به سرویس خواننده و نویسندهٔ تکثیر

شکل ۳: چندین نمونهٔ سرویس خواننده داده را به مناطق مختلف تکثیر میکنند
نمودار بالا سرویس تکثیر نتفلیکس را نشان میدهد. سرویس خواننده در US-EAST-1 داده را از تاپیکها و پارتیشنهای Kafka میکشد، آن را پردازش میکند و به سرویس نویسنده در مناطق دیگر ارسال میکند؛ و سرویس نویسنده داده را در سرورهای EVCache مینویسد و در دسترس بودن منطقهای را تضمین میکند.
سرویس خوانندهٔ تکثیر (Replication Reader Service)
سرویس خواننده پیامها را از Kafka دریافت میکند، تبدیلهای لازم را اعمال میکند و تازهترین مقدارها را از سرویس محلی EVCache میگیرد. برای سادهسازی مدیریت، نتفلیکس از یک کلاستر Kafka واحد برای سرویس تکثیر استفاده میکند که از بیش از ۲۰۰ کلاستر EVCache پشتیبانی میکند. هر کلاستر EVCache بهعنوان یک تاپیک در Kafka نمایش داده میشود و تاپیکها بر اساس حجم رویدادهایی که مدیریت میکنند پارتیشنبندی میشوند.
گروههای مصرفکنندهٔ متفاوتی برای خواندن از این کلاستر Kafka درون سرویس خواننده تعیین میشوند. هر گروه مصرفکننده متناظر با مجموعهای از نودهاست و هر نود مسئول خواندن چندین پارتیشن است. این ساختار امکان پردازش موازی و توزیع مؤثر بار را فراهم میکند و گذردهی بالا و مدیریت کارآمد داده را تضمین میکند. هر گروه مصرفکننده یک منطقهٔ متفاوت را هدف میگیرد و تضمین میکند داده در چندین منطقه تکثیر شود.
سرویس خواننده روی نمونههای EC2 میزبانی میشود و مقیاسپذیری و تابآوری فراهم میکند. هر سرویس خواننده یک کلاستر محاسباتی است؛ گروهی از رایانههای بههمپیوسته (نودها) که با هم برای انجام محاسبات پیچیده و کارهای پردازش داده همکاری میکنند. با سازماندهی خوانندهها بهصورت گروههای مصرفکننده، نتفلیکس میتواند سامانه را بهصورت افقی مقیاس دهد تا با تقاضا همگام شود.
پس از آنکه سرویس خواننده دادهٔ لازم را بازیابی و تبدیل کرد، یک فراخوانی بینمنطقهای به سرویس نویسنده در منطقهٔ هدف آغاز میکند. تبدیل شامل تبدیل دادهٔ بازیابیشده (که شامل کلید، فراداده و مقدار است) به قالب JSON مناسب برای انتقال از طریق REST است. این فراخوانی همهٔ اطلاعات لازم را شامل میشود و تضمین میکند داده با دقت تکثیر شود.
سرویس نویسندهٔ تکثیر (Replication Writer Service)
وظیفهٔ اصلی سرویس نویسنده دریافت فراخوانیهای REST از سرویس خواننده و نوشتن داده در سرویس مقصد EVCache است. سرویس نویسنده نیز روی نمونههای EC2 میزبانی میشود و مقیاسپذیری و تابآوری فراهم میکند. پس از دریافت داده، سرویس نویسنده آن را پردازش میکند و جفت کلید-مقدار را روی سرور EVCache در منطقهٔ مقصد مینویسد. این کار در دسترس بودن داده بین مناطق را تضمین میکند. سرویس نویسنده برای مدیریت حجمهای بزرگ داده بهصورت کارآمد طراحی شده است، یکپارچگی فرایند تکثیر را حفظ میکند و تضمین میکند سامانه مقاوم و قابل اتکا باقی بماند.
چرا تکثیرِ آغازشده توسط کلاینت را به جای تکثیرِ آغازشده توسط سرور انتخاب کردیم؟
برای EVCache، ما تکثیر آغازشده توسط کلاینت را انتخاب کردیم، عمدتاً چون کلاینت EVCache آگاه از توپولوژی است. این آگاهی از توپولوژی به کلاینت اجازه میدهد داده را در محیط کش توزیعشده بهصورت کارآمد مدیریت و مسیردهی کند. بهطور مشخص، کلاینت EVCache از موارد زیر آگاه است:
-
مکان نودها (Node Locations): کلاینت مکانهای فیزیکی یا منطقی نودهای memcached را میداند، از جمله اینکه کدام نودها در کدام دیتاسنتر یا منطقه هستند.
-
در دسترس بودن نودها (Node Availability): کلاینت میداند کدام نودهای memcached اکنون در دسترس و عملیاتیاند و میتواند از نودهای از کار افتاده یا مشکلدار دوری کند.
-
توزیع داده (Data Distribution): کلاینت میفهمد داده چگونه بین نودهای memcached توزیع شده است، از جمله اینکه کدام نودها رپلیکای آیتمهای دادهٔ مشخص را نگه میدارند.
-
تأخیر شبکه (Network Latency): کلاینت میتواند بر اساس تأخیر شبکه تصمیم بگیرد و نودهایی را انتخاب کند که سریعترین زمان پاسخ را برای عملیات خواندن و نوشتن فراهم میکنند.
مزیتهای تکثیر آغازشده توسط کلاینت
-
آگاهی از توپولوژی: همانطور که توضیح داده شد، آگاهی توپولوژیک کلاینت EVCache به آن اجازه میدهد تصمیمهای هوشمندانه دربارهٔ مسیردهی داده و تکثیر بگیرد. این کار توزیع کارآمد داده را تضمین میکند و تأخیر را کمینه میسازد.
-
کاهش بار سرور: با آغاز کردن تکثیر در سطح کلاینت، بار محاسباتی روی سرورها کاهش مییابد. این به سرورها اجازه میدهد روی مسئولیتهای اصلیشان مثل سرویسدهی درخواستهای خواندن و نوشتن تمرکز کنند، بدون سربار اضافهٔ مدیریت وظایف تکثیر.
-
مقیاسپذیری: تکثیر آغازشده توسط کلاینت مقیاسدهی افقی را سادهتر میکند. با افزایش تعداد کلاینتها، بار کاری تکثیر میان آنها توزیع میشود، گلوگاهِ تکنقطهای ایجاد نمیشود و سامانه میتواند بار بیشتر را مدیریت کند.
-
انعطافپذیری: با تکثیر آغازشده توسط کلاینت، پیادهسازی و مدیریت راهبردهای مختلف تکثیر و بهینهسازیها در سطح کلاینت آسانتر است. این موارد شامل فیلتر کردن پیامهای ناخواسته، اعمال قواعد ویژهٔ کسبوکار و تنظیم پویا رفتار تکثیر بر اساس شرایط جاری میشود.
عیبهای تکثیر آغازشده توسط کلاینت
-
پیچیدگی در مدیریت کلاینت: مدیریت منطق تکثیر در سطح کلاینت میتواند پیچیدگی ایجاد کند. اطمینان از اینکه همهٔ کلاینتها با جدیدترین منطق تکثیر بهروز هستند و مدیریت ناسازگاریهای احتمالی بین کلاینتها میتواند چالشبرانگیز باشد.
-
افزایش ترافیک شبکه: تکثیر آغازشده توسط کلاینت میتواند ترافیک شبکه را افزایش دهد، چون هر کلاینت مسئول ارسال دادهٔ تکثیر بین مناطق است. این موضوع نسبت به رویکرد متمرکزِ آغازشده توسط سرور میتواند مصرف پهنای باند بیشتر و ازدحام احتمالی ایجاد کند؛ زیرا در رویکرد متمرکز، ترافیک تکثیر میتواند کارآمدتر مدیریت و بهینه شود.
-
تکرار پیامها (Message Duplication): چون هر کلاینت مسئول آغاز کردن تکثیر است، امکان تلاشهای تکراری وجود دارد، بهخصوص اگر چند کلاینت دادههای مشابهی را مدیریت کنند. این میتواند به ناکارآمدی و مصرف بیشتر منابع منجر شود.
-
مدیریت خطا: پیادهسازی مدیریت خطای قدرتمند و سازوکارهای retry در سطح کلاینت میتواند از رویکرد متمرکزِ آغازشده توسط سرور پیچیدهتر باشد. تضمین قابلیت اتکای داده در برابر خرابی شبکه یا کرش کلاینتها لایهٔ دیگری از پیچیدگی اضافه میکند.
بهبودهای کارایی
نتفلیکس بهصورت پیوسته تلاش میکند زیرساخت خود را هم از نظر عملکرد و هم از نظر هزینه بهینه کند. دو بهبود مهم در فرایند تکثیر EVCache شامل پیادهسازی فشردهسازی دستهای و حذف بالانسرهای شبکه بوده است.
فشردهسازی دستهای (Batch Compression)
برای کاهش مصرف پهنای باند شبکه، فشردهسازی دستهای را برای دادهای که از خواننده به نویسنده منتقل میشود پیادهسازی کردیم. این فرایند شامل دستهبندی چندین پیام و اعمال فشردهسازی Zstandard روی آن دسته است. با فشردهسازی داده پیش از انتقال، به کاهش ۳۵٪ در مصرف پهنای باند شبکه رسیدیم. این بهینهسازی قابل توجه نهتنها هزینهها را کاهش میدهد، بلکه کارایی کلی فرایند تکثیر را نیز افزایش میدهد. فشردهسازی دستهای تضمین میکند انتقال داده میان کلاسترهای خواننده و نویسنده کارآمدتر باشد، سربار کمتر شود و گذردهی بهتر شود.
شکل ۴: مقایسهٔ اندازهٔ Payload فشردهشده در برابر فشردهنشده
این نمودار کاهش کلی اندازهٔ payload را در موارد استفاده و کلاسترهای مختلف نشان میدهد. به دلیل الگوهای ترافیکی متنوع هر کلاستر، ما صرفهجویی تجمعی حدود ۳۵٪ در گذردهی شبکه مشاهده کردیم.
حذف بالانسرهای شبکه (Removing Network Load Balancers)
در ابتدا برای مدیریت ارتباط میان سرویسهای خواننده و نویسنده از Network Load Balancerها (NLB) استفاده میکردیم. اما این پیکربندی هزینههای اضافی انتقال شبکه ایجاد میکرد. برای حل این مسئله، از استفاده از NLBها به سمت استفاده از Eureka DNS برای کشف سرویس مهاجرت کردیم. با Eureka DNS میتوانیم آدرسهای IP نودهای نویسنده را دریافت کنیم و خودمان مسیردهی را انجام دهیم. این تغییر هزینههای انتقال شبکه را ۵۰٪ کاهش داد، در حالی که تأخیرهای قابل پیشبینی و توزیع بار کارآمد حفظ شد.
شکل ۵: مقایسهٔ توپولوژی ارتباطی با و بدون NLB
مهاجرت به لودبالانسینگ سمت کلاینت زیرساخت ما را سادهتر کرد و بدون قربانی کردن عملکرد، هزینهها را بهطور چشمگیری کاهش داد. با حذف وابستگی به NLBها، کنترل بیشتری روی فرایند مسیردهی ترافیک به دست آوردیم و این به بهرهبرداری کارآمدتر از منابع منجر شد.
شکل ۶: بایتهای پردازششده در NLB قبل و بعد از مهاجرت
نمودار بالا میزان ترافیکی را نشان میدهد که از طریق NLBها مسیردهی میشد. از آنجا که از NLBها مهاجرت کردیم و شروع به استفاده از لودبالانسینگ سمت کلاینت کردیم، توانستیم بهطور قابل توجهی در هزینههای انتقال شبکه صرفهجویی کنیم.
مصرف روزانهٔ NLB در مجموع مناطق حدود ۴۵ GB/s بود و پس از این تغییر، مصرف به کمتر از ۱۰۰ MB/s کاهش یافت. ما همچنان NLBها را بهعنوان یک گزینهٔ پشتیبان در معماریمان نگه میداریم.
جمعبندی
ساخت یک سامانهٔ کش جهانیِ مقاوم، مقیاسپذیر و کارآمد برای نتفلیکس حیاتی است تا تجربهٔ کاربری روانی ارائه دهد. با بهرهگیری از EVCache و یک سرویس تکثیر خوشطراحیشده، نتفلیکس دسترسپذیری بالا، تأخیر کم و کارایی هزینهای را تضمین میکند. کلاینت EVCache آگاه از توپولوژی، همراه با تکثیر آغازشده توسط کلاینت، مدیریت و مسیردهی کارآمد داده را ممکن میسازد، بار سرورها را کاهش میدهد و مقیاسپذیری را افزایش میدهد.
از طریق انتخابهای طراحی دقیق، مانند استفاده از یک کلاستر Kafka واحد برای مدیریت سادهتر و پیادهسازی یک سرویس خوانندهٔ تکثیر انعطافپذیر، نتفلیکس زیرساخت کش خود را برای مدیریت حجم عظیم داده در چندین منطقه بهینه کرده است. این کار تضمین میکند داده همیشه آماده و در دسترس باشد، حتی در سناریوهای فیلاور، و تجربهٔ کاربری روان و واکنشپذیر حفظ شود.
در ادامهٔ مسیر بهبود مستمر و نوآوری، مشتاقیم سامانههایمان را بیش از پیش ارتقا دهیم تا نیازهای در حال تحول کاربران جهانیمان را برآورده کنیم. با پالایش مداوم رویکردها و پذیرش فناوریهای جدید، نتفلیکس هدف دارد تجربهای مقاومتر، پاسخگوتر و مقرونبهصرفهتر برای کاربران در سراسر جهان ارائه دهد.




