Често задавани въпроси за архитектурата и работата на VKontakte

Историята на създаването на VKontakte е в Wikipedia, разказана е от самия Павел. Изглежда вече всички я познават. За вътрешността, архитектурата и структурата на сайта на HighLoad++ Павел ми каза през 2010 г. Оттогава са изтекли много сървъри, така че ние ще актуализираме информацията: ще я направим дисекция, ще извадим вътрешностите, ще я претеглим и ще разгледаме VK устройството от техническа гледна точка.

Често задавани въпроси за архитектурата и работата на VKontakte

Алексей Акулович (AterCattus) backend разработчик в екипа на VKontakte. Преписът на този доклад е колективен отговор на често задавани въпроси относно работата на платформата, инфраструктурата, сървърите и взаимодействието между тях, но не и относно развитието, а именно относно желязото. Отделно за базите данни и какво има VK вместо това, за събирането на регистрационни файлове и наблюдението на целия проект като цяло. Детайли под кройката.



Повече от четири години се занимавам с всякакви задачи, свързани с бекенда.

  • Качване, съхранение, обработка, разпространение на медии: видео, стрийминг на живо, аудио, снимки, документи.
  • Инфраструктура, платформа, мониторинг на разработчиците, регистрационни файлове, регионални кешове, CDN, патентован RPC протокол.
  • Интеграция с външни услуги: насочени известия, анализ на външна връзка, RSS емисия.
  • Помага на колеги с различни въпроси, отговорите на които изискват гмуркане в непознат код.

През това време имах пръст в много компоненти на сайта. Искам да споделя този опит.

Обща архитектура

Всичко, както обикновено, започва със сървър или група от сървъри, които приемат заявки.

Преден сървър

Предният сървър приема заявки чрез HTTPS, RTMP и WSS.

HTTPS - това са заявки за основната и мобилната уеб версия на сайта: vk.com и m.vk.com, както и други официални и неофициални клиенти на нашия API: мобилни клиенти, месинджъри. Имаме рецепция RTMP-трафик за предавания на живо с отделни предни сървъри и WSS- връзки за Streaming API.

За HTTPS и WSS на сървъри си струва Nginx. За RTMP излъчвания наскоро преминахме към нашето собствено решение киве, но е извън обхвата на доклада. За устойчивост на грешки тези сървъри рекламират общи IP адреси и действат в групи, така че ако има проблем на един от сървърите, потребителските заявки да не се губят. За HTTPS и WSS същите тези сървъри криптират трафика, за да поемат част от натоварването на процесора върху себе си.

Няма да говорим повече за WSS и RTMP, а само за стандартните HTTPS заявки, които обикновено се свързват с уеб проект.

бекенда

Зад предната част обикновено има backend сървъри. Те обработват заявки, които предният сървър получава от клиенти.

То kPHP сървъри, на който работи HTTP демонът, тъй като HTTPS вече е дешифриран. kPHP е сървър, който работи на prefork модели: стартира главен процес, куп дъщерни процеси, предава слушащи сокети към тях и те обработват техните заявки. В този случай процесите не се рестартират между всяка заявка от потребителя, а просто нулират състоянието си до първоначалното състояние с нулева стойност - заявка след заявка, вместо рестартиране.

Разпределение на товара

Всички наши бекендове не са огромен набор от машини, които могат да обработват всяка заявка. Ние тях разделени на отделни групи: общ, mobile, api, video, staging... Проблемът на отделна група машини няма да засегне всички останали. В случай на проблеми с видеото, потребителят, който слуша музика, дори няма да разбере за проблемите. До кой бекенд да се изпрати заявката се решава от nginx отпред според конфигурацията.

Събиране на показатели и повторно балансиране

За да разберем колко коли трябва да имаме във всяка група, ние не разчитайте на QPS. Бекендовете са различни, имат различни заявки, всяка заявка има различна сложност на изчисляване на 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 смятам, че това вече не е толкова актуално.

