سوالات متداول در مورد معماری و کار VKontakte

تاریخچه ایجاد VKontakte در ویکی پدیا است؛ این توسط خود پاول گفته شده است. به نظر می رسد که همه او را از قبل می شناسند. درباره داخلی، معماری و ساختار سایت در HighLoad++ Pavel در سال 2010 به من گفت. بسیاری از سرورها از آن زمان به بیرون درز کرده اند، بنابراین ما اطلاعات را به روز می کنیم: آن را تشریح می کنیم، داخل آن را بیرون می آوریم، وزن می کنیم و از نقطه نظر فنی به دستگاه VK نگاه می کنیم.

سوالات متداول در مورد معماری و کار VKontakte

الکسی آکولوویچ (AterCattus) توسعه دهنده باطن در تیم VKontakte. متن این گزارش پاسخی جمعی به سؤالات متداول در مورد عملکرد پلت فرم، زیرساخت، سرورها و تعامل بین آنها است، اما نه در مورد توسعه، یعنی نه. در مورد آهن. به طور جداگانه، در مورد پایگاه های داده و آنچه VK به جای آن دارد، در مورد جمع آوری گزارش ها و نظارت بر کل پروژه به عنوان یک کل. جزئیات زیر برش.



بیش از چهار سال است که من با انواع کارهای مربوط به باطن سروکار دارم.

  • آپلود، ذخیره، پردازش، توزیع رسانه: ویدئو، پخش زنده، صدا، عکس، اسناد.
  • زیرساخت، پلتفرم، نظارت بر توسعه‌دهنده، گزارش‌ها، حافظه‌های پنهان منطقه‌ای، CDN، پروتکل RPC اختصاصی.
  • ادغام با سرویس های خارجی: اعلان های فشار، تجزیه لینک خارجی، فید RSS.
  • کمک به همکاران در مورد سؤالات مختلف، که پاسخ به آنها مستلزم فرو رفتن در کد ناشناخته است.

در این مدت من در بسیاری از اجزای سایت دست داشتم. من می خواهم این تجربه را به اشتراک بگذارم.

معماری عمومی

همه چیز، طبق معمول، با یک سرور یا گروهی از سرورها شروع می شود که درخواست ها را می پذیرند.

سرور جلو

سرور جلو درخواست ها را از طریق HTTPS، RTMP و WSS می پذیرد.

HTTPS - اینها درخواست هایی برای نسخه های وب اصلی و تلفن همراه سایت است: vk.com و m.vk.com و سایر مشتریان رسمی و غیر رسمی API ما: مشتریان تلفن همراه، پیام رسان ها. پذیرایی داریم RTMP-ترافیک برای پخش زنده با سرورهای مجزای جلو و WSS- اتصالات برای Streaming API.

برای HTTPS و WSS روی سرورها ارزش دارد انجیناکس. برای پخش RTMP، ما اخیراً به راه حل خودمان تغییر داده ایم کیو، اما از حوصله گزارش خارج است. برای تحمل خطا، این سرورها آدرس های IP رایج را تبلیغ می کنند و به صورت گروهی عمل می کنند تا در صورت بروز مشکل در یکی از سرورها، درخواست های کاربران از بین نرود. برای HTTPS و WSS، همین سرورها ترافیک را رمزگذاری می کنند تا بخشی از بار CPU را بر عهده بگیرند.

ما بیشتر در مورد WSS و RTMP صحبت نمی کنیم، بلکه فقط در مورد درخواست های استاندارد HTTPS که معمولاً با یک پروژه وب مرتبط هستند صحبت خواهیم کرد.

بخش مدیریت

پشت قسمت جلویی معمولاً سرورهای باطنی وجود دارد. آنها درخواست هایی را که سرور جلویی از مشتریان دریافت می کند پردازش می کنند.

آن سرورهای kPHP، که دیمون HTTP روی آن اجرا می شود، زیرا HTTPS قبلاً رمزگشایی شده است. kPHP سروری است که روی آن اجرا می شود مدل های پیش چنگال: یک پردازش اصلی را شروع می کند، یک دسته از پردازش های فرزند، سوکت های گوش دادن را به آنها ارسال می کند و آنها درخواست های خود را پردازش می کنند. در این حالت، فرآیندها بین هر درخواست کاربر مجدداً راه اندازی نمی شوند، بلکه به سادگی به جای راه اندازی مجدد، وضعیت خود را به حالت صفر اصلی - درخواست پس از درخواست بازنشانی می کنند.

توزیع بار

همه بک‌اندهای ما مجموعه عظیمی از ماشین‌ها نیستند که بتوانند هر درخواستی را پردازش کنند. ما آنها به گروه های جداگانه تقسیم می شوند: عمومی، موبایل، api، ویدئو، مرحله ... مشکل در یک گروه جداگانه از ماشین ها روی همه ماشین های دیگر تاثیری نخواهد داشت. در صورت بروز مشکل در ویدیو، کاربری که به موسیقی گوش می دهد حتی از مشکلات آن مطلع نخواهد شد. nginx در قسمت جلو با توجه به پیکربندی تصمیم می گیرد که درخواست به کدام backend ارسال شود.

جمع آوری متریک و تعادل مجدد

برای اینکه بفهمیم در هر گروه چند ماشین باید داشته باشیم، ما به QPS اعتماد نکنید. Backend ها متفاوت هستند، درخواست های متفاوتی دارند، هر درخواست پیچیدگی متفاوتی برای محاسبه QPS دارد. به همین دلیل ما ما با مفهوم بار روی سرور به عنوان یک کل کار می کنیم - روی CPU و Perf.

ما هزاران سرور از این قبیل داریم. هر سرور فیزیکی یک گروه kPHP را برای بازیافت تمام هسته ها اجرا می کند (زیرا kPHP تک رشته ای است).

سرور محتوا

CS یا Content Server یک فضای ذخیره سازی است. CS سروری است که فایل‌ها را ذخیره می‌کند و همچنین فایل‌های آپلود شده و انواع وظایف همگام پس‌زمینه‌ای را که بخش اصلی وب به آن اختصاص می‌دهد، پردازش می‌کند.

