Олексій Найденов. ITooLabs. Кейс розробки на Go (Golang) телефонної платформи. Частина 1

Олексій Найденов, CEO ITooLabs, Розповідає про розробку телекомунікаційної платформи для операторів зв'язку мовою програмування Go (Golang). Олексій також ділиться досвідом розгортання та експлуатації платформи в одному з найбільших азіатських операторів зв'язку, який використав платформу для надання послуг голосової пошти (VoiceMail) та Віртуальної АТС (Cloud PBX).

Олексій Найденов. ITooLabs. Кейс розробки на Go (Golang) телефонної платформи. Частина 1

Олексій Найденов (далі – АН): - Всім привіт! Мене звуть Олексій Найденов. Я директор компанії ITooLabs. Насамперед я хотів би відповісти, що тут роблю і яким чином тут опинився.

Якщо ви подивіться Bitrix24 Marketplace (розділ «Телефонія»), то 14 додатків та 36, які там є (40%) – це ми:

Олексій Найденов. ITooLabs. Кейс розробки на Go (Golang) телефонної платформи. Частина 1

Точніше сказати, це наші партнери-оператори, але за цим стоїть наша платформа (Platform as a Service) - те, що ми їм продаємо за невелику копійчину. Власне, про розвиток цієї платформи та про те, як ми прийшли до Go, я б і хотів розповісти.

Цифри нашої платформи зараз:

Олексій Найденов. ITooLabs. Кейс розробки на Go (Golang) телефонної платформи. Частина 1

44 партнери-оператори, включаючи «Мегафон». Ми, взагалі кажучи, дуже любимо пускатися в різні авантюри, і ми маємо фактичний доступ до 100 мільйонів абонентів 44 операторів тут, в Росії. Тому, якщо в когось з'являтимуться якісь бізнес-ідеї, ми завжди їх з радістю вислухаємо.

  • 5000 компаній-користувачів.
  • 20 000 абонентів у сумі. Це все b2b – ми працюємо лише з компаніями.
  • 300 дзвінків за хвилину вдень.
  • 100 мільйонів хвилин викликів за минулий рік (ми це відсвяткували). Це без урахування внутрішніх переговорів, які є на нашій платформі.

Як це починалося?

Як взагалі правильні чуваки починають робити свою платформу? Треба ще врахувати, що у нас в анамнезі була «хардкор-інтерпрайз» розробка, та ще й у найточнішу для enterprise пору року! Це був той щасливий час, коли приходиш до замовника і кажеш: «Нам потрібна ще пара серверів». А замовник: «Та не питання! У нас десятка у стійці стоїть».

Тому ми займалися Oracle, Java, WebSphere, Db2 і таке інше. Тому ми взяли, звичайно, найкращі вендорські рішення, інтегрували їх та спробували з цим злетіти. Гуляли на свої. Це б такий внутрішній стартап.

Все це взагалі розпочалося у 2009 році. З 2006-го ми щільно займаємося операторськими рішеннями так чи інакше. Зробили кілька замовних віртуальних АТС (на зразок того, що у нас зараз є на замовлення): подивилися, вирішили, що це добре, і вирішили зробити внутрішній стартап.

Олексій Найденов. ITooLabs. Кейс розробки на Go (Golang) телефонної платформи. Частина 1

Взяли VMWare. Оскільки гуляли на свої, то довелося одразу відмовитись від крутого вендорського Storage. Ми все про них знаємо: що обіцянки потрібно ділити на 3, а вартість множити на 10. Тому робили DirDB і таке інше.

Потім воно почало зростати. До цього додався сервіс білінгу, тому що платформа перестала справлятися. Потім серер білінгу з MySQL пішов на Mongo. У результаті вийшло працююче рішення, яке переробляє всі виклики, які туди йдуть:

Олексій Найденов. ITooLabs. Кейс розробки на Go (Golang) телефонної платформи. Частина 1

