Пропоную вашій увазі переведення гостьового поста з блогу Go від імені Yang Zhou, що в даний момент займає позицію інженера в Qihoo 360.
Qihoo 360 є лідируючим постачальником антивірусних продуктів для інтернету і мобільних пристроїв в Китаї, контролює велику платформу дистриб'юції мобільних додатків для Android (магазин додатків). На кінець червня 2014 року Qihoo користувалися 500 мільйонів активних користувачів ПК на місяць і понад 640 мільйонів користувачів мобільних пристроїв. У Qihoo також є свій браузер і пошуковий движок, обидва не менш популярні серед китайців.
Моя команда, відділ push-повідомлень, надає фундаментальний сервіс обміну повідомленнями для більш ніж 50 додатків серед продуктів компанії (як для ПК, так і для мобільних пристроїв), а також тисячам сторонніх додатків, що використовують нашу відкриту платформу.
Наш «роман» з Go бере свій початок у 2012 році, коли ми намагалися налагодити роботу push. Найперший варіант представляв з себе зв'язку nginx + lua + redis, але продуктивність під серйозним навантаженням не підійшла під наші вимоги. Під час чергового пошуку правильного стека нашу увагу привернув свіжий реліз Go 1.0.3. Ми виготовили прототип буквально за кілька тижнів, багато в чому завдяки горутинам (легковагові потоки) і го-каналам (типізовані черги), які є примітивами мови.
Спочатку наша гофернута система була розгорнута на 20 серверах, обслуговуючи загалом 20 мільйонів активних сполук. Вона справлялася з відправкою всього 2 мільйонів повідомлень на день. Зараз система розгорнута на 400 серверах, підтримує 200 + мільйонів активних з'єднань і забезпечує відправку більш ніж 10 мільярдів повідомлень щодня.
Паралельно зі стрімким зростанням бізнесу і підвищенням вимог до сервісу push-повідомлень початкова система на Go швидко вперлася в ліміти: розмір купи досягав 69G, паузи GC становили по 3-6 секунд. Більш того, ми перезавантажували наші сервери щотижня для звільнення пам'яті. Якщо чесно, думали навіть позбутися Go і переписати все ядро на C. Однак, незабаром плани змінилися: затик стався під час перенесення бізнес логіки. Одній людині (мені) неможливо було подужати підтримку системи на Go, паралельно забезпечуючи портування бізнес логіки на C.
Так що я прийняв рішення залишитися з Go (і на мій погляд - це наймудріше з усіх, що я міг тоді прийняти) і якісний прогрес прийшов досить скоро.
Ось кілька прийомів і оптимізацій:
- Повторно використовувати TCP з "єднання (пул з" єднань), щоб уникнути створення нових об "єктів та буферів під час взаємодії;
- Повторно використовувати об'єкти і виділену пам'ять, для зменшення навантаження на GC;
- Використовувати групи горутин для обробки черг завдань або повідомлень, отриманих від
горутин підключень. Тобто. класичний pub/sub замість породження безлічі горутин, по одній на кожен вхідний запит;
- Моніторити і контролювати кількість горутин у процесі. Відсутність контролю може призвести до появи непосильного навантаження на GC через стрибок у кількості створюваних горутин. Наприклад, якщо допустити безконтрольне створення горутин для обробки запитів ззовні, то блокування тільки що створених внаслідок звернення до внутрішніх сервісів, наприклад, призведе до створення нових горутин і так далі;
- Не забудьте вказати дедлайни на читання і на запис для мережевих з'єднань при роботі з мобільною мережею, інакше горутини можуть заблокуватися. Однак, застосовуйте їх обережно з LAN мережею, інакше ефективність RPC-взаємодії може постраждати;
- Використовувати при необхідності Pipelining для викликів (при доступності Full Duplex для TCP).
У результаті вийшло три ітерації нашої архітектури, дві ітерації RPC фреймворку, навіть з обмеженою кількістю людей для реалізації. Я б відніс це досягнення до зручності розробки на Go. Наводжу свіжу діаграму нашої архітектури:
Результат безперервних поліпшень і доробок у вигляді таблиці:
І, що не менш крутіше, заодно ми розробили платформу для профілювання Go програм в реальному часі (Visibility Platform). Тепер ми маємо доступ до статусу системи та діагностичної інформації, передбачаючи будь-які неполадки.
Скріншоти:
З чудового: з цим інструментом ми можемо симулювати підключення і поведінку мільйонів користувачів, використовуючи модуль Distributed Stress Test Tool (також написаний на Go), спостерігати за результатами в реальному часі і з візуалізацією. Це дозволяє нам вивчати ефективність будь-якого нововведення або оптимізації, запобігаючи будь-яким проблемам з продуктивністю в бойових умовах. Практично будь-яка можлива оптимізація системи вже була випробувана нами. І ми з нетерпінням чекаємо хороших новин від команди, що відповідає за GC в Go, вони могли б позбавити нас від зайвої роботи з оптимізацією коду під GC в подальшому. Я також допускаю, що наші хитрощі скоро стануть просто рудиментарними, оскільки Go продовжує розвиватися.
Я хочу завершити цю історію подякою за можливість брати участь в Gopher China. Це був урочистий захід, що дозволив нам дізнатися багато, поділитися своїми знаннями, перейнятися популярністю і успіхом Go в Китаї. Велика кількість команд в Qihoo вже встигли ознайомитися з Go і навіть спробувати. Я переконаний, що скоро ще більше китайських інтернет-компаній приєднаються до тренду і перепишуть свої системи на Go, таким чином зусилля команди, що стоїть за Go, принесуть користь величезній кількості розробників і компаній в найближчому майбутньому.