ما ده ها هزار سرور فیزیکی داریم که فایل ها را ذخیره می کنند. کاربران عاشق آپلود فایل ها هستند و ما دوست داریم آنها را ذخیره و به اشتراک بگذاریم. برخی از این سرورها توسط سرورهای pu/pp ویژه بسته می شوند.

pu/pp

اگر تب شبکه را در VK باز کردید، pu/pp را دیدید.

سوالات متداول در مورد معماری و کار VKontakte

pu/pp چیست؟ اگر سرور را یکی پس از دیگری ببندیم، دو گزینه برای آپلود و دانلود فایل در سرور بسته شده وجود دارد: مستقیما از طریق http://cs100500.userapi.com/path یا از طریق سرور میانی - http://pu.vk.com/c100500/path.

Pu نام تاریخی آپلود عکس است و pp پروکسی عکس است. یعنی یک سرور برای آپلود عکس است و دیگری برای آپلود. اکنون نه تنها عکس ها بارگذاری شده اند، بلکه نام آن حفظ شده است.

این سرورها جلسات HTTPS را خاتمه دهیدبرای حذف بار پردازنده از حافظه همچنین از آنجایی که فایل های کاربر در این سرورها پردازش می شوند، هرچه اطلاعات حساس کمتری در این ماشین ها ذخیره شود، بهتر است. به عنوان مثال، کلیدهای رمزگذاری HTTPS.

از آنجایی که ماشین‌ها توسط ماشین‌های دیگر ما بسته می‌شوند، می‌توانیم به آنها IP خارجی "سفید" ندهیم، و دادن "خاکستری". به این ترتیب ما در استخر IP صرفه جویی کردیم و تضمین کردیم که از ماشین ها در برابر دسترسی خارجی محافظت می کنیم - به سادگی هیچ IP برای ورود به آن وجود ندارد.

انعطاف پذیری در برابر IP های مشترک. از نظر تحمل خطا، این طرح یکسان عمل می کند - چندین سرور فیزیکی یک IP فیزیکی مشترک دارند و سخت افزار روبروی آنها انتخاب می کند که کجا درخواست ارسال شود. در مورد گزینه های دیگر بعدا صحبت خواهم کرد.

نکته بحث برانگیز این است که در این مورد مشتری اتصالات کمتری را حفظ می کند. اگر IP یکسانی برای چندین ماشین وجود داشته باشد - با میزبان یکسان: pu.vk.com یا pp.vk.com، مرورگر مشتری محدودیتی در تعداد درخواست‌های همزمان به یک میزبان دارد. اما در زمان HTTP/2 همه جا حاضر، من معتقدم که این دیگر چندان مرتبط نیست.

عیب آشکار این طرح این است که مجبور است پمپ تمام ترافیک، که از طریق سرور دیگری به فضای ذخیره سازی می رود. از آنجایی که ما ترافیک را از طریق ماشین‌ها پمپاژ می‌کنیم، هنوز نمی‌توانیم با استفاده از همان طرح ترافیک سنگین، به عنوان مثال، ویدئو را پمپاژ کنیم. ما آن را مستقیماً منتقل می کنیم - یک اتصال مستقیم جداگانه برای ذخیره سازی های جداگانه مخصوصاً برای ویدیو. ما محتوای سبک تر را از طریق یک پروکسی منتقل می کنیم.

چندی پیش نسخه بهبود یافته پروکسی را دریافت کردیم. اکنون به شما می گویم که تفاوت آنها با معمولی چیست و چرا این امر ضروری است.

خورشید

در سپتامبر 2017، اوراکل که قبلا Sun را خریداری کرده بود، تعداد زیادی از کارمندان Sun را اخراج کرد. می توان گفت که در این لحظه شرکت متوقف شد. هنگام انتخاب نام برای سیستم جدید، مدیران ما تصمیم گرفتند یاد و خاطره این شرکت را گرامی بدارند و نام سیستم جدید را Sun گذاشتند. در میان خود ما به سادگی او را "خورشید" می نامیم.

سوالات متداول در مورد معماری و کار VKontakte

pp چند مشکل داشت. یک IP در هر گروه - کش بی اثر. چندین سرور فیزیکی یک آدرس IP مشترک را به اشتراک می گذارند و هیچ راهی برای کنترل اینکه درخواست به کدام سرور می رود وجود ندارد. بنابراین، اگر کاربران مختلف برای یک فایل بیایند، در این صورت اگر یک کش در این سرورها وجود داشته باشد، فایل به کش هر سرور ختم می شود. این یک طرح بسیار ناکارآمد است، اما هیچ کاری نمی توان انجام داد.

در نتیجه - ما نمی توانیم مطالب را خرد کنیم، زیرا ما نمی توانیم سرور خاصی را برای این گروه انتخاب کنیم - آنها یک IP مشترک دارند. همچنین به دلایل داخلی ما داریم امکان نصب چنین سرورهایی در مناطق وجود نداشت. آنها فقط در سن پترزبورگ ایستادند.

با خورشیدها سیستم انتخاب را تغییر دادیم. حالا داریم مسیریابی anycast: مسیریابی پویا، هرکست، دیمون خود چک. هر سرور دارای IP مجزای خود است، اما یک زیرشبکه مشترک. همه چیز به گونه ای پیکربندی شده است که اگر یک سرور از کار بیفتد، ترافیک به طور خودکار در سرورهای دیگر همان گروه پخش می شود. اکنون امکان انتخاب یک سرور خاص وجود دارد، بدون کش اضافی، و قابلیت اطمینان تحت تأثیر قرار نگرفت.

پشتیبانی از وزن. اکنون می‌توانیم در صورت نیاز ماشین‌هایی با قدرت‌های مختلف نصب کنیم و همچنین در صورت بروز مشکلات موقتی، وزن خورشیدهای فعال را تغییر دهیم تا بار روی آن‌ها کم شود تا «استراحت» کنند و دوباره شروع به کار کنند.

