Разработка облачного сервиса Scorocode: часть 1

в 8:26, , рубрики: BaaS, backend, backend as a service, Go, golang, mongodb, scorocode, Блог компании Scorocode, облачный сервис, Программирование, разработка мобильных приложений, разработка облачных сервисов

Разработка облачного сервиса Scorocode: часть 1 - 1

В этой статье я расскажу, как мы разрабатывали облачный сервис Scorocode, с какими проблемами столкнулись, и, что самое важное, поделюсь планами развития.

Небольшой опрос в конце статьи позволит читателям отдать голоса за планируемые в будущем функции, тем самым повлияв на стратегию развития сервиса.

Предпосылки

Уже с 2011 года я активно использовал Parse для проведения быстрых экспериментов с разработкой мобильных и web-приложений. Полезность сервиса не вызывала сомнений, лишь некоторые огрехи периодически вызывали желание найти что-то более удобное.

Со временем, попробовав несколько похожих сервисов, я пришел к следующим выводам:

  1. Backend как сервис нужен многим разработчикам систем в архитектуре «клиент-сервер», так как ускоряет работу в разы, особенно на начальном этапе, когда структура данных и серверная логика уже нужны, а ресурс для её разработки ограничен.
  2. Основной функционал такого сервиса: структурированное хранение и доступ к данным с помощью SDK для разных платформ и возможность разработки серверной логики. Дополнительный функционал, как сообщения sms/push/email, готовые объекты типа пользователи/роли, с одной стороны, может реализовываться разработчиками самостоятельно, с другой стороны, еще больше ускоряет работу и позволяет сосредоточиться на frontend.
  3. Нет ни одного сервиса с полноценной документацией на русском языке. Да, есть переводы кусков и небольшое количество примеров, но они не дают полного понимания возможностей и подводных камней сервиса.

В связи с этим возникла идея разработки подобного сервиса для русскоязычной аудитории, в котором можно было бы реализовать собственные пожелания и пожелания пользователей.

Историю о том, как мы в 2015 году проходили организационный путь от идеи до инвестпроекта опущу, об этом лучше расскажут мои коллеги. А вот на технической части остановлюсь подробнее.

Средства разработки

После определения минимума функций, которые нужно было реализовать, стали определяться со средствами разработки.

Вариант использования платного проприетарного ПО сразу было отметен в связи с его несостоятельностью. Основной причиной явилось то, что в течение последних 5-6 лет отрасль разработки ПО претерпела сильные качественные изменения в положительную сторону. Те задачи, которые раньше могли решаться исключительно с помощью платформ и средств «монстров» IT, сегодня могут быть решены быстро и эффективно с помощью современных средств разработки, языков программирования и платформ, большинство из которых распространяется под лицензией MIT.

Итак, мы сформировали перечень функций, и стали выбирать платформу, на которой будет работать основная часть сервиса – сервер API. По нашему мнению, один сервер должен был держать не менее 10 тысяч запросов в секунду, чтобы из таких серверов можно было собирать кластеры, выдерживающие нагрузку до 50 тысяч запросов в секунду. Это число появилось не случайно. В одной из разрабатываемых нами промышленных систем есть именно такие требования к нагрузке, и мы приняли его за отправную точку, с прицелом на перевод backend этой системы в облако (кстати, использование требований к этой же системе нам позволило рассчитать экономические выгоды от использования облачного backend).

В итоге было протестировано 3 варианта реализации API с форматом обмена JSON. Тестирование проводилось с помощью Яндекс.Танк. Результаты:

  1. Node.js + Express.js – 4 000 запросов в секунду
  2. Node.js + Total.js – 1 500 запросов в секунду
  3. Собственный сервер, написанный на Golang – 20 000 запросов в секунду

Добавлю, что в качестве СУБД была единогласно выбрана mongoDB, как современная, масштабируемая СУБД, выдерживающая большие нагрузки, с подробной и качественной документацией и большим числом примеров и драйверов под популярные языки программирования.

