
امروز ما هیجان زده هستیم که در مورد پینگورا ، یک پروکسی جدید HTTP که ما با استفاده از زنگ زدگی در خانه ساخته ایم که بیش از 1 تریلیون درخواست در روز ایجاد کرده است ، عملکرد ما را تقویت می کند ، و بسیاری از ویژگی های جدید را برای مشتریان Cloudflare امکان پذیر می کند ، در حالی که فقط یک سوم نیاز دارد. از CPU و منابع حافظه زیرساخت های پروکسی قبلی ما.
همانطور که CloudFlare مقیاس کرده است ، ما از Nginx پیشی گرفته ایم. این سالها بسیار عالی بود ، اما با گذشت زمان محدودیت های آن در مقیاس ما به معنای ایجاد چیزی جدید بود. ما دیگر نتوانستیم عملکرد مورد نیاز خود را بدست آوریم و NGINX ویژگی هایی را که برای محیط بسیار پیچیده خود نیاز داشتیم ، نداریم.
بسیاری از مشتریان و کاربران CloudFlare از شبکه جهانی CloudFlare به عنوان یک پروکسی بین مشتریان HTTP (مانند مرورگرهای وب ، برنامه ها ، دستگاه های IoT و موارد دیگر) و سرورها استفاده می کنند. در گذشته ، ما در مورد چگونگی اتصال مرورگرها و سایر نمایندگان کاربر به شبکه ما بسیار صحبت کرده ایم ، و ما فناوری زیادی را توسعه داده ایم و پروتکل های جدید را پیاده سازی کرده ایم (به Quic و بهینه سازی برای HTTP2 مراجعه کنید) تا این مرحله از اتصال را برقرار کند. کارآمدتر.
امروز ، ما روی بخش دیگری از معادله تمرکز می کنیم: خدمتی که بین شبکه و سرورهای ما در اینترنت ترافیک می کند. این سرویس پروکسی به CDN ، کارگران واکشی ، تونل ، جریان ، R2 و بسیاری ، بسیاری از ویژگی ها و محصولات دیگر قدرت می دهد.
بیایید به این دلیل بپردازیم که چرا ما تصمیم به جایگزینی سرویس میراث خود گرفتیم و چگونه پینگورا را توسعه دادیم ، سیستم جدید ما که به طور خاص برای موارد و مقیاس استفاده از مشتری CloudFlare طراحی شده است.
چرا یک پروکسی دیگر بسازید
با گذشت سالها ، استفاده ما از Nginx در برابر محدودیت ها پیش رفته است. برای برخی از محدودیت ها ، ما در اطراف آنها بهینه یا کار کردیم. اما دیگران برای غلبه بر آنها بسیار سخت تر بودند.
محدودیت های معماری به عملکرد آسیب می رساند
معماری NGINX کارگر (فرآیند) دارای اشکالاتی در مورد موارد استفاده ما است که به عملکرد و کارآیی ما آسیب می رساند.
اول ، در nginx هر درخواست فقط توسط یک کارگر واحد قابل ارائه است. این منجر به بار نامتعادل در تمام هسته های CPU می شود که منجر به کندی می شود.
به دلیل این اثر پین کردن فرآیند درخواست ، درخواست هایی که CPU سنگین یا مسدود کردن وظایف IO را انجام می دهند می توانند درخواست های دیگر را کاهش دهند. از آنجا که این پست های وبلاگ گواهی می دهند ، ما زمان زیادی را صرف کار کردن در مورد این مشکلات کردیم.
مهمترین مشکل برای موارد استفاده ما استفاده مجدد از اتصال است. ماشین های ما اتصالات TCP را به سرورهای مبداء به درخواست های HTTP پروکسی برقرار می کنند. استفاده مجدد از اتصال با استفاده از اتصالات قبلاً تأسیس شده از استخر اتصال ، پرش از دستهای TCP و TLS مورد نیاز در اتصال جدید ، سرعت TTFB (زمان به اولین بار) را با استفاده از اتصالات قبلاً تأسیس شده ، سرعت می بخشد.
با این حال ، استخر اتصال Nginx برای هر کارگر است. هنگامی که یک درخواست بر روی یک کارگر خاص قرار می گیرد ، فقط می تواند از اتصالات موجود در آن کارگر استفاده مجدد کند. وقتی کارگران NGINX بیشتری را برای مقیاس کردن اضافه می کنیم ، نسبت استفاده مجدد از اتصال ما بدتر می شود زیرا اتصالات در استخرهای جدا شده تر از همه فرآیندها پراکنده می شوند. این منجر به آهسته تر TTFB و اتصالات بیشتر برای حفظ می شود که منابع (و پول) را هم برای ما و هم برای مشتریان خود مصرف می کند.