اشتراک گذاری بر اساس شناسه محتوا. یک چیز خنده دار در مورد اشتراک گذاری: ما معمولاً محتوا را به گونه ای تقسیم می کنیم که کاربران مختلف از طریق همان "sun" به یک فایل بروند تا یک کش مشترک داشته باشند.

ما اخیراً برنامه "Clover" را راه اندازی کردیم. این یک مسابقه آنلاین در یک پخش زنده است که در آن میزبان سوالاتی را می پرسد و کاربران به صورت بلادرنگ پاسخ می دهند و گزینه ها را انتخاب می کنند. این برنامه دارای یک چت است که در آن کاربران می توانند چت کنند. می تواند به طور همزمان به پخش متصل شود بیش از 100 هزار نفر. همه آنها پیام هایی می نویسند که برای همه شرکت کنندگان ارسال می شود و یک آواتار به همراه پیام می آید. اگر 100 هزار نفر برای یک آواتار در یک "خورشید" بیایند، گاهی اوقات می تواند پشت ابر بچرخد.

به منظور مقاومت در برابر انبوه درخواست‌ها برای یک فایل، برای نوع خاصی از محتوا است که ما یک طرح احمقانه را روشن می‌کنیم که فایل‌ها را در تمام «آفتاب‌های» موجود در منطقه پخش می‌کند.

خورشید از درون

پروکسی معکوس در nginx، کش یا در حافظه رم یا در دیسک های سریع Optane/NVMe. مثال: http://sun4-2.userapi.com/c100500/path - پیوندی به "خورشید" که در منطقه چهارم، گروه سرور دوم قرار دارد. فایل مسیر را می بندد که به صورت فیزیکی روی سرور 100500 قرار دارد.

مخزن

ما یک گره دیگر را به طرح معماری خود اضافه می کنیم - محیط کش.

سوالات متداول در مورد معماری و کار VKontakte

در زیر نمودار طرح بندی آمده است کش های منطقه ای، حدود 20 مورد از آنها وجود دارد. این مکان‌هایی هستند که انبارها و "خورشیدها" در آن قرار دارند که می‌توانند ترافیک را از طریق خود ذخیره کنند.

سوالات متداول در مورد معماری و کار VKontakte

این ذخیره سازی محتوای چندرسانه ای است؛ هیچ اطلاعات کاربری در اینجا ذخیره نمی شود - فقط موسیقی، ویدیو، عکس.

برای تعیین منطقه کاربر، ما ما پیشوندهای شبکه BGP اعلام شده در مناطق را جمع آوری می کنیم. در مورد بازگشت مجدد، اگر نتوانستیم IP را با پیشوندها پیدا کنیم، باید پایگاه داده geoip را تجزیه کنیم. ما منطقه را با IP کاربر تعیین می کنیم. در کد، می‌توانیم به یک یا چند منطقه از کاربر نگاه کنیم - نقاطی که او از نظر جغرافیایی به آنها نزدیک‌تر است.

چگونه کار می کند؟

ما محبوبیت فایل ها را بر اساس منطقه حساب می کنیم. تعدادی از کش منطقه ای که کاربر در آن قرار دارد و یک شناسه فایل وجود دارد - ما این جفت را می گیریم و رتبه را با هر بار دانلود افزایش می دهیم.

در همان زمان، شیاطین - خدمات در مناطق - هر از گاهی به API می آیند و می گویند: "من فلان کش هستم، لیستی از محبوب ترین فایل های منطقه من را که هنوز در من نیستند به من بدهید. ” API دسته ای از فایل ها را ارائه می دهد که بر اساس رتبه بندی مرتب شده اند، دیمون آنها را دانلود می کند، آنها را به مناطق می برد و فایل ها را از آنجا تحویل می دهد. این تفاوت اساسی بین pu/pp و Sun از کش ها است: آنها بلافاصله فایل را از طریق خودشان می دهند، حتی اگر این فایل در حافظه پنهان نباشد، و کش ابتدا فایل را برای خود دانلود می کند و سپس شروع به پس دادن آن می کند.

در این صورت می گیریم محتوای نزدیک به کاربران و پخش بار شبکه به عنوان مثال، فقط از حافظه پنهان مسکو، ما بیش از 1 ترابیت بر ثانیه در ساعات اوج مصرف توزیع می کنیم.

اما مشکلاتی وجود دارد - سرورهای کش لاستیکی نیستند. برای محتوای فوق العاده محبوب، گاهی اوقات شبکه کافی برای یک سرور جداگانه وجود ندارد. سرورهای کش ما 40-50 گیگابیت بر ثانیه هستند، اما محتوایی وجود دارد که چنین کانالی را کاملاً مسدود می کند. ما به سمت اجرای ذخیره سازی بیش از یک نسخه از فایل های محبوب در منطقه حرکت می کنیم. امیدوارم تا پایان سال آن را اجرایی کنیم.

ما به معماری کلی نگاه کردیم.

  • سرورهای جلویی که درخواست ها را می پذیرند.
  • پشتیبان هایی که درخواست ها را پردازش می کنند.
  • ذخیره سازی هایی که توسط دو نوع پراکسی بسته می شوند.
  • کش های منطقه ای

چه چیزی از این نمودار کم است؟ البته پایگاه هایی که داده ها را در آنها ذخیره می کنیم.

پایگاه های داده یا موتورها

ما آنها را نه پایگاه داده، بلکه موتورها - موتورها می نامیم، زیرا ما عملا پایگاه داده به معنای عمومی پذیرفته شده نداریم.

سوالات متداول در مورد معماری و کار VKontakte

این یک اقدام ضروری است.. این اتفاق به این دلیل رخ داد که در سال‌های 2008-2009، زمانی که VK رشد انفجاری در محبوبیت داشت، این پروژه به طور کامل روی MySQL و Memcache کار می‌کرد و مشکلاتی وجود داشت. MySQL عاشق خراب کردن و خراب کردن فایل‌ها بود، پس از آن بازیابی نمی‌شد، و Memcache به تدریج از نظر عملکرد ضعیف شد و مجبور شد دوباره راه‌اندازی شود.

