Інфраструктура обробки черг в соціальній мережі Мій Світ

    
 
Деякий час тому ми розповідали про сервер черг, принципах його роботи і внутрішній устрій. Тепер же, нарешті, прийшов час перейти до розгляду черг з більш продуктової точки зору і розповісти про інфраструктуру, яка застосовується для обробки завдань. Давайте почнемо трохи здалеку, з того, на чому ми зупинилися в минулій статті: для чого, власне, черги можна застосовувати.
 
 Про користь черг Звичайно ж, найпоширеніший спосіб застосування — для асинхронного виконання операцій, породжуваних в процесі обробки запитів користувача, результати виконання яких не потрібні негайно для здійснення зворотного зв'язку. У нашому випадку це, наприклад, відправлення пошти і повідомлень, пропозицій дружби, оновлення пошукових індексів, скидання кешей, постобробка даних, отриманих від користувача (перекодування відео, нарізка фотографій), відправка даних підсистем, час реакції / надійність яких не гарантується.
 
На практиці з'ясувалося, що, володіючи зручною інфраструктурою для розподіленої обробки завдань, спектр застосування істотно вище:
 
     
  • В силу того, що кожна подія в сервері черг володіє часом активації, можна використовувати черги в якості планувальника відкладених завдань . Таким чином, наприклад, у нас зроблені відкладені пости в групах. Замість публікації в стрічці групи пост укладається у тимчасове сховище, а в черзі створюється завдання з часом активації, рівним часу публікації поста.
  •  
  • Використовуючи час активації, можна розмазувати навантаження на бази даних у часі , відкладаючи виконання деяких непріоритетних завдань з пікового часу на нічне.
  •  
  • Можна за допомогою черг ротувати контент , що відображається групі користувачів. У нас застосовується для соціальної модерації фотографій. У спеціальну чергу складаються зображення, які необхідно отмодеріровать, а вебсервер при віддачі контенту користувачеві бере одну фотографію з черги і показує модератору.
  •  
  • Черга може вбудовуватися в якості прошарку в API прийому даних, наприклад, push-нотифікацій від колег з сусіднього проекту, для підвищення надійності і згладжування навантаження.
  •  
  • Для організації отримання даних від зовнішніх джерел , наприклад, шляхом скачування деякого контенту зі стороннього ресурсу.
  •  
  • Також можна здійснювати примусову синхронізацію даних з дружніми проектами шляхом обходу нашої бази і відправки безлічі запитів до їх API. Здійснювати це можна періодично і протягом тривалого часу, регулюючи навантаження на API стороннього проекту за допомогою обмеження кількості обробників або за допомогою розмазування часу активації подій.
  •  
  • Аналогічним же чином можна здійснювати обхід власних сховищ з метою виправлення даних, побитих внаслідок логічних помилок в коді, або з метою оновлення структури збережених даних в новий формат.
  •  
  • Дуже зручно за допомогою черг здійснювати модифікації розподілено зберігаються і пов'язаних даних . Для прикладу можна взяти таку операцію як видалення поста, яка також вимагає видалення всіх коментарів і лайків. Відповідно, розбити її можна на три окремих черги: одна відповідає за видалення поста, інша за видалення коментарів, третя за видалення лайків. Таким чином, отримуємо, що швидкість розгрібання кожної черги залежить тільки від стану конкретного сховища і, наприклад, проблеми зі сховищем лайків не вплинуть на видалення власне постів.
  •  
 Інфраструктура обробки Варто визнати, що деякий час ми намагалися обробляти завдання з черг без створення для цього спеціалізованій інфраструктури. У міру збільшення кількості типів черг ставало все більш очевидним, що встежити за зростаючою кількістю скриптик і демоночков, вельми схожих, але трохи різних, стає все складніше. Потрібно стежити за навантаженням і запускати достатню кількість процесів, не забувати моніторити їх, при випаданні серверів своєчасно порушувати процеси на інших машинах. З'явилося розуміння, що потрібна інфраструктура, яку можна сказати: «ось тобі кластер серверів-обробників, ось тобі сервери черг, давай розгрібай». Тому ми трохи подумали, подивилися, чи є готові рішення, взяли Perl (тому що це один з основних мов, поряд з C, на якому ми розробляємо) і понеслася.
 