همانطور که در پست های وبلاگ گذشته ذکر شد ، ما برای برخی از این موضوعات راه حل داریم. اما اگر بتوانیم به مسئله اساسی بپردازیم: مدل کارگر/فرآیند ، همه این مشکلات را به طور طبیعی برطرف خواهیم کرد.
اضافه کردن برخی از انواع قابلیت ها دشوار است
NGINX یک سرور وب بسیار خوب ، Balancer Load یا یک دروازه ساده است. اما Cloudflare بیش از این کار می کند. ما قبلاً تمام عملکردهای مورد نیاز خود را در اطراف NGINX ایجاد می کردیم ، که انجام آن کار ساده ای نیست در حالی که سعی می کنیم بیش از حد از پایگاه بالادست NGINX فاصله نگیریم.
به عنوان مثال ، هنگام تلاش مجدد/عدم موفقیت بیش از یک درخواست ، گاهی اوقات می خواهیم با مجموعه ای متفاوت از عنوان های درخواست ، درخواست را به سرور Origin متفاوت ارسال کنیم. اما این چیزی نیست که nginx به ما اجازه می دهد انجام دهیم. در مواردی از این دست ، ما وقت و تلاش خود را صرف کار در اطراف محدودیت های Nginx می کنیم.
در همین حال ، زبانهای برنامه نویسی که ما باید با آنها کار کنیم ، به کاهش مشکلات کمک نمی کند. Nginx صرفاً در C است که با طراحی از حافظه ایمن نیست. کار با چنین پایه کد شخص ثالث بسیار مستعد خطا است. ورود به مشکلات ایمنی حافظه ، حتی برای مهندسان باتجربه بسیار آسان است و ما می خواستیم تا حد امکان از این موارد جلوگیری کنیم.
زبان دیگری که ما برای تکمیل C استفاده کردیم LUA است. کمتر خطرناک است اما عملکرد کمتری نیز دارد. علاوه بر این ، ما اغلب خودمان را هنگام کار با کد پیچیده LUA و منطق تجارت از دست داده ایم.
و جامعه Nginx خیلی فعال نیست و توسعه تمایل به "پشت درهای بسته" دارد.
انتخاب خودمان را انتخاب می کنیم
طی چند سال گذشته ، همانطور که ما همچنان به رشد پایگاه مشتری و مجموعه ویژگی های خود ادامه داده ایم ، ما به طور مداوم سه گزینه را ارزیابی می کنیم:
- به سرمایه گذاری در NGINX ادامه دهید و احتمالاً آن را برای انطباق 100٪ مطابق با نیازهای ما به کار ببرید. ما تخصص مورد نیاز را داشتیم، اما با توجه به محدودیت های معماری که در بالا ذکر شد، تلاش قابل توجهی برای بازسازی آن به گونه ای لازم است که به طور کامل نیازهای ما را پشتیبانی کند.
- به پایگاه کد پروکسی شخص ثالث دیگری مهاجرت کنید. قطعا پروژه های خوبی وجود دارد، مانند فرستاده و دیگران. اما این مسیر به این معنی است که همان چرخه ممکن است چند سال دیگر تکرار شود.
- با یک تخته سنگ تمیز شروع کنید، یک پلت فرم و چارچوب داخلی بسازید. این انتخاب به بیشترین سرمایه گذاری اولیه از نظر تلاش مهندسی نیاز دارد.
ما در چند سال گذشته هر یک از این گزینه ها را هر سه ماهه ارزیابی کردیم. هیچ فرمول واضحی وجود ندارد که بگوید کدام انتخاب بهترین است. برای چندین سال، ما با کمترین مقاومت مسیر را ادامه دادیم و به تقویت NGINX ادامه دادیم. با این حال، در برخی مواقع، ساختن بازده سرمایه گذاری پروکسی خودمان ارزشش را داشت. ما تماس گرفتیم تا یک پروکسی از ابتدا بسازیم و شروع به طراحی برنامه پروکسی رویاهایمان کردیم.
پروژه پینگورا
تصمیمات طراحی
برای ایجاد پروکسی که میلیون ها درخواست در ثانیه را سریع، کارآمد و امن ارائه می کند، ابتدا باید چند تصمیم مهم در طراحی بگیریم.
ما Rust را به عنوان زبان پروژه انتخاب کردیم، زیرا می تواند کاری را که C می تواند به روشی امن در حافظه انجام دهد، بدون به خطر انداختن عملکرد، انجام دهد.
اگرچه چندین کتابخانه HTTP شخص ثالث عالی وجود دارد، مانند hyper، ما تصمیم گرفتیم که خودمان را بسازیم زیرا می خواهیم انعطاف پذیری را در نحوه مدیریت ترافیک HTTP به حداکثر برسانیم و مطمئن شویم که می توانیم با سرعت خودمان نوآوری کنیم.
در Cloudflare، ما ترافیک را در سراسر اینترنت مدیریت می کنیم. ما موارد زیادی از ترافیک HTTP عجیب و غیر سازگار با RFC داریم که باید از آنها پشتیبانی کنیم. این یک معضل رایج در سراسر جامعه و وب HTTP است، که در آن تنش بین پیروی دقیق از مشخصات HTTP و تطبیق با تفاوت های ظریف یک اکوسیستم گسترده از مشتریان یا سرورهای بالقوه قدیمی وجود دارد. انتخاب یک طرف می تواند کار سختی باشد.
کدهای وضعیت HTTP در RFC 9110 به عنوان یک عدد صحیح سه رقمی تعریف می شوند و عموماً انتظار می رود که در محدوده 100 تا 599 باشند. Hyper یکی از این پیاده سازی ها بود. با این حال، بسیاری از سرورها از استفاده از کدهای وضعیت بین 599 و 999 پشتیبانی می کنند. مشکلی برای این ویژگی ایجاد شده بود که جنبه های مختلف بحث را بررسی می کرد. در حالی که تیم هایپر در نهایت آن تغییر را پذیرفتند، دلایل معتبری برای رد چنین درخواستی برای آنها وجود داشت، و این تنها یکی از موارد متعدد رفتار ناسازگاری بود که ما نیاز به حمایت داشتیم.
به منظور برآورده کردن الزامات موقعیت CloudFlare در اکوسیستم HTTP ، ما به یک کتابخانه HTTP قوی ، مجاز و قابل تنظیم نیاز داشتیم که بتواند از وحشی اینترنت زنده بماند و از انواع موارد استفاده غیر سازگار پشتیبانی کند. بهترین راه برای تضمین آن اجرای خودمان است.
تصمیم طراحی بعدی حول سیستم برنامه ریزی بار کاری ما بود. ما به منظور به اشتراک گذاشتن منابع ، به ویژه استخرهای اتصال ، به راحتی ، چند رشته را بیش از چند پردازش انتخاب کردیم. ما همچنین تصمیم گرفتیم که برای جلوگیری از برخی کلاسهای مشکلات عملکردی که در بالا ذکر شد ، دزدی کار لازم است. زمان اجرا Tokio Async مناسب برای نیازهای ما بود.
سرانجام ، ما می خواستیم که پروژه ما بصری و توسعه دهنده باشد. آنچه ما می سازیم محصول نهایی نیست و باید به عنوان یک سکوی قابل گسترش باشد زیرا ویژگی های بیشتری در بالای آن ساخته می شود. ما تصمیم گرفتیم یک رابط قابل برنامه ریزی مبتنی بر رویداد "زندگی یک درخواست" را شبیه به Nginx/OpenResty اجرا کنیم. به عنوان مثال ، مرحله "فیلتر درخواست" به توسعه دهندگان اجازه می دهد تا هنگام دریافت یک هدر درخواست ، کد را اصلاح یا رد کنند. با این طراحی ، می توانیم منطق کسب و کار و منطق پراکسی عمومی خود را به طور پاک از هم جدا کنیم. توسعه دهندگان که قبلاً روی NGINX کار می کردند می توانند به راحتی به پینگورا تغییر دهند و به سرعت تولید شوند.
پینگورا در تولید سریعتر است
بیایید سریع به حال حاضر شویم. پینگورا تقریباً هر درخواست HTTP را که نیاز به تعامل با یک سرور Origin دارد (برای مثال برای حافظه پنهان) انجام می دهد ، و ما داده های عملکرد زیادی را در این فرآیند جمع آوری کرده ایم.
ابتدا ، ببینیم که چگونه پینگورا ترافیک مشتری ما را سرعت می بخشد. ترافیک کلی در پینگورا کاهش 5ms در کاهش متوسط TTFB و 80ms در صدک 95 را نشان می دهد. این به این دلیل نیست که ما کد را سریعتر اجرا می کنیم. حتی خدمات قدیمی ما می تواند درخواست های موجود در محدوده زیر میلیسوت را کنترل کند.
پس انداز از معماری جدید ما ناشی می شود که می تواند اتصالات را در همه موضوعات به اشتراک بگذارد. این به معنای نسبت استفاده مجدد اتصال بهتر است ، که زمان کمتری را در دست های TCP و TLS می گذراند.