معلوم شد که پروژه‌ای که به‌طور فزاینده‌ای محبوب می‌شود، دارای ذخیره‌سازی دائمی است که داده‌ها را خراب می‌کند، و یک حافظه پنهان که سرعت آن کاهش می‌یابد. در چنین شرایطی، توسعه یک پروژه رو به رشد دشوار است. تصمیم گرفته شد تا موارد مهمی را که پروژه بر روی دوچرخه های خودمان متمرکز شده بود بازنویسی کنیم.

راه حل موفقیت آمیز بود. فرصتی برای انجام این کار و همچنین یک ضرورت شدید وجود داشت، زیرا روش‌های دیگری برای مقیاس‌بندی در آن زمان وجود نداشت. تعداد زیادی پایگاه داده وجود نداشت، NoSQL هنوز وجود نداشت، فقط MySQL، Memcache، PostrgreSQL وجود داشت - و بس.

عملیات جهانی. توسعه توسط تیم ما از توسعه دهندگان C هدایت شد و همه چیز به روشی ثابت انجام شد. صرف نظر از موتور، همه آنها تقریباً فرمت فایل یکسانی داشتند که روی دیسک نوشته شده بود، پارامترهای راه‌اندازی یکسانی داشتند، سیگنال‌ها را به روشی مشابه پردازش می‌کردند و در موارد و موقعیت‌های لبه تقریباً یکسان رفتار می‌کردند. با رشد موتورها، کارکرد سیستم برای مدیران راحت است - هیچ باغ وحشی وجود ندارد که نیاز به نگهداری داشته باشد، و آنها باید نحوه کار با هر پایگاه داده شخص ثالث جدید را یاد بگیرند، که امکان افزایش سریع و راحت را فراهم می کند. تعداد آنها

انواع موتور

تیم تعداد زیادی موتور نوشت. در اینجا فقط تعدادی از آنها وجود دارد: دوست، نکات، تصویر، ipdb، حروف، لیست‌ها، گزارش‌ها، memcached، meowdb، اخبار، نوستراداموس، عکس، لیست‌های پخش، pmemcached، جعبه ایمنی، جستجو، ذخیره‌سازی، لایک‌ها، وظایف، …

برای هر وظیفه ای که به ساختار داده خاصی نیاز دارد یا درخواست های غیر معمول را پردازش می کند، تیم C یک موتور جدید می نویسد. چرا که نه.

موتور جداگانه داریم memcached، که شبیه معمولی است اما با انبوهی از خوبی ها و سرعتش کم نمی شود. ClickHouse نیست، اما کار می کند. به صورت جداگانه موجود است pmemcached - آیا memcached مداوم، که همچنین می تواند داده ها را روی دیسک ذخیره کند، علاوه بر این، در RAM قرار می گیرد، تا هنگام راه اندازی مجدد، داده ها را از دست ندهند. موتورهای مختلفی برای وظایف فردی وجود دارد: صف ها، لیست ها، مجموعه ها - همه چیزهایی که پروژه ما نیاز دارد.

خوشه ها

از منظر کد، نیازی به در نظر گرفتن موتورها یا پایگاه های داده به عنوان فرآیندها، موجودیت ها یا نمونه ها نیست. کد به طور خاص با خوشه ها، با گروه هایی از موتورها کار می کند - یک نوع در هر خوشه. فرض کنید یک خوشه memcached وجود دارد - این فقط یک گروه از ماشین ها است.

کد به هیچ وجه نیازی به دانستن مکان فیزیکی، اندازه یا تعداد سرورها ندارد. او با استفاده از یک شناسه خاص به خوشه می رود.

برای انجام این کار، باید یک موجودیت دیگر را اضافه کنید که بین کد و موتورها قرار دارد - پروکسی.

پروکسی RPC

پروکسی اتوبوس اتصال، که تقریباً کل سایت روی آن اجرا می شود. در عین حال داریم بدون کشف خدمات - در عوض، یک پیکربندی برای این پراکسی وجود دارد که مکان همه خوشه‌ها و همه خرده‌های این خوشه را می‌داند. این کاری است که ادمین ها انجام می دهند.

برنامه نویسان اصلاً اهمیتی نمی دهند که چقدر، کجا و چه هزینه ای دارد - آنها فقط به کلاستر می روند. این به ما خیلی اجازه می دهد. هنگام دریافت یک درخواست، پروکسی درخواست را تغییر مسیر می دهد، با دانستن اینکه کجا - خودش این را تعیین می کند.

سوالات متداول در مورد معماری و کار VKontakte

در این مورد، پروکسی یک نقطه محافظت در برابر خرابی سرویس است. اگر برخی از موتورها کند شود یا خراب شود، آنگاه پروکسی این را می‌فهمد و به سمت مشتری پاسخ می‌دهد. این به شما امکان می دهد تا زمان را حذف کنید - کد منتظر پاسخ موتور نمی ماند، اما می فهمد که کار نمی کند و باید به نحوی متفاوت رفتار کند. کد باید برای این واقعیت آماده شود که پایگاه داده ها همیشه کار نمی کنند.

پیاده سازی های خاص

گاهی اوقات ما هنوز واقعاً می خواهیم یک نوع راه حل غیر استاندارد را به عنوان موتور داشته باشیم. در همان زمان، تصمیم گرفته شد که از پراکسی rpc آماده خود که به طور خاص برای موتورهای ما ایجاد شده است استفاده نکنیم، بلکه یک پروکسی جداگانه برای این کار ایجاد کنیم.

برای MySQL، که هنوز اینجا و آنجا داریم، از db-proxy استفاده می کنیم و برای ClickHouse - خانه بچه گربه.

به طور کلی اینگونه کار می کند. سرور خاصی وجود دارد، kPHP، Go، Python را اجرا می کند - به طور کلی، هر کدی که بتواند از پروتکل RPC ما استفاده کند. کد به صورت محلی بر روی یک پروکسی RPC اجرا می شود - هر سروری که کد در آن قرار دارد، پروکسی محلی خود را اجرا می کند. در صورت درخواست، پروکسی متوجه می شود که کجا باید برود.