Выбор был сделан в пользу собственной разработки, и мы начали прорабатывать архитектуру.

Архитектура

Основной задачей при построении архитектуры сервиса была реализация масштабируемой кластерной системы. После экспериментов мы пришли к следующей конфигурации:

  • Точка входа запросов к API – DNS Round Robin, распределяет вызовы между балансировщиками;
  • Балансирировщик – Nginx, распределяет запросы между серверами API;
  • Сервер API – собственная разработка на Golang, реализована в архитектуре MC (Model-Controller), каждый сервер получает данные о приложении их из основной базы данных (mongoDB), в том числе адрес кластера данных, в котором хранятся данные приложения, кэшируя эти данные (кэш в Redis – 10 минут, со сбросом при внесении изменений в приложение);
  • Кластер данных – кластер mongoDB и инстанс Redis;
  • Файловое хранилище – OpenStack Swift;
  • Сервер очередей – RabbitMQ, используется для постановки в очередь заданий на запуск серверных скриптов, отправки сообщений и т.п.
  • Микросервисы – собственные разработки на Golang: миграция с Parse, отправка сообщений (email, PUSH, SMS), выполнение серверного кода (модуль, использующий движок Google V8)

В процессе разработки и тестирования возникало много мелких задач, но в целом архитектура оказалась жизнеспособной и комплекс успешно прошел испытания.

Функции

Как я уже писал выше, на начальном этапе мы реализовали базовый функционал. Границы минимального набора базировались на необходимости миграции пользователей Parse и минимальными требованиями к функциям backend для разработки не очень сложных приложений.

В ходе реализации функциональности возникали серьёзные и не очень проблемы. Пару характерных привожу ниже.

Проблема 1. Скорость парсинга BSON.

Как вы знаете, mongoDB отдает данные в формате BSON, который достаточно просто разбирается и конвертируется в JSON. Тем не менее на больших объемах парсинг BSON занимает довольно приличное время. К примеру, на выборке 1000 документов среднего размера парсинг BSON в JSON занимает более 1,5 секунд. Для нас такая скорость была неприемлемой.

Попробовали полностью переписать парсер драйвера mgo.v2. Не помогло. Пришли к выводу, что уменьшить время можно было либо увеличением частоты и количества ядер на сервере, либо перекладыванием этой задачи на клиента.

В итоге было принято решение все выборки возвращать в формате BSON с последующим разбором в SDK на клиенте. Так это и работает по сей день.

Проблема 2. Скорость работы триггеров JavaScript.

Изначально движком, который будет выполнять серверные скрипты, был выбран Google V8, и он прекрасно справился со своей задачей для асинхронных скриптов. А вот с триггерами на операции с данными возникли проблемы.

Сам движок V8 очень шустрый, но стартует он относительно медленно – 150-300 миллисекунд. А у нас было ограничение на время работы триггера – 500 миллисекунд. Отдавать половину этого времени на старт движка было неразумно. Создавать пул заранее запущенных «воркеров» – нажить кучу проблем с переключением контекстов.

Поэтому для триггеров нами был выбран самый быстрый вариант для выполнения кода JavaScript в Golang – библиотека Otto Роберта Кримена. Да, у нее есть определенные ограничения, но для задачи выполнения триггеров она подошла идеально. На основе этой библиотеки нами был реализован «терминатор» стека вызовов для прерывания бесконечного цикла вызовов триггеров (например, когда в триггере beforeInsert вызывается операция insert).

О проблемах и задачах, возникающих в процессе реализации, можно писать бесконечно. Надеюсь, что аудитория сама укажет технические темы, о которых интересно было бы почитать, и я с удовольствием о них расскажу.

Что дальше?

Сейчас мы запланировали и начали работы над новыми функциями системы. Учитывая стабильно высокий уровень интереса к сервису Scorocode, мы бы хотели узнать мнение сообщества о необходимости реализации таких функций. Готовы ответить на все ваши вопросы в комментариях к статье.

Автор: Scorocode

Источник

Поделиться новостью

* - обязательные к заполнению поля