Очевидният недостатък на схемата е, че трябва помпа целия трафик, който отива в хранилището, през друг сървър. Тъй като изпомпваме трафик през машини, все още не можем да изпомпваме тежък трафик, например видео, използвайки същата схема. Ние го предаваме директно - отделна директна връзка за отделни хранилища специално за видео. Ние предаваме по-леко съдържание чрез прокси.

Неотдавна получихме подобрена версия на проксито. Сега ще ви кажа как се различават от обикновените и защо е необходимо това.

Sun

През септември 2017 г. Oracle, която преди това беше купила Sun, уволни огромен брой служители на Sun. Можем да кажем, че в този момент компанията престана да съществува. При избора на име за новата система, нашите администратори решиха да отдадат дължимото на паметта на тази компания и кръстиха новата система Sun. Помежду си я наричаме просто „слънца“.

Често задавани въпроси за архитектурата и работата на VKontakte

pp имаше няколко проблема. Едно IP на група - неефективен кеш. Няколко физически сървъра споделят общ IP адрес и няма начин да контролирате към кой сървър ще отиде заявката. Следователно, ако различни потребители дойдат за един и същ файл, тогава ако има кеш на тези сървъри, файлът се озовава в кеша на всеки сървър. Това е много неефективна схема, но нищо не може да се направи.

Следователно - не можем да разделяме съдържание, тъй като не можем да изберем конкретен сървър за тази група - те имат общ IP. Също така поради някои вътрешни причини, които имаме не беше възможно да се инсталират такива сървъри в региони. Те стояха само в Санкт Петербург.

Със слънцата променихме системата за подбор. Сега имаме anycast маршрутизиране: динамично маршрутизиране, произволно предаване, демон за самопроверка. Всеки сървър има свой индивидуален IP, но обща подмрежа. Всичко е конфигурирано по такъв начин, че ако един сървър се повреди, трафикът се разпределя автоматично между другите сървъри от същата група. Сега е възможно да изберете конкретен сървър, без излишно кеширанеи надеждността не е засегната.

Поддръжка на тежестта. Сега можем да си позволим да инсталираме машини с различна мощност според нуждите, а също така, в случай на временни проблеми, да променим теглото на работещите „слънца“, за да намалим натоварването върху тях, така че те да „почиват“ и да започнат да работят отново.

Шардинг по идентификатор на съдържанието. Странно нещо за шардинга: обикновено разделяме съдържание, така че различните потребители да отиват до един и същ файл през едно и също „слънце“, така че да имат общ кеш.

Наскоро стартирахме приложението „Детелина“. Това е онлайн викторина в предаване на живо, където водещият задава въпроси, а потребителите отговарят в реално време, като избират опции. Приложението има чат, където потребителите могат да чатят. Може едновременно да се свърже с излъчването повече от 100 хиляди души. Всички те пишат съобщения, които се изпращат до всички участници, а аватар идва заедно със съобщението. Ако 100 хиляди души дойдат за един аватар в едно „слънце“, тогава понякога може да се търкаля зад облак.

За да издържим на изблици на заявки за един и същ файл, за определен тип съдържание включваме глупава схема, която разпространява файлове във всички налични „слънца“ в региона.

Слънце отвътре

Обратно прокси на nginx, кеш или в RAM, или на бързи дискове Optane/NVMe. Пример: http://sun4-2.userapi.com/c100500/path — връзка към „слънцето“, което се намира в четвъртия регион, втората сървърна група. Той затваря файла с пътя, който физически се намира на сървър 100500.

Кеш

Добавяме още един възел към нашата архитектурна схема - кеширащата среда.

Често задавани въпроси за архитектурата и работата на VKontakte

По-долу е диаграмата на оформлението регионални кешове, има около 20 от тях. Това са местата, където се намират кешове и „слънца“, които могат да кешират трафика през себе си.

Често задавани въпроси за архитектурата и работата на VKontakte

Това е кеширане на мултимедийно съдържание; тук не се съхраняват потребителски данни - само музика, видео, снимки.