در تمام مشتریان ، پینگورا در مقایسه با سرویس قدیمی تنها یک سوم اتصالات جدید در ثانیه را ایجاد می کند. برای یکی از مشتری های اصلی ، نسبت استفاده مجدد از اتصال را از 87. 1 ٪ به 99. 92 ٪ افزایش داد ، که باعث کاهش اتصالات جدید به منشأ آنها تا 160 برابر شد. برای ارائه شماره به صورت شهودی تر ، با جابجایی به پینگورا ، ما هر روز 434 سال زمان دستی را به مشتریان و کاربران خود ذخیره می کنیم.
ویژگی های بیشتر
داشتن یک رابط کاربری دوستدار توسعه دهنده که مهندسان با آن آشنا هستند و در عین حال حذف محدودیت های قبلی به ما امکان می دهد تا ویژگی های بیشتری را با سرعت بیشتری توسعه دهیم. عملکردهای اصلی مانند پروتکل های جدید به عنوان بلوک های سازنده محصولات بیشتری که می توانیم به مشتریان ارائه دهیم عمل می کند.
به عنوان مثال، ما توانستیم پشتیبانی HTTP/2 بالادستی را بدون موانع عمده به Pingora اضافه کنیم. این به ما اجازه داد تا مدت کوتاهی بعد gRPC را به مشتریان خود ارائه دهیم. افزودن همین قابلیت به NGINX به تلاش مهندسی بسیار بیشتری نیاز دارد و ممکن است محقق نشود.
اخیراً ما Cache Reserve را اعلام کرده ایم که در آن Pingora از حافظه R2 به عنوان لایه ذخیره سازی استفاده می کند. همانطور که ما عملکردهای بیشتری را به Pingora اضافه می کنیم، می توانیم محصولات جدیدی را ارائه دهیم که قبلاً امکان پذیر نبودند.
کارآمدتر
در تولید، Pingora در مقایسه با سرویس قدیمی ما با همان بار ترافیک، حدود 70٪ CPU کمتر و 67٪ حافظه کمتر مصرف می کند. پس انداز ناشی از چند عامل است.
کد Rust ما در مقایسه با کد قدیمی Lua ما کارآمدتر اجرا می شود. علاوه بر این، تفاوت های کارایی با معماری آنها نیز وجود دارد. برای مثال، در NGINX/OpenResty، زمانی که کد Lua می خواهد به یک هدر HTTP دسترسی پیدا کند، باید آن را از ساختار NGINX C بخواند، یک رشته Lua را اختصاص دهد و سپس آن را در رشته Lua کپی کند. پس از آن، لوا باید رشته جدید خود را نیز جمع آوری کند. در Pingora، این فقط یک دسترسی مستقیم به رشته است.
مدل چند رشته ای همچنین اشتراک گذاری داده ها را در بین درخواست ها کارآمدتر می کند. NGINX همچنین دارای حافظه مشترک است اما به دلیل محدودیت های پیاده سازی، هر دسترسی به حافظه مشترک باید از قفل mutex استفاده کند و فقط رشته ها و اعداد را می توان در حافظه مشترک قرار داد. در Pingora، بیشتر موارد به اشتراک گذاشته شده را می توان مستقیماً از طریق مراجع مشترک در پشت شمارنده های مرجع اتمی در دسترس قرار داد.
بخش مهم دیگری از صرفه جویی در CPU، همانطور که در بالا ذکر شد، ایجاد اتصالات جدید کمتر است. دست دادن TLS در مقایسه با ارسال و دریافت داده از طریق اتصالات ایجاد شده گران است.
امن تر
حمل و نقل سریع و ایمن ویژگی ها دشوار است، به خصوص در مقیاس ما. پیش بینی هر مورد لبه ای که می تواند در یک محیط توزیع شده که میلیون ها درخواست را در ثانیه پردازش می کند، رخ دهد، دشوار است. تجزیه و تحلیل فازی و استاتیک تنها می تواند تا حد زیادی کاهش دهد. معناشناسی ایمن Rust از ما در برابر رفتارهای نامشخص محافظت می کند و به ما اطمینان می دهد که سرویس ما به درستی اجرا می شود.
با این تضمین ها می توانیم بیشتر روی چگونگی تعامل تغییر در سرویس ما با سایر خدمات یا منشأ مشتری تمرکز کنیم. ما می توانیم ویژگی هایی را در یک کادو بالاتر ایجاد کنیم و از ایمنی حافظه و تشخیص تصادفات سخت نباشیم.
هنگامی که تصادفات رخ می دهد ، یک مهندس برای تشخیص چگونگی وقوع و ایجاد آن باید وقت خود را صرف کند. از زمان آغاز پینگورا ، ما چند صد تریلیون درخواست کرده ایم و به دلیل کد خدمات ما هنوز سقوط نکرده ایم.
در حقیقت ، تصادفات پینگورا بسیار نادر است که معمولاً وقتی با یکی روبرو می شویم ، مسائل نامربوط پیدا می کنیم. اخیراً پس از شروع سرویس ما ، یک اشکال هسته را کشف کردیم. ما همچنین مشکلات سخت افزاری را در چند دستگاه کشف کرده ایم ، در گذشته که اشکالات حافظه نادر ناشی از نرم افزار ما را حتی پس از اشکال زدایی قابل توجه تقریباً غیرممکن بود.
نتیجه
به طور خلاصه ، ما یک پروکسی داخلی ساخته ایم که سریعتر ، کارآمدتر و همه کاره تر به عنوان بستر محصولات فعلی و آینده ما است.
ما در مورد مشکلاتی که با آن روبرو بودیم ، بهینه سازی هایی که ما اعمال کردیم و درسهایی که از ساخت پینگورا آموخته ایم و آن را برای برقراری بخش قابل توجهی از اینترنت آموخته ایم ، با جزئیات فنی بیشتری باز خواهیم گشت. ما همچنین با برنامه خود برای باز کردن منبع باز خواهیم گشت.
پینگورا آخرین تلاش ما برای بازنویسی سیستم ما است ، اما آخرین ما نخواهد بود. همچنین تنها یکی از بلوک های ساختمانی در مجلسی مجدد سیستم های ما است.
آیا علاقه دارید به ما بپیوندید تا در ایجاد اینترنت بهتر کمک کنید؟تیم های مهندسی ما در حال استخدام هستند.
برای شروع کار با برنامه رایگان ما که اینترنت شما را سریعتر و ایمن تر می کند ، از 1. 1. 1. 1 بازدید کنید.
برای کسب اطلاعات بیشتر در مورد مأموریت ما برای کمک به ایجاد اینترنت بهتر ، از اینجا شروع کنید. اگر به دنبال یک مسیر شغلی جدید هستید ، موقعیت های باز ما را بررسی کنید.
مقالات آموزش فارکس...
ما را در سایت مقالات آموزش فارکس دنبال می کنید
برچسب :
نویسنده : بهزاد فراهانی
بازدید : <-PostHit->
تاريخ : جمعه
25 فروردين
1402 ساعت: 15:22