Але десь там, усередині, крутиться цей вендорський продукт – головний, ядерний, який ми колись взяли. Приблизно до кінця 2011 року ми для себе зрозуміли, що головним пляшковим шийкою для нас, звичайно, буде саме цей продукт - ми в нього впораємося. Ми бачили перед собою стіну, в яку бігли повним скаком, оскільки клієнти йшли, додавалися.
Відповідно нам потрібно було щось робити. Звичайно, ми провели досить довгі дослідження щодо різних продуктів – і open source, і вендорських. Я не буду зараз на цьому зупинятись – не про те йдеться. Останній запасний варіант, про який ми думали - це робити свою власну платформу.

Зрештою, ми прийшли саме до цього варіанту. Чому? Тому що всі вендорські та open source продукти робилися для вирішення проблем 10-річної давності. Добре, якщо 10-річної, а дехто і більше! Для нас став очевидним вибір: або ми прощаємося з нашою прекрасною ідеєю про ідеальну послугу (для партнерів, операторів і себе), або ми робимо щось своє.

Ми вирішили робити щось своє!

Вимоги до платформи

Якщо довго чимось займаєшся (експлуатуєш чужий продукт), то в голові потихеньку складаються думка: а як би я це зробив сам? Оскільки ми у компанії всі програмісти (крім продавців, не програмістів немає), то вимоги у нас склалися давно, і вони були зрозумілі:

  1. Висока швидкість розробки. Вендорський продукт, який нас мучив, не влаштовував насамперед тим, що все виходило довго та повільно. Ми хотіли швидко – ми мали багато ідей! У нас і зараз багато ідей, але тоді список ідей був такий, що здавалося, ніби вперед на десять років. Нині лише на рік.
  2. Максимальна утилізація багатоядерного заліза. Це теж для нас було важливо, оскільки ми бачили, що ядер ставатиме лише більше і більше.
  3. Висока надійність. Те, із чим ми теж наплакалися.
  4. Висока стійкість до збоїв.
  5. Ми хотіли прийти зрештою до процесу з щоденними релізами. Для цього нам потрібний був вибір мови.

Олексій Найденов. ITooLabs. Кейс розробки на Go (Golang) телефонної платформи. Частина 1

Відповідно, з вимог до продукту, які ми собі пред'явили, виростають явно логічним чином вимоги до мови.

  1. Якщо ми хочемо підтримки мультиядерних систем, нам потрібна підтримка паралельного виконання.
  2. Якщо нам потрібна швидкість розробки – нам потрібна мова за допомогою конкурентної розробки, конкурентного програмування. Якщо хтось не стикався з різницею, то вона дуже проста:
    • паралельне програмування – це про те, як два різні потоки виконуються на різних ядрах;
    • Конкурентне виконання, точніше підтримка конкурентності - це про те, як мова (або runtime, неважливо) допомагає приховати всю складність, яка випливає з паралельного виконання.
  3. Висока стійкість. Очевидно, що нам потрібен був кластер, причому краще, ніж у нас на вендорському продукті.

Олексій Найденов. ITooLabs. Кейс розробки на Go (Golang) телефонної платформи. Частина 1

Варіантів у нас було не так багато насправді, якщо згадати. По-перше, "Ерланг" - ми його любимо і знаємо, це був мій особистий, персональний фаворит. По-друге, Java – навіть Java, саме Scala. По-третє, мова, яку на той час ми взагалі не знали – Go. Він тоді тільки-но з'явився, точніше, приблизно два роки вже існував, але ще не вийшов у реліз.

Переміг Go!

Історія Go

Ми зробили платформу на ньому. Спробую пояснити чому.

Коротка історія Go. Стартував 2007-го, відкритий 2009-го, перша версія вийшла 2012-го (тобто ми почали працювати ще до першого релізу). Ініціатором виступав Google, який хотів замінити у себе, як я підозрюю, Java.