За да определим региона на потребителя, ние събираме BGP мрежови префикси, обявени в регионите. В случай на резервен вариант, ние също трябва да анализираме базата данни geoip, ако не можем да намерим IP по префикси. Ние определяме региона по IP на потребителя. В кода можем да разгледаме един или повече региони на потребителя - онези точки, до които той е най-близо географски.

Как действа тя?

Отчитаме популярността на файловете по региони. Има номер на регионалния кеш, където се намира потребителят, и идентификатор на файл - ние вземаме тази двойка и увеличаваме оценката с всяко изтегляне.

В същото време демони - услуги в региони - от време на време идват в API и казват: „Аз съм такъв и такъв кеш, дайте ми списък с най-популярните файлове в моя регион, които все още не са на мен. ” API доставя куп файлове, сортирани по рейтинг, демонът ги изтегля, отвежда ги в регионите и доставя файловете оттам. Това е основната разлика между pu/pp и Sun от кеша: те предават файла през себе си веднага, дори ако този файл не е в кеша, а кешът първо изтегля файла в себе си и след това започва да го връща.

В този случай получаваме съдържание по-близо до потребителите и разпределяне на мрежовото натоварване. Например, само от московския кеш ние разпространяваме повече от 1 Tbit/s в пиковите часове.

Но има проблеми - кеш сървърите не са гумени. За супер популярно съдържание понякога няма достатъчно мрежа за отделен сървър. Нашите кеш сървъри са 40-50 Gbit/s, но има съдържание, което напълно задръства такъв канал. Преминаваме към внедряване на съхранение на повече от едно копие на популярни файлове в региона. Надявам се до края на годината да го реализираме.

Разгледахме общата архитектура.

  • Предни сървъри, които приемат заявки.
  • Бекендове, които обработват заявки.
  • Хранилища, които са затворени от два вида проксита.
  • Регионални кешове.

Какво липсва на тази диаграма? Разбира се, базите данни, в които съхраняваме данни.

Бази данни или двигатели

Ние ги наричаме не бази данни, а двигатели - Двигатели, защото практически нямаме бази данни в общоприетия смисъл.

Често задавани въпроси за архитектурата и работата на VKontakte

Това е необходима мярка. Това се случи, защото през 2008-2009 г., когато VK имаше експлозивен ръст на популярност, проектът работеше изцяло върху MySQL и Memcache и имаше проблеми. MySQL обичаше да се срива и поврежда файлове, след което не се възстановяваше, а Memcache постепенно влошаваше производителността си и трябваше да се рестартира.

Оказва се, че все по-популярният проект е имал постоянно съхранение, което поврежда данните, и кеш, който забавя. В такива условия е трудно да се развие разрастващ се проект. Беше решено да се опитаме да пренапишем критичните неща, върху които беше фокусиран проектът, върху нашите собствени велосипеди.

Решението беше успешно. Имаше възможност да се направи това, както и крайна необходимост, тъй като по това време не съществуваха други начини за мащабиране. Нямаше куп бази данни, NoSQL все още не съществуваше, имаше само MySQL, Memcache, PostrgreSQL - и това е всичко.

Универсална операция. Разработката беше ръководена от нашия екип от C разработчици и всичко беше направено по последователен начин. Независимо от двигателя, всички те имаха приблизително един и същ файлов формат, записан на диск, едни и същи параметри за стартиране, обработваха сигнали по един и същи начин и се държаха приблизително еднакво в случай на крайни ситуации и проблеми. С нарастването на двигателите за администраторите е удобно да работят със системата - няма зоопарк, който трябва да се поддържа, и те трябва да се научат отново как да работят с всяка нова база данни на трета страна, което направи възможно бързото и удобно увеличаване броят им.

Видове двигатели

Екипът написа доста двигатели. Ето само някои от тях: приятел, подсказки, изображение, ipdb, писма, списъци, регистрационни файлове, memcached, meowdb, новини, nostradamus, снимка, плейлисти, pmemcached, sandbox, търсене, съхранение, харесвания, задачи, …