سوالات متداول در مورد معماری و کار VKontakte

اگر یک موتور بخواهد به موتور دیگری برود، حتی اگر همسایه باشد، از طریق یک پروکسی می رود، زیرا ممکن است همسایه در مرکز داده دیگری باشد. موتور نباید به دانستن موقعیت مکانی چیزی غیر از خودش متکی باشد - این راه حل استاندارد ما است. ولی البته استثنا هم داره :)

نمونه ای از طرح TL که طبق آن همه موتورها کار می کنند.

memcache.not_found                                = memcache.Value;
memcache.strvalue	value:string flags:int = memcache.Value;
memcache.addOrIncr key:string flags:int delay:int value:long = memcache.Value;

tasks.task
    fields_mask:#
    flags:int
    tag:%(Vector int)
    data:string
    id:fields_mask.0?long
    retries:fields_mask.1?int
    scheduled_time:fields_mask.2?int
    deadline:fields_mask.3?int
    = tasks.Task;
 
tasks.addTask type_name:string queue_id:%(Vector int) task:%tasks.Task = Long;

این یک پروتکل باینری است که نزدیکترین آنالوگ آن است پروتوباف این طرح زمینه های اختیاری، انواع پیچیده - پسوندهای اسکالرهای داخلی و پرس و جوها را از پیش توصیف می کند. همه چیز طبق این پروتکل کار می کند.

RPC بیش از TL بیش از TCP/UDP... UDP؟

ما یک پروتکل RPC برای اجرای درخواست های موتور داریم که در بالای طرح TL اجرا می شود. این همه از طریق یک اتصال TCP/UDP کار می کند. TCP قابل درک است، اما چرا اغلب به UDP نیاز داریم؟

UDP کمک می کند از مشکل تعداد زیادی اتصال بین سرورها جلوگیری کنید. اگر هر سرور یک پروکسی RPC داشته باشد و به طور کلی بتواند به هر موتوری برود، ده ها هزار اتصال TCP در هر سرور وجود دارد. بار وجود دارد، اما بی فایده است. در مورد UDP این مشکل وجود ندارد.

بدون دست دادن TCP اضافی. این یک مشکل معمولی است: هنگامی که یک موتور جدید یا یک سرور جدید راه اندازی می شود، بسیاری از اتصالات TCP به یکباره برقرار می شوند. برای درخواست های سبک وزن کوچک، به عنوان مثال، بارگذاری UDP، تمام ارتباطات بین کد و موتور وجود دارد دو بسته UDP: یکی در یک جهت پرواز می کند، دومی در جهت دیگر. یک رفت و برگشت - و کد پاسخی از موتور بدون دست دادن دریافت کرد.

بله، همه چیز فقط کار می کند با درصد بسیار کمی از دست دادن بسته. این پروتکل از ارسال مجدد و زمان‌بندی پشتیبانی می‌کند، اما اگر مقدار زیادی از دست بدهیم، تقریباً TCP دریافت می‌کنیم که سودی ندارد. ما UDP را در اقیانوس ها هدایت نمی کنیم.

ما هزاران سرور از این قبیل داریم، و طرح یکسان است: یک بسته موتور روی هر سرور فیزیکی نصب شده است. آنها عمدتا تک رشته ای هستند تا در سریع ترین زمان ممکن بدون مسدود شدن اجرا شوند و به عنوان راه حل های تک رشته ای خرد می شوند. در عین حال، ما هیچ چیز قابل اعتمادتر از این موتورها نداریم و توجه زیادی به ذخیره سازی مداوم داده ها می شود.

ذخیره سازی دائمی داده ها

موتورها بیلوگ می نویسند. Binlog فایلی است که در انتهای آن رویدادی برای تغییر وضعیت یا داده ها اضافه می شود. در راه حل های مختلف به طور متفاوتی نامیده می شود: ورود به سیستم باینری، WAL, AOF، اما اصل یکسان است.

برای جلوگیری از خواندن مجدد موتور برای سالهای طولانی هنگام راه اندازی مجدد، موتورها می نویسند عکس های فوری - وضعیت فعلی. در صورت لزوم ابتدا از روی آن می خوانند و سپس از بینلوگ خواندن را تمام می کنند. همه binlog ها در یک قالب باینری نوشته می شوند - طبق طرح TL، به طوری که مدیران می توانند آنها را به طور مساوی با ابزارهای خود مدیریت کنند. چنین نیازی به عکس های فوری وجود ندارد. یک هدر کلی وجود دارد که نشان می دهد عکس فوری چه کسی int است، جادوی موتور، و کدام بدنه برای کسی مهم نیست. این مشکل از موتوری است که عکس فوری را ضبط کرده است.

من به سرعت اصل عملکرد را شرح خواهم داد. یک سرور وجود دارد که موتور روی آن کار می کند. او یک بیلوگ خالی جدید برای نوشتن باز می کند و یک رویداد برای تغییر در آن می نویسد.

سوالات متداول در مورد معماری و کار VKontakte

در یک نقطه، او یا تصمیم می گیرد خودش یک عکس فوری بگیرد یا سیگنالی دریافت می کند. سرور یک فایل جدید ایجاد می کند، کل وضعیت آن را در آن می نویسد، اندازه binlog فعلی - offset - را به انتهای فایل اضافه می کند و به نوشتن ادامه می دهد. یک بیلوگ جدید ایجاد نشده است.

سوالات متداول در مورد معماری و کار VKontakte

زمانی که موتور دوباره راه اندازی می شود، هم یک binlog و هم یک عکس فوری روی دیسک وجود دارد. موتور کل عکس فوری را می خواند و وضعیت آن را در یک نقطه مشخص بالا می برد.

سوالات متداول در مورد معماری و کار VKontakte

موقعیتی را که در زمان ایجاد عکس فوری بود و اندازه binlog را می خواند.

سوالات متداول در مورد معماری و کار VKontakte

