- PVSM.RU - https://www.pvsm.ru -

Хроники LinguaLeo: как мы сделали «Диалоги на английском» с Node.js и DynamoDB

Хроники LinguaLeo: как мы сделали «Диалоги на английском» с Node.js и DynamoDB

Пользователи LinguaLeo начинают изучать английский язык в Джунглях [1] — хранилище тысяч материалов разного уровня сложности, формата и тематик; шаг за шагом учатся слышать и понимать речь носителей языка и расширять словарный запас. Кому нужна грамматика — идут в Курсы [2]. Словарный запас пополняется не только из Джунглей, при добавлении незнакомых слов в Личный словарь, но и с помощью подготовленных Наборов слов [3], доступных для индивидуального изучения. В разделе Общение можно вести Диалоги на английском [4], чтобы практиковать язык с другими пользователями LinguaLeo в режиме real-time, выбирая темы для общения. Общение только на английском!

Для создания Диалогов на английском мы использовали Node.js, DynamoDB (все на AWS). Сейчас поделимся нашим опытом.

Почему Node.js?

Хроники LinguaLeo: как мы сделали «Диалоги на английском» с Node.js и DynamoDB

«… Чтобы обеспечить нашим пользователям все возможности для общения, мы выбрали технологию Node.js. Почему? Node.js адекватен для работы со stream'ами, где существует огромное количество модулей [5]. Кроме того, грамотный JavaScript-программист, работая на Node, сможет реализовать весь объём задачи, а не только её серверную часть (в отличие, например, от специалиста по Erlang).

В качестве «транспортного узла» между сервером и клиентом мы использовали WebSockets как наиболее быстрый способ доставки информации.

Для решения проблемы кросс-платформенности нами была выбрана библиотека Sock.JS [6]. Во-первых, из-за соответствия стандартам W3C для WS, а во-вторых, Sock.JS отлично подходит для наших потребностей.

Хроники LinguaLeo: как мы сделали «Диалоги на английском» с Node.js и DynamoDB

Итак, поехали. Демон Node.js мы запустили на «облачных» мощностях Amazon EC2. Деплоится приложение через git. За логирование и перезапуск отвечает модуль forever [7]. К слову, у Amazon'а есть очень полезный сервис — Amazon Cloudwatch [8]. Его функционал позволяет мониторить основные параметры системы, а главное его достоинство — настраиваемые нотификации, которые позволяют следить только за тем, что действительно важно. Для получения детальной информации о состоянии приложения мы используем nodetime [9].

Хроники LinguaLeo: как мы сделали «Диалоги на английском» с Node.js и DynamoDB

В качестве драйвера для работы с DynamoDB используется модуль dynode [10]. Он хорошо себя зарекомендовал, однако несовершенная техническая документация добавила дёгтя в бочку мёда. Официальный Node.js SDK [11] от Amazon'а вышел позднее и всё ещё находится в стадии Developer Preview. Таким вот естественным путём образом было принято решение использовать продукты от Amazon'а, ибо они отвечают всем техническим требованиям.

Хроники LinguaLeo: как мы сделали «Диалоги на английском» с Node.js и DynamoDB

Тонкости работы Node.js

Особое внимание мы уделили проблеме авторизации пользователя на сервере диалогов. У нас за авторизацию на сервере отвечают cookie. Это весьма «дорогое» решение с точки зрения скорости работы, но оно подкупает стабильностью и безопасностью, плюс независимой сервис-ориентированной архитектурой.

Как это работает? Cookie пользователя поступают через WebSockets на сервер диалогов → сервер диалогов проверяет валидность cookie, отправляя запрос с полученной кукой к нашему API, а оно уже отдаёт данные пользователя обратно. Если данные получены без ошибок, сервер «Диалогов» делает своё дело (авторизует) и отсылает данные на пользователя [12].

Мы знаем, что многие занимаются на LinguaLeo не только дома, но и на работе. Часто случается, что корпоративные фаерволы настроены чрезмерно строго, из-за чего все порты, кроме стандартных, бывают недоступны. Мы помним об этом, и для WebSocket-соединения используем порт 443 (без привязки к https). Это решение позволяет избегать возможных сетевых проблем.

Особенности DynamoDB

Чуть больше года назад Amazon выпустил распределённую NoSQL базу данных — Amazon DynamoDB [13]. На этапе проектирования системной архитектуры перед нами стоял выбор между двумя продуктами: MongoDB и DynamoDB. От первого варианта мы отказались из-за сложностей с администрированием, и выбор пал на Amazon, так как поддержки в случае с его продуктом не требовалось. Ну и, конечно, самим было интересно «обкатать» технологию для дальнейшего использования.

Оказалось, работа с DynamoDB сильно отличается от всего того, что мы привыкли видеть. Являясь SaaS-продуктом, эта штука научила нас принимать в расчёт время на http-запрос. В пределах датацентра Amazon'а среднее время запроса составляет около 20 миллисекунд, из-за которых мы пришли к выводу: крайне желательно все выборки делать только через индексы (это быстрее и дешевле), а scan-запросы использовать исключительно для аналитики или миграций.

Структура данных

Dialogs — хранение метаданных диалога, кэширование последнего сообщения.

Хроники LinguaLeo: как мы сделали «Диалоги на английском» с Node.js и DynamoDB

UserDialogs — список диалогов пользователя, кэширование + счетчик непрочитанных сообщений.

Хроники LinguaLeo: как мы сделали «Диалоги на английском» с Node.js и DynamoDB

Messages — все сообщения пользователей.