За всяка задача, която изисква специфична структура от данни или обработва нетипични заявки, 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 през океаните.

Имаме хиляди такива сървъри и схемата е една и съща: на всеки физически сървър се инсталира пакет от двигатели. Те са предимно еднонишкови, за да работят възможно най-бързо без блокиране, и са разделени като еднонишкови решения. В същото време нямаме нищо по-надеждно от тези двигатели и много внимание се обръща на постоянното съхранение на данни.

Постоянно съхранение на данни

Двигателите пишат binlogs. Бинлогът е файл, в края на който се добавя събитие за промяна на състояние или данни. В различни решения се нарича по различен начин: двоичен дневник, WAL, AOF, но принципът е същият.

За да попречи на двигателя да препрочита целия binlog в продължение на много години при рестартиране, двигателите пишат моментни снимки - текущо състояние. Ако е необходимо, първо четат от него и след това завършват четенето от binlog. Всички binlogs се пишат в един и същ двоичен формат - според TL схемата, така че администраторите да могат да ги администрират еднакво със своите инструменти. Няма такава нужда от моментни снимки. Има обща заглавка, която показва чия моментна снимка е int, магията на двигателя и кое тяло не е важно за никого. Това е проблем с двигателя, който е записал моментната снимка.

Набързо ще опиша принципа на работа. Има сървър, на който работи двигателя. Той отваря нов празен binlog за писане и записва събитие за промяна в него.

Често задавани въпроси за архитектурата и работата на VKontakte

В един момент той или сам решава да направи моментна снимка, или получава сигнал. Сървърът създава нов файл, записва цялото му състояние в него, добавя текущия размер на binlog - отместване - към края на файла и продължава да пише по-нататък. Нов binlog не се създава.

Често задавани въпроси за архитектурата и работата на VKontakte

В даден момент, когато двигателят се рестартира, на диска ще има както binlog, така и моментна снимка. Машината чете цялата моментна снимка и повишава състоянието си в определен момент.

Често задавани въпроси за архитектурата и работата на VKontakte

Чете позицията, която е била по време на създаването на моментната снимка и размера на binlog.

Често задавани въпроси за архитектурата и работата на VKontakte

Прочита края на binlog, за да получи текущото състояние и продължава да записва следващи събития. Това е проста схема, всички наши двигатели работят по нея.

Репликация на данни

В резултат на това репликацията на данни в нашия базирани на изявления — записваме в binlog не някакви промени в страницата, а именно заявки за промяна. Много подобно на това, което идва по мрежата, само леко модифицирано.

Същата схема се използва не само за репликация, но и за създаване на резервни копия. Имаме двигател - пишещ майстор, който пише в binlog. На всяко друго място, където администраторите го настроят, този binlog се копира и това е - имаме резервно копие.

Често задавани въпроси за архитектурата и работата на VKontakte

Ако е необходимо реплика за четенеЗа да се намали натоварването при четене на процесора, машината за четене просто се стартира, която чете края на 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 всички ключове, сортирани по време, и по този начин получавате производствен дневник в реално време. Схемата се използва, когато трябва да дебъгвате нещо в производството в реално време, без да нарушавате нищо, без да спирате или разрешавате трафик към други машини, но този журнал не трае дълго.

За надеждно съхранение на трупи имаме двигател трупи-двигател. Именно затова е създаден и се използва широко в огромен брой клъстери. Най-големият клъстер, който познавам, съхранява 600 TB опаковани регистрационни файлове.

Двигателя е много стар, има клъстери вече на 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 трилион на ден. Ние обаче искаме да ги запазим поне няколко годиниза да разберете тенденциите в показателите. Сглобяването на всичко това е голям проблем, който все още не сме решили. Ще ви кажа как работи през последните няколко години.

Имаме функции, които записват тези показатели към локалния memcacheза да намалите броя на записите. Веднъж за кратък период от време, стартиран локално stats-daemon събира всички записи. След това демонът обединява показателите в два слоя сървъри трупи-събирачи, който събира статистически данни от куп наши машини, така че слоят зад тях да не умре.