انتهای بیلوگ را می خواند تا به وضعیت فعلی برسد و به نوشتن رویدادهای بعدی ادامه می دهد. این یک طرح ساده است؛ همه موتورهای ما بر اساس آن کار می کنند.

تکثیر داده ها

در نتیجه، تکرار داده ها در ما مبتنی بر بیانیه - ما در باینلاگ هیچ تغییری در صفحه نمی نویسیم، بلکه دقیقاً تغییر درخواست ها. بسیار شبیه به آنچه از طریق شبکه می آید، فقط کمی تغییر یافته است.

از همین طرح نه تنها برای تکرار، بلکه همچنین استفاده می شود برای ایجاد پشتیبان. ما یک موتور داریم - یک استاد نوشتن که در بینلوگ می نویسد. در هر مکان دیگری که مدیران آن را تنظیم کرده اند، این binlog کپی می شود، و تمام - ما یک نسخه پشتیبان داریم.

سوالات متداول در مورد معماری و کار VKontakte

در صورت نیاز ماکت خواندنبرای کاهش بار خواندن CPU، موتور خواندن به سادگی راه اندازی می شود که انتهای binlog را می خواند و این دستورات را به صورت محلی اجرا می کند.

تاخیر در اینجا بسیار کم است و می توان متوجه شد که ماکت چقدر از استاد عقب مانده است.

اشتراک گذاری داده ها در پراکسی RPC

اشتراک گذاری چگونه کار می کند؟ چگونه پراکسی متوجه می‌شود که به کدام قطعه خوشه ارسال کند؟ کد نمی گوید: "ارسال برای 15 قطعه!" - نه، این کار توسط پروکسی انجام می شود.

ساده ترین طرح firstint است - اولین شماره در درخواست

get(photo100_500) => 100 % N.

این نمونه ای برای یک پروتکل متنی ساده memcached است، اما، البته، پرس و جوها می توانند پیچیده و ساختاریافته باشند. مثال، اولین عدد را در پرس و جو و باقیمانده را وقتی بر اندازه خوشه تقسیم می کند، می گیرد.

این زمانی مفید است که بخواهیم محلی بودن داده یک موجودیت واحد را داشته باشیم. فرض کنید 100 یک شناسه کاربر یا گروه است و می‌خواهیم تمام داده‌های یک موجودیت برای پرس‌و‌جوهای پیچیده روی یک قطعه باشد.

اگر ما اهمیتی نمی دهیم که درخواست ها چگونه در خوشه پخش می شوند، گزینه دیگری وجود دارد - هش کردن کل خرده.

hash(photo100_500) => 3539886280 % N

ما همچنین هش، باقیمانده تقسیم و عدد خرده را دریافت می کنیم.

هر دوی این گزینه‌ها فقط در صورتی کار می‌کنند که ما برای این واقعیت آماده باشیم که وقتی اندازه خوشه را افزایش می‌دهیم، آن را چند برابر می‌کنیم یا آن را افزایش می‌دهیم. به عنوان مثال، ما 16 خرده داشتیم، به اندازه کافی نداریم، ما بیشتر می خواهیم - می توانیم با خیال راحت 32 قطعه را بدون خرابی دریافت کنیم. اگر بخواهیم چند برابر را افزایش دهیم، خرابی وجود خواهد داشت، زیرا نمی‌توانیم همه چیز را به‌طور دقیق و بدون ضرر تقسیم کنیم. این گزینه ها مفید هستند، اما نه همیشه.

اگر نیاز به افزودن یا حذف تعداد دلخواه سرور داشته باشیم، استفاده می کنیم هش کردن مداوم روی حلقه a la Ketama. اما در عین حال، ما محلی بودن داده ها را کاملاً از دست می دهیم؛ باید درخواست را در خوشه ادغام کنیم تا هر قطعه پاسخ کوچک خود را برگرداند و سپس پاسخ ها را با پراکسی ادغام کنیم.

درخواست های فوق العاده خاص وجود دارد. به نظر می رسد: پراکسی RPC درخواست را دریافت می کند، تعیین می کند که به کدام خوشه برود و قطعه قطعه را تعیین می کند. سپس یا استادهای نوشتن وجود دارند، یا اگر خوشه پشتیبانی از ماکت داشته باشد، در صورت درخواست به یک نسخه ارسال می کند. این همه توسط پروکسی انجام می شود.

سوالات متداول در مورد معماری و کار VKontakte

سیاهههای مربوط

ما لاگ را به چند روش می نویسیم. واضح ترین و ساده ترین آنها این است نوشتن گزارش در memcache.

ring-buffer: prefix.idx = line

یک پیشوند کلید وجود دارد - نام لاگ، یک خط، و اندازه این گزارش وجود دارد - تعداد خطوط. یک عدد تصادفی از 0 به تعداد خطوط منهای 1 می گیریم. کلید در memcache پیشوندی است که با این عدد تصادفی الحاق شده است. ما خط ورود و زمان فعلی را در مقدار ذخیره می کنیم.

در مواقعی که لازم است لاگ ها را بخوانیم، انجام می دهیم Multi Get همه کلیدها، بر اساس زمان مرتب شده اند، و بنابراین یک ورود به سیستم تولید در زمان واقعی دریافت کنید. این طرح زمانی استفاده می‌شود که شما نیاز دارید چیزی را در زمان واقعی، بدون شکستن چیزی، بدون توقف یا اجازه دادن به ترافیک به ماشین‌های دیگر اشکال‌زدایی کنید، اما این گزارش مدت زیادی دوام نمی‌آورد.

برای ذخیره مطمئن سیاهههای مربوط، ما یک موتور داریم logs- engine. دقیقاً به همین دلیل ایجاد شد و به طور گسترده در تعداد زیادی از خوشه ها استفاده می شود. بزرگترین خوشه ای که من می شناسم 600 ترابایت سیاهههای مربوط را ذخیره می کند.

موتور بسیار قدیمی است، خوشه هایی وجود دارد که قبلاً 6-7 ساله هستند. مشکلاتی با آن وجود دارد که ما سعی در حل آنها داریم، به عنوان مثال، ما شروع به استفاده فعال از ClickHouse برای ذخیره گزارش ها کردیم.