Автори - дуже імениті:

  • Кен Томсон, який стояв за Unix'ом, вигадав UTF-8, працював над системою Plan 9;
  • Роб Пайк, який разом із Кеном вигадував UTF-8, теж працював над Plan 9, над Inferno, Limbo у Bell Labs;
  • Роберт Гізмер, якого ми знаємо і любимо за те, що він придумав Java HotSpot Compiler, і за те, що він працював над генератором у V8 (інтерпретатор Javascript'а від Google);
  • І понад 700 учасників, включаючи деякі наші патчі.

Олексій Найденов. ITooLabs. Кейс розробки на Go (Golang) телефонної платформи. Частина 1

Go: перший погляд

Ми бачимо, що мова більш-менш проста, зрозуміла. У нас є очевидні типи: у деяких випадках їх потрібно оголошувати, у деяких – не потрібно (це означає, що типи виводяться так чи інакше).

Олексій Найденов. ITooLabs. Кейс розробки на Go (Golang) телефонної платформи. Частина 1

Видно, що модно описувати структури. Видно, що ми маємо поняття покажчика (там, де зірочка). Видно, що є спеціальна підтримка для оголошення ініціалізації масивів та асоціативних масивів.

Приблизно зрозуміло – жити можна. Пробуємо написати Hello, world:

Олексій Найденов. ITooLabs. Кейс розробки на Go (Golang) телефонної платформи. Частина 1

Що бачимо? Це Сі-подібний синтаксис, точка з комою необов'язкова. Вона може бути роздільником для двох рядків, але тільки в тому випадку, якщо це дві конструкції, які саме на одному рядку.

Бачимо, що дужки в керуючих структурах (у 14-му рядку) необов'язкові, а ось фігурні завжди обов'язкові. Бачимо, що типізація статична. Тім у більшості випадків виводиться. Цей приклад трохи складніший за звичайний Hello, world – просто для того, щоб показати, що є бібліотека.

Що ще бачимо важливе? Код організовано у пакети. І для того, щоб пакет використовувати у своєму власному коді, необхідно його імпортувати за допомогою директиви import – це також важливо. Запускаємо – працює. Чудово!

Пробуємо далі щось складніше: Hello, world, але тільки тепер це http-сервер. Що бачимо тут цікавого?

Олексій Найденов. ITooLabs. Кейс розробки на Go (Golang) телефонної платформи. Частина 1

По-перше, функція виступає параметром. Це означає, що функція у нас – «першокласний громадянин» і з ним можна робити багато цікавого у функціональному стилі. Далі бачимо несподіване: директива import посилається безпосередньо на репозиторій GitHub. Все вірно, так і є – більше, так і треба робити.

Go універсальним ідентифікатором пакета є url його репозиторія. Є спеціальна утиліта Goget, яка йде за всіма залежностями, скачає їх, встановить, скомпілює і підготує до використання, якщо це необхідно. При цьому Goget знає про html-meta. Відповідно, можна тримати http-каталог, в якому будуть посилання на свій конкретний репозиторій (як ми, наприклад, робимо).

Що ми ще бачимо? Http та Json у штатній бібліотеці. Є, очевидно, інтроспекція – reflection, яка має використовуватися в encoding/json, тому що ми йому підставляємо просто якийсь довільний об'єкт.

Запускаємо і бачимо, що у нас у 20 рядків уклався корисний код, який компілюється, запускається та віддає поточне середнє завантаження машини (на машині, на якій він запущений).
Що ще важливо з того, що ми можемо тут одразу побачити? Воно компілюється в один статичний бінарник (buinary). Цей бінарник взагалі не має жодних залежностей, ніяких бібліотек! Його можна скопіювати на будь-яку систему, одразу запустити, і воно працюватиме.

Рухаємося далі.

Go: методи та інтерфейси

Go має методи. Ви можете оголосити метод для будь-якого типу користувача. Причому це необов'язково структура, а може бути або якогось типу. Ви можете оголосити або для N32 і писати для нього методи, щоб робити щось корисне.

І ось тут ми вперше впадаємо в ступор ... З'ясовується, що Go немає класів як таких. Ті, хто знає Go, можуть сказати, що там є включення типів, але це зовсім інше. Чим раніше розробник перестане думати про це як про наслідування, тим краще. У Go класів немає, і спадкування теж немає.

Запитання! Що ж компанія авторів під керівництвом Google дала нам, щоб відображати всю складність світу? Нам дали інтерфейси!

Олексій Найденов. ITooLabs. Кейс розробки на Go (Golang) телефонної платформи. Частина 1

Інтерфейс – такий спеціальний тип, який дозволяє написати просто методи, сигнатури методів. Далі будь-який тип, для якого ці методи існують (виконуються), відповідатиме цьому інтерфейсу. Це означає, що ви можете просто описати відповідну функцію для одного типу, іншого (що відповідає тому типу інтерфейсу). Далі – оголосити змінну типу цього інтерфейсу та присвоїти їй будь-який з цих об'єктів.

Для любителів хардкору можу сказати, що в цій змінній буде насправді два покажчики: один – на дані, інший – на спеціальну таблицю дескрипторів, яка характерна саме для цього конкретного типу для інтерфейсу цього типу. Тобто компілятор такі таблиці дескрипторів робить на момент лінкування.

І є у Go, звичайно, покажчики на void. Слово interface {} (з двома фігурними дужками) – це змінна, яка дозволяє вказувати взагалі будь-який об'єкт у принципі.
Поки що все гаразд, все звичне. Нічого дивного.

Go: goroutines

Тепер наближаємося до того, що зацікавило: легковажні процеси – goroutines (горутини) у термінології Go.

Олексій Найденов. ITooLabs. Кейс розробки на Go (Golang) телефонної платформи. Частина 1

  1. По-перше, вони дійсно легкі (менше 2 Кб).
  2. По-друге, витрати на створення такої горутини нікчемні: їх можна створювати тисячу за секунду – нічого не буде.
  3. Обслуговуються вони своїм власним планувальником, який просто передає управління від однієї горутини до іншої.
  4. При цьому управління передається у таких випадках:
    • якщо зустрічається вираз go (якщо горутина запускає наступну горутину);
    • якщо вмикається блокуючий виклик Input/Out;
    • якщо запускається складання сміття;
    • якщо запускається операція з каналами.

Тобто щоразу, коли програма на Go запускається на комп'ютері, вона визначає кількість ядер у системі, запускає стільки потоків, скільки потрібно (скільки ядер у системі чи скільки ви їй сказали). Відповідно, планувальник запускатиме ці легковагові потоки виконання у всіх цих потоках операційної системи в кожному ядрі.

Слід зазначити, що це максимально ефективний спосіб утилізації заліза. Крім показаного, ми робимо ще багато чого. Ми робимо, наприклад, системи DPI, які дозволяють в один unit обслуговувати 40 гігабіт (дивлячись, що відбувається в цих рядках).

Там ми ще до Go використовували ту саму схему саме з цієї причини: тому що це дозволяє зберігати локальність кешу процесора, значно знизити кількість перемикань контексту ОС (що теж займає дуже багато часу). Повторюся: це максимально ефективний спосіб утилізувати заліза.

Цей простий приклад 21 рядок – приклад, який робить просто echo-server. При цьому зверніть увагу, що функція serve – дуже проста, вона лінійна. Там немає жодних колбеків, жодної потреби морочитися і думати… Ви просто читаєте та пишете!

При цьому, якщо ви читаєте і пишете, воно насправді має заблокуватися - це горутина просто кладеться в чергу і дістається планувальником тоді, коли знову можливе виконання. Тобто цей простий код може працювати луною на стільки з'єднань, скільки дозволить ОС на цій машині.

Продовження буде зовсім скоро.

Небагато реклами 🙂

Дякую, що залишаєтеся з нами. Вам подобаються наші статті? Бажаєте бачити більше цікавих матеріалів? Підтримайте нас, оформивши замовлення або порекомендувавши знайомим, хмарні VPS для розробників від $4.99, унікальний аналог entry-level серверів, який був винайдений нами для Вас: Вся правда про VPS (KVM) E5-2697 v3 (6 Cores) 10GB DDR4 480GB SSD 1Gbps від $19 чи як правильно ділити сервер? (Доступні варіанти з RAID1 і RAID10, до 24 ядер і до 40GB DDR4).

Dell R730xd вдвічі дешевше в дата-центрі Equinix Tier IV в Амстердамі? Тільки в нас 2 х Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 ТБ від $199 у Нідерландах! Dell R420 – 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB – від $99! Читайте про те Як побудувати інфраструктуру корп. класу із застосуванням серверів Dell R730xd Е5-2650 v4 вартістю 9000 євро за копійки?

Джерело: habr.com

Додати коментар або відгук