Често задавани въпроси за архитектурата и работата на VKontakte

Ако е необходимо, можем да пишем директно на събирачите на логове.

Често задавани въпроси за архитектурата и работата на VKontakte

Но писането от код директно към колектори, заобикаляйки stas-daemom, е лошо мащабируемо решение, защото увеличава натоварването на колектора. Решението е подходящо само ако по някаква причина не можем да повдигнем memcache stats-daemon на машината или тя се срине и отидем директно.

След това събирачите на регистрационни файлове обединяват статистически данни в meowDB - това е нашата база данни, която може да съхранява и показатели.

Често задавани въпроси за архитектурата и работата на VKontakte

След това можем да направим двоични селекции „близо до SQL“ от кода.

Често задавани въпроси за архитектурата и работата на VKontakte

Експеримент

През лятото на 2018 г. имахме вътрешен хакатон и се появи идеята да се опитаме да заменим червената част на диаграмата с нещо, което може да съхранява метрики в ClickHouse. Имаме регистрационни файлове в ClickHouse - защо не опитате?

Често задавани въпроси за архитектурата и работата на VKontakte

Имахме схема, която пишеше логове през KittenHouse.

Често задавани въпроси за архитектурата и работата на VKontakte

Ние решихме добавете още една “*Къща” към диаграмата, който ще получи точно показателите във формата, в който нашият код ги записва чрез UDP. Тогава тази *Къща ги превръща във вложки, като трупи, което 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. Ние се развиваме в Git: използвайте GitLab и TeamCity за разгръщане. Клоновете за разработка се обединяват в главния клон, от главния за тестване те се обединяват в постановка, а от постановка в продукция.

Преди разгръщането се вземат текущият производствен клон и предишният и в тях се разглеждат diff файловете - промени: създадени, изтрити, променени. Тази промяна се записва в binlog на специален copyfast двигател, който може бързо да репликира промените в целия ни сървърен флот. Тук не се използва директно копиране, а репликация на клюки, когато един сървър изпраща промени на най-близките си съседи, тези на своите съседи и т.н. Това ви позволява да актуализирате кода за десетки и единици секунди в целия парк. Когато промяната достигне локалната реплика, тя прилага тези корекции към своята локална файлова система. Връщането също се извършва по същата схема.

Ние също внедряваме kPHP много и той също има собствена разработка Git според диаграмата по-горе. Тъй като това HTTP сървър двоичен, тогава не можем да създадем diff - двоичният файл за освобождаване тежи стотици MB. Следователно тук има още една опция - версията се записва binlog copyfast. С всяко изграждане се увеличава, а по време на връщане също се увеличава. Версия репликирани на сървъри. Местните копифасти виждат, че нова версия е влязла в binlog, и чрез същата репликация на клюки те вземат най-новата версия на двоичния файл за себе си, без да уморяват нашия главен сървър, но внимателно разпределят товара в мрежата. Какво следва грациозно рестартиране за новата версия.

За нашите двигатели, които също са по същество двоични, схемата е много подобна:

  • git главен клон;
  • двоичен в дебютантка;
  • версията е написана на binlog copyfast;
  • репликирани на сървъри;
  • сървърът изважда нов .dep;
  • dpkg -i;
  • грациозно рестартиране до нова версия.

Разликата е, че нашият двоичен файл е пакетиран в архиви дебютантка, а при изпомпване те dpkg -i се поставят в системата. Защо kPHP е разгърнат като двоичен файл, а двигателите са разгърнати като dpkg? Стана така. Работи - не го пипайте.

Полезни връзки:

Алексей Акулович е един от онези, които като част от програмния комитет помагат PHP Русия на 17 май ще се превърне в най-голямото събитие за PHP разработчици в последно време. Вижте какъв страхотен компютър имаме, какво високоговорители (двама от тях разработват PHP ядро!) - изглежда като нещо, което не можете да пропуснете, ако пишете PHP.

Източник: www.habr.com

Добавяне на нов коментар