При проектуванні інфраструктури для обробки черг ключовими були наступні характеристики:
 
     
  • розподіленість — завдання одного типу можуть виконуватися на різних серверах;
  •  
  • відмовостійкість — випадання десяти-двадцяти відсотків серверів кластера не повинно призводити до деградації важливих черг;
  •  
  • гомогенність — відсутність будь-якої спеціалізації серверів, будь-яке завдання може виконуватися на будь-якому сервері;
  •  
  • пріоритизація — є критичні черги, а є ті, які можуть і почекати;
  •  
  • автоматизація — мінімум ручного втручання (особливо адмінській): балансування — перерозподіл обробників кожного типу в залежності від потреб; адаптація до сервера — визначення кількості обробників на кожному сервері в залежності від його характеристик, відстеження споживання пам'яті і процесора;
  •  
  • пакетна обробка — завдання одного типу повинні групуватися для можливості застосування оптимізацій;
  •  
  • статистика — збір даних про поведінку черг, потоці завдань, кількості, наявності помилок;
  •  
  • моніторинг — оповіщення про нештатні ситуації та зміні стандартної поведінки черг.
  •  
Схему ми при цьому застосували досить класичну, виділивши три компоненти: менеджер, майстер і слот.
 
 
 
 Слот — це власне процес-обробник черги (Воркер). Слотів на кожному сервері може бути запущено безліч, кожен слот характеризується типом черги, яку він в даний момент обробляє. Спеціалізація слотів на чергах певних типів підвищує локальність даних, з'єднань до баз даних і зовнішніх сервісів, спрощує налагодження (в тому числі на випадок псування пам'яті процесу і освіти «корок»).
 
 Майстер — це процес-бригадир, відповідальний за роботу слотів на певному сервері. У його обов'язки входить запуск і зупинка слотів, підтримання оптимальної кількості слотів на сервері в залежності від його конфігурації, доступної пам'яті і LA, моніторинг життєдіяльності слотів, знищення завислих і що використали занадто багато системних ресурсів слотів.
 
 Менеджер — це процес-керівник кластеру, запускається в одному примірнику (під pacemaker'ом для надійності) і відповідає за весь кластер. Займається мікроменеджментом: на основі даних від сервера черг і статистичних даних від майстрів визначає необхідну кількість слотів кожного типу і вирішує, який конкретний слот буде займатися завданнями якого типу.
 
 Управління кластером Визначення необхідної кількості слотів для обробки кожного типу черги менеджером здійснюється на базі:
 
     
  • конфігураційних параметрів, в тому числі: пріоритет черзі; мінімальне і максимальне допустиму кількість обробників; кількість завдань, оброблюваних за одну ітерацію;
  •  
  • статистичних даних за останній час, в тому числі: кількість активних завдань у черзі; кількість оброблених завдань; сумарний час обробки завдань; сумарний час життя обробників.
  •  
При цьому для виключення мигання слотів навмисно допускається певна неточність: якщо необхідну кількість слотів незначно відрізняється від поточного, то переконфігурації не відбувається («незначність» відхилення вгору і вниз визначається як функція від пріоритету черги). З цією ж метою складання нової карти слотів завжди здійснюється на підставі поточної. Це призводить до того, що слоти стабільно займаються обробкою одного типу завдань. Також при розподілі слотів по серверам менеджер намагається не допустити скупчування обробників одного типу і розмазує їх по кластеру.
 
Слід визнати, що підібрати формулу, добре балансуючу кількість слотів кожного типу, нам вдалося не з першого разу — спочатку намагалися обійтися меншою кількістю параметрів і коефіцієнтів. У процесі підбору формули намалювали адміночку менеджера, за якою можна проаналізувати правильність його дій, а також подивитися яким чином слоти певних типів розподіляються по серверах.
 
 
 
До речі, на цій картинці видна пара забавних ефектів, наприклад, що кількість слотів (у тому числі середнє) може бути менше мінімального. Це означає, що час життя слота не дуже велике, швидше за все в силу інтенсивного використання пам'яті, і вони часто перезапускати. Або те, що завантаження може бути сильно більше 100%, так як вона враховує не тільки обробляти завдання, а й ті, які накопичуються в черзі. Саме на основі цього показника менеджер визначає, що кількість слотів треба зменшувати або збільшувати (підсвічується червоним). Для деяких типів черг ми свідомо завищуємо мінімальна кількість слотів цього типу. Це необхідно, щоб забезпечити для цих черг мінімальний час реакції на подію і бути впевненими, що як тільки воно потрапить в чергу, то моментально піде в обробку, не чекаючи виконання попередніх завдань.
 
 
 
 Місцеве самоврядування Сервера, на яких запускаються майстра, різні за потужністю: частина старі і так собі, частина нові і цілком нічого. Тому необхідно автоматично, без налаштувань з боку адмінів, визначати доступні ресурси, поточні апетити слотів і підлаштовувати їх кількість. У нашому випадку з'ясувалося, що найцінніший ресурс — оперативна пам'ять. Саме вона визначає, скільки слотів можна запустити на сервері. Зрозуміло, для її економії ми постаралися максимально використовувати copy-on-write і завантажити весь необхідний для виконання слотів код в майстрові з тим, щоб після форка слотів вони всю цю пам'ять поділяли. Для того щоб підрахувати допустима кількість слотів на одному сервері, стандартних VSZ і RSS недостатньо — вони не містять інформації про те наскільки пам'ять процесів розшарена між процесам. На щастя, в linux з деяких пір є чудовий параметр PSS (Proportional Set Size), який для кожної сторінки пам'яті обернено пропорційний кількості процесів, між якими вона понишпорив (віднімати і вирахувати PSS процесу можна з / proc / $ pid / smaps). Сума PSS майстра і всіх слотів, розділена на кількість слотів, приблизно відповідає середнім розміром слота (трохи більше, так як на майстер спеціально не ділимо). Додавши суму PSS до вільної пам'яті і розділивши на середній розмір слота, можна отримати їх допустиму кількість (мінус деякий запас на всякий випадок).
 
У процесі роботи слоти постійно взаємодіють з майстром, отримуючи від нього команди на зміну параметрів слота і передаючи йому статистичну інформацію, яка згодом використовується менеджером при плануванні. Слот періодично підраховує кількість пам'яті, якою він володіє ексклюзивно (Private_Dirty), і, якщо воно перевищує заданий ліміт, то завершує свою роботу. Згодом такий слот перезапускається майстром з чистого аркуша. Це дозволяє економити пам'ять і не дає окремим слотам порушувати функціонування сервера в цілому.
 
 Алгоритм обробки завдань
Власне процес обробки завдань побудований за досить простому циклічного алгоритму. Слот постійно посилає серверу черг запит на одержання чергової пачки завдань. У разі наявності подій вони обробляються. При цьому по можливості всі події з пачки обробляються спільно. Це дозволяє зменшити кількість запитів до баз даних за рахунок угруповання однотипних і посилки мультізапросов. Або, в разі відсутності підтримки мультізапросов з боку сховищ, прискорити спілкування з ними за рахунок параллелизации асинхронних запитів шляхом застосування libev або libcoro. Логічним рішенням звичайно ж було б взагалі використовувати асинхронну обробку завдань у рамках event-машини. Але на практиці таке рішення сопряженно зі складнощами при розробці (необхідність виконання одного і того ж коду-під синхронного веб-сервера і асинхронного обробника черги) і налагодженні.
 
Обробка помилок побудована максимально простим способом і використовує особливість сервера черг, який при видачі завдань оброблювачу лочіт їх на деякий час (як правило, на дві години, але це налаштовується для кожної черги). Всі непередбачені розробником ситуації просто призводять до того, що завдання залишається залоченним в черзі протягом двох годин і після цього підхоплюється іншим обробником. Такий підхід дозволяє однотипним чином реагувати як на проблеми в логіці обробки, так і на помилки в низкоуровневом коді, які можуть призводити до падіння обробника в «кірку». Більш-менш штатні ситуації при цьому можна обробляти і більш розумним способом, наприклад, за допомогою відкладання часу активації події на пізніший час. У будь-якому випадку подія вважається обробленим тільки тоді, коли обробник пошле серверу черг команду на видалення.
 
Весь процес обробки завдань акуратно обкладений відправкою статистичної інформації, малюванням різноманітних графіків і моніторингом. В якості первинного моніторингу використовується інформація про кількість завдань у черзі і про те, скільки з них залочений. Збільшення кількості залоченних подій явно говорить про те, що в черзі накопичуються події, які не можуть бути оброблені через наявність фатальних помилок. Зростання кількості активних завдань говорить про те, що вступник потік не встигає оброблятися.
 
 Висновок На закінчення хочеться відзначити, що описувана інфраструктура використовується в Моєму Світі не перший пару років і не вимагала якогось істотного втручання або модифікації за цей час, навіть незважаючи на те, що відчутно зросли і кількість черг, і кількість використовуваних серверів. За цей час нам вдалося практично звести нанівець зоопарк скриптик і демоночков, перевівши їх на інфраструктуру менеджера черг. Її зручність призвело до того, що нові черги з'являються в проекті не рідше, ніж нові ajax-функції. І на поточний момент на кластері з 40 серверів (близько 3500 обробників) за добу обробляється порядку 350 мільйонів завдань 150 різних типів.
 
P.S. Зрозуміло, зараз вже з'явилися такі готові рішення, як gearman , і якщо є потреба в розподіленої обробці завдань, то має сенс дивитися в їхній бік. Але нам пощастило потурбуватися цим завданням трохи раніше, і ми отримали масу задоволення в процесі розробки власного рішення.
    
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.