جمع آوری لاگ ها در ClickHouse

این نمودار نشان می دهد که چگونه وارد موتورهای خود می شویم.

سوالات متداول در مورد معماری و کار VKontakte

کدی وجود دارد که به صورت محلی از طریق RPC به پروکسی RPC می رود و می فهمد که کجا باید به موتور برود. اگر بخواهیم لاگ در ClickHouse بنویسیم، باید دو قسمت را در این طرح تغییر دهیم:

  • برخی از موتورها را با ClickHouse جایگزین کنید.
  • پروکسی RPC را که نمی تواند به ClickHouse دسترسی پیدا کند، با راه حلی که می تواند و از طریق RPC جایگزین کنید.

موتور ساده است - ما آن را با یک سرور یا خوشه ای از سرورها با ClickHouse جایگزین می کنیم.

و برای رفتن به ClickHouse، این کار را انجام دادیم خانه بچه گربه. اگر مستقیماً از KittenHouse به ClickHouse برویم، با آن مقابله نمی‌کنیم. حتی بدون درخواست، از اتصالات HTTP تعداد زیادی ماشین جمع می شود. برای اینکه این طرح کار کند، روی سروری با ClickHouse پراکسی معکوس محلی مطرح می شود، که به گونه ای نوشته شده است که بتواند حجم اتصالات مورد نیاز را تحمل کند. همچنین می تواند داده ها را به طور نسبتاً قابل اعتمادی درون خود بافر کند.

سوالات متداول در مورد معماری و کار VKontakte

گاهی اوقات ما نمی خواهیم طرح RPC را در راه حل های غیر استاندارد، به عنوان مثال، در nginx پیاده سازی کنیم. بنابراین KittenHouse قابلیت دریافت لاگ از طریق UDP را دارد.

سوالات متداول در مورد معماری و کار VKontakte

اگر فرستنده و گیرنده گزارش‌ها روی یک دستگاه کار کنند، احتمال از دست دادن یک بسته UDP در میزبان محلی بسیار کم است. به عنوان مصالحه بین نیاز به پیاده سازی RPC در راه حل شخص ثالث و قابلیت اطمینان، ما به سادگی از ارسال UDP استفاده می کنیم. بعداً به این طرح باز خواهیم گشت.

نظارت

ما دو نوع گزارش داریم: آنهایی که توسط مدیران بر روی سرورهایشان جمع آوری می شوند و آنهایی که توسط توسعه دهندگان از روی کد نوشته شده اند. آنها با دو نوع معیار مطابقت دارند: سیستم و محصول.

معیارهای سیستم

روی تمام سرورهای ما کار می کند داده های شبکه، که آمار را جمع آوری و ارسال می کند کربن گرافیت. بنابراین، ClickHouse به عنوان یک سیستم ذخیره سازی استفاده می شود، و نه به عنوان مثال Whisper. در صورت لزوم، می توانید مستقیماً از ClickHouse بخوانید یا استفاده کنید گرافانا برای معیارها، نمودارها و گزارش ها. به عنوان توسعه دهندگان، ما دسترسی کافی به Netdata و Grafana داریم.

معیارهای محصول

برای راحتی کار، چیزهای زیادی نوشته ایم. به عنوان مثال، مجموعه ای از توابع معمولی وجود دارد که به شما امکان می دهد مقادیر Counts، UniqueCounts را در آمار بنویسید، که به جایی دیگر ارسال می شود.

statlogsCountEvent   ( ‘stat_name’,            $key1, $key2, …)
statlogsUniqueCount ( ‘stat_name’, $uid,    $key1, $key2, …)
statlogsValuetEvent  ( ‘stat_name’, $value, $key1, $key2, …)

$stats = statlogsStatData($params)

متعاقباً، می‌توانیم از فیلترهای مرتب‌سازی و گروه‌بندی استفاده کنیم و هر کاری را که از آمار می‌خواهیم انجام دهیم - نمودار بسازیم، Watchdogs را پیکربندی کنیم.

ما خیلی می نویسیم بسیاری از معیارها تعداد رویدادها از 600 میلیارد تا 1 تریلیون در روز است. با این حال، ما می خواهیم آنها را حفظ کنیم حداقل یکی دو سالبرای درک روندهای متریک کنار هم گذاشتن همه اینها مشکل بزرگی است که هنوز آن را حل نکرده ایم. من به شما می گویم که در چند سال گذشته چگونه کار کرده است.

ما توابعی داریم که این معیارها را می نویسند به حافظه پنهان محلیبرای کاهش تعداد ورودی ها یک بار در مدت زمان کوتاهی به صورت محلی راه اندازی شد stats-daemon تمام رکوردها را جمع آوری می کند. سپس، شیطان معیارها را در دو لایه سرور ادغام می کند سیاهههای مربوط به جمع آوری، که آمار یک دسته از ماشین های ما را جمع آوری می کند تا لایه پشت آنها از بین نرود.

سوالات متداول در مورد معماری و کار VKontakte

در صورت لزوم، می‌توانیم مستقیماً برای logs-collectors بنویسیم.

سوالات متداول در مورد معماری و کار VKontakte

اما نوشتن از کد مستقیماً به کلکتورها، دور زدن stas-daemom، راه حل ضعیفی مقیاس پذیر است زیرا بار روی کلکتور را افزایش می دهد. راه حل فقط در صورتی مناسب است که به دلایلی نتوانیم memcache stats-daemon را روی دستگاه افزایش دهیم، یا خراب شد و مستقیماً رفتیم.

در مرحله بعد، logs-collectors آمار را در آن ادغام می کند meowDB - این پایگاه داده ما است که می تواند معیارها را نیز ذخیره کند.

سوالات متداول در مورد معماری و کار VKontakte

سپس می‌توانیم انتخاب‌های باینری «نزدیک به SQL» را از روی کد انجام دهیم.

سوالات متداول در مورد معماری و کار VKontakte

تجربه