Хроники LinguaLeo: как мы сделали «Диалоги на английском» с Node.js и DynamoDB

User2User — участники диалога.

Хроники LinguaLeo: как мы сделали «Диалоги на английском» с Node.js и DynamoDB

Пример 1

Процесс получения всех диалогов пользователя для страницы «Мои диалоги на английском» [14] реализован нестандартно. На странице пользователь видит все свои диалоги, в том числе общее количество непрочитанных сообщений, а также последнее сообщение каждого диалога.

Данные выбираются за два запроса. Первый – это выборка всех dialogld из таблицы UserDialogs с помощью Query [15]. Второй – это получение диалогов посредством BatchGetItem. Тут есть небольшой нюанс — BatchGetItem [16] за один раз выбирает максимум 100 сообщений. Поэтому, если их у пользователя 242 штуки, нам потребуется сделать 3 запроса, что занимает около 70-100 миллисекунд. Помня об этом и стремясь к большей оптимизации производительности, мы кэшируем данные диалогов в ElasticCache [17], обновляя его при каждом новом сообщении.

Хроники LinguaLeo: как мы сделали «Диалоги на английском» с Node.js и DynamoDB

Пример 2

Добавление нового сообщения в диалог тоже происходит нетривиально. Скорость базы данных нам не очень важна, поскольку мы не дожидаемся записи в DynamoDB. Нам ведь нужно провести большое количество записей, а это играет значительную роль для быстроты последующей выборки. Сначала мы записываем новое сообщение в таблицу Messages PutItem [18], а после кэшируем в таблице Dialogs UpdateItem [19]. Дальше нам нужно заинкрементить messageCounter в таблице UserDialogs для каждого пользователя (UpdateItem). Всякое ведь бывает, вдруг один из пользователей решил удалить диалог и сбросил нам счетчик сообщений. В сумме = 4 запроса, по времени собираются около 70-100 миллисекунд. К сожалению, такие транзакции не поддерживаются в DynamoDB, что накладывает ощутимые ограничения для процессов, где сохранность данных критично важна.

А вообще, изменение требований к продукту — явление довольно частое. Иногда это влечёт за собой трансформацию структуры данных. В реляционных базах данных это решается с помощью ALTER TABLE, а вот в DynamoDB этой штуки просто нет. Изменение схемы тут обходится очень дорого. Нам приходится пересоздавать таблицы или использовать Elastic MapReduce [20]. За оба варианта приходится платить. Много данных = много денег. Чтобы с этим как-то справиться, пришлось выбирать все данные Scan'ом по 1 мегабайту, а после записывать в новую таблицу. Отнимает очень много времени, но это плата за отсутствие DBA.

Впечатления от DynamoDB

Наш эксперимент по использованию DynamoDB удался. Это потрясающая база данных, которая отличается простотой масштабирования. Работаете с ней и забываете про администрирование. Но помните: взамен она требует аккуратного обращения на этапе проработки архитектуры. Иначе возможны всякие там неожиданные повороты и неприятные грабли. Мы рекомендуем использовать DynamoDB, если некритичных данных много, а работать с ними надо часто. Мы пользуемся — нам нравится» :)

***
Общайтесь легко и непринуждённо в Диалогах на английском [21] — практикуйте английский язык в приятной компании и присоединяйтесь к нашей команде [22]!

Следите за новостями на Facebook [23], Вконтакте [24] и в Twitter [25], делитесь впечатлениями и получайте удовольствие. Свобода общения — это здорово!

Хроники LinguaLeo: как мы сделали «Диалоги на английском» с Node.js и DynamoDB Команда LinguaLeo

Автор: LinguaLeo

Источник [26]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/tehnologii/32248

Ссылки в тексте:

[1] Джунглях: http://lingualeo.ru/jungle

[2] Курсы: http://lingualeo.ru/course/grammar

[3] Наборов слов: http://lingualeo.ru/glossary

[4] Диалоги на английском: http://habrahabr.ru/company/lingualeo/blog/164013/

[5] модулей: https://npmjs.org/

[6] Sock.JS: https://github.com/sockjs

[7] forever: https://github.com/nodejitsu/forever

[8] Amazon Cloudwatch: http://aws.amazon.com/cloudwatch/

[9] nodetime: https://nodetime.com/

[10] dynode: https://github.com/Wantworthy/dynode

[11] Node.js SDK: http://aws.amazon.com/sdkfornodejs/

[12] пользователя: https://gist.github.com/mbarinov/5102527

[13] DynamoDB: http://aws.amazon.com/dynamodb/

[14] «Мои диалоги на английском»: http://lingualeo.com/savanna/dialogs

[15] Query: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/API_Query.html

[16] BatchGetItem: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/API_BatchGetItems.html

[17] ElasticCache: http://aws.amazon.com/elasticache/

[18] Messages PutItem: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/API_PutItem.html

[19] Dialogs UpdateItem: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/API_UpdateItem.html

[20] Elastic MapReduce: http://aws.amazon.com/elasticmapreduce/

[21] Диалогах на английском: http://lingualeo.ru/savanna/dialogs

[22] присоединяйтесь к нашей команде: https://docs.google.com/a/lingualeo.com/document/d/1jrYc4hHDmHi8YgQ3lclKGg4gt3Buq6WgZpfCkpijWK4/edit

[23] Facebook: https://www.facebook.com/LinguaLeo.ru

[24] Вконтакте: http://vkontakte.ru/lingualeo_ru

[25] Twitter: https://twitter.com/lingualeo

[26] Источник: http://habrahabr.ru/post/176859/