در تابستان 2018، ما یک هکاتون داخلی داشتیم، و این ایده مطرح شد که سعی کنیم قسمت قرمز نمودار را با چیزی جایگزین کنیم که بتواند معیارها را در ClickHouse ذخیره کند. ما گزارش هایی در ClickHouse داریم - چرا آن را امتحان نمی کنیم؟

سوالات متداول در مورد معماری و کار VKontakte

ما طرحی داشتیم که از طریق KittenHouse لاگ می نوشت.

سوالات متداول در مورد معماری و کار VKontakte

ما تصمیم گرفتیم یک "*House" دیگر به نمودار اضافه کنید، که دقیقاً معیارها را در قالبی که کد ما آنها را از طریق UDP می نویسد دریافت می کند. سپس این *House آنها را به درج هایی مانند سیاهههای مربوط تبدیل می کند که KittenHouse آنها را درک می کند. او می‌تواند این گزارش‌ها را به‌طور کامل به ClickHouse تحویل دهد، که باید بتواند آنها را بخواند.

سوالات متداول در مورد معماری و کار VKontakte

طرح با پایگاه داده memcache، stats-daemon و logs-collectors با این یکی جایگزین شده است.

سوالات متداول در مورد معماری و کار VKontakte

طرح با پایگاه داده memcache، stats-daemon و logs-collectors با این یکی جایگزین شده است.

  • در اینجا یک کد ارسالی وجود دارد که به صورت محلی در StatsHouse نوشته شده است.
  • StatsHouse معیارهای UDP را که قبلاً به درج‌های SQL تبدیل شده‌اند، به صورت دسته‌ای به KittenHouse می‌نویسد.
  • KittenHouse آنها را به ClickHouse می فرستد.
  • اگر بخواهیم آنها را بخوانیم، آنها را با دور زدن StatsHouse - مستقیماً از ClickHouse با استفاده از SQL معمولی می خوانیم.

آیا هنوز است؟ یک آزمایش، اما ما دوست داریم که چگونه به نظر می رسد. اگر مشکلات طرح را برطرف کنیم، شاید به طور کامل به آن تغییر دهیم. من شخصاً امیدوارم.

طرح آهن را ذخیره نمی کند. به سرورهای کمتری نیاز است، آمارهای محلی و جمع‌آوری‌کننده‌های گزارش مورد نیاز نیست، اما ClickHouse به سرور بزرگ‌تری نسبت به موارد موجود در طرح فعلی نیاز دارد. سرورهای کمتری مورد نیاز است، اما باید گرانتر و قدرتمندتر باشند.

مستقر کنید

ابتدا اجازه دهید به استقرار PHP نگاه کنیم. ما در حال توسعه هستیم دستگاه گوارش: استفاده کنید گیتلب и TeamCity برای استقرار شاخه های توسعه در شاخه اصلی ادغام می شوند، از استاد برای آزمایش در مرحله بندی و از مرحله به مرحله تولید ادغام می شوند.

قبل از استقرار، شاخه تولید فعلی و قبلی گرفته می شود و فایل های متفاوت در آنها در نظر گرفته می شود - تغییرات: ایجاد، حذف، تغییر. این تغییر در بیلوگ یک موتور کپی فست ویژه ثبت شده است که می تواند به سرعت تغییرات را در کل ناوگان سرور ما تکرار کند. آنچه در اینجا استفاده می شود کپی کردن مستقیم نیست، بلکه تکرار شایعات، هنگامی که یک سرور تغییرات را به نزدیکترین همسایگان خود، تغییرات را به همسایگان خود و غیره ارسال می کند. این به شما امکان می دهد کد را در ده ها و واحد ثانیه در کل ناوگان به روز کنید. هنگامی که تغییر به ماکت محلی می رسد، این وصله ها را روی آن اعمال می کند سیستم فایل محلی. برگشت نیز طبق همین طرح انجام می شود.

ما همچنین kPHP را بسیار مستقر می کنیم و همچنین توسعه خاص خود را دارد دستگاه گوارش مطابق نمودار بالا از آنجایی که این سرور HTTP باینری، پس ما نمی توانیم تفاوت تولید کنیم - باینری انتشار صدها مگابایت وزن دارد. بنابراین، گزینه دیگری در اینجا وجود دارد - نسخه به آن نوشته شده است binlog copyfast. با هر ساخت افزایش می یابد و در هنگام بازگشت نیز افزایش می یابد. نسخه روی سرورها کپی شده است. کپی‌فاست‌های محلی می‌بینند که یک نسخه جدید وارد binlog شده است و با همان تکرار شایعات، آخرین نسخه باینری را برای خود می‌گیرند، بدون اینکه سرور اصلی ما را خسته کنند، اما با دقت بار را در سراسر شبکه پخش می‌کنند. آنچه در ادامه می آید راه اندازی مجدد برازنده برای نسخه جدید

برای موتورهای ما که اساساً باینری نیز هستند، این طرح بسیار شبیه است:

  • شاخه اصلی git;
  • باینری در .deb;
  • نسخه به binlog copyfast نوشته شده است.
  • روی سرورها کپی شده است.
  • سرور یک .dep تازه را بیرون می آورد.
  • dpkg -i;
  • راه اندازی مجدد برازنده به نسخه جدید.

تفاوت این است که باینری ما در آرشیو بسته بندی شده است .deb، و هنگام پمپاژ آنها dpkg -i بر روی سیستم قرار می گیرند. چرا kPHP به صورت باینری و موتورها به صورت dpkg مستقر می شوند؟ این طور شد. کار می کند - به آن دست نزنید.

پیوندهای مفید:

الکسی آکولوویچ یکی از کسانی است که به عنوان بخشی از کمیته برنامه کمک می کند PHP روسیه در 17 می، بزرگترین رویداد برای توسعه دهندگان PHP در چند وقت اخیر خواهد بود. ببین چه کامپیوتر باحالی داریم، چه بلندگوها (دو نفر از آنها در حال توسعه هسته PHP هستند!) - به نظر می رسد چیزی است که اگر PHP بنویسید نمی توانید از دست بدهید.

منبع: www.habr.com

اضافه کردن نظر