[Перевод] Строим real-time веб-приложения с RethinkDB

От переводчика: Совсем недавно узнал про эту довольно интересную базу данных и как раз наткнулся на свежую статью. На Хабре нет почти ни слова о RethinkDB, в связи с чем было решено сделать этот перевод. Добро пожаловать под кат!

image

База данных RethinkDB упрощает разработку веб-приложений, рассчитанных на обновления в режиме реального времени.
RethinkDB — это open source база данных для real-time приложений. Она располагает встроенной системой уведомления об изменениях, которая беспрерывно транслирует обновления для вашего приложения. Вместо постоянного запрашивания новых данных, позвольте базе данных самой отправлять вам последние изменения. Возможность «подписываться» на потоковые обновления может сильно упростить архитектуру вашего приложения и работу с клиентами, поддерживающими постоянный коннект к вашей серверной части.

RethinkDB является безсхемным хранилищем JSON документов, но также поддерживает и некоторые особенности реляционных БД. RethinkDB также поддерживает кластеризацию, что делает её очень удобной в расширении. Вы можете настроить шардинг и копирование через встроенный веб-интерфейс. Последняя версия RethinkDB также включает в себя автоматический «fail-over» для кластеров с тремя и более серверами. (прим. переводчика: подразумевается возможность продолжения работы с БД в случае падения одного из серверов.)

Язык запросов в RethinkDB, который называется ReQL, нативно встраивается в код на том языке, на которым вы пишите своё приложение. Если, например, вы кодите на Python, то при написании запросов к БД будете использовать обычный для Python синтаксис. Каждый запрос составляется из функций, который разработчик собирает в цепочку, чтобы точно описать необходимую операцию.

Несколько слов о ReQL
RethinkDB содержит в себе таблицы, в которых хранятся традиционные JSON документы. Структура самих JSON объектов может иметь глубокую вложенность. Каждый документ в RethinkDB имеет свой основной ключ (primary key) — свойство «id» с уникальным для таблицы-родителя значением. Ссылаясь на primary key в своём запросе вы можете получить конкретный документ.

Написание ReQL запросов в приложении довольно похоже на использование API конструктора SQL запросов. Ниже, на языке JavaScript, представлен простой пример ReQL запроса для определения количества уникальных фамилий в таблице users:

r.table("users").pluck("last_name").distinct().count()


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

  • table запрашивает определнную таблицу в базе данных
  • pluck достаёт определенное свойство (или несколько свойств) из каждой записи
  • disctinct убирает повторяющиеся значения, оставляя только по одному уникальному
  • count подсчитывает и возвращает количество полученных элементов


Традиционные CRUD операции также просты. ReQL включает в себя функцию insert, которую можно использовать для добавления новых JSON документов в таблицу:

r.table("fellowship").insert([
   { name: "Frodo", species: "hobbit" },
   { name: "Sam", species: "hobbit" },
   { name: "Merry", species: "hobbit" },
   { name: "Pippin", species: "hobbit" },
   { name: "Gandalf", species: "istar" },
   { name: "Legolas", species: "elf" },
   { name: "Gimili", species: "dwarf" },
   { name: "Aragorn", species: "human" },
   { name: "Boromir", species: "human" }
])


Функция filter достаёт документы, которые соответствуют определённым параметрам:

r.table("fellowship").filter({species: "hobbit"})


Вы можете добавлять в цепочку такие функции как update или delete, чтобы выполнить определенные операции над документами, возвращенными из filter:

r.table("fellowship").filter({species: "hobbit"}).update({species: "halfling"})


ReQL включает более 100 функций, которые можно комбинировать для достижения необходимого результата. Есть функции для управления потоками, изменения документов, агрегации, записи и т.д. Также существуют функции, «заточенные» под выполнение стандартных операций со строками, числами, метками времени и геопространственными координатами.

Существует даже команда http, которую можно использовать для получения данных из сторонних Web API. В следующем примере показано как можно использовать http для получения постов с Reddit:

r.http("http://www.reddit.com/r/aww.json")("data")("children")("data").orderBy(r.desc("score")).limit(5).pluck("score", "title", "url")


После того как посты получены, они сортируются по очкам и затем отображаются определённые свойства лучшей пятёрки постов. Используя ReQL «на полную мощность», разработчики могут выполнять действительно сложные манипуляции с данными.

Как работает ReQL

RethinkDB client libraries (далее «драйвера») отвечают за интеграцию ReQL в тот язык программирования, на котором ведется разработка приложения. Драйвера внедряют функции для всевозможных запросов, поддерживаемых базой данных. ReQL выражения расцениваются как структурированные объекты, которые похожи на абстрактное синтаксическое дерево. Но для того чтобы выполнить запрос, драйвера переводят эти объекты запроса в специальный формат »RethinkDB’s JSON wire protocol format», в котором затем передаются в базу данных.

Функция run, замыкающая цепочку, переводит запрос, выполняет его на сервере и возвращает результат. Как правило вы будете передавать в эту функцию соединение с сервером, чтобы она смогла выполнить операцию. В официальных драйверах работа с соединением выполняется в ручном режиме. Это значит что вам нужно создавать соединение и закрывать его после выполнения операции.

В следующем примере показано как выполнить запрос в RethinkDB из-под Node.js с установленным драйвером ReQL для JavaScript. Этот запрос достаёт всех хафлингов (halflings) из таблицы fellowship и отображает их в консоли:

var r = require("rethinkdb");

r.connect().then(function(conn) {
return r.table("fellowship")
         .filter({species: "halfling"}).run(conn)
   .finally(function() { conn.close(); });
})
.then(function(cursor) {
return cursor.toArray();
})
.then(function(output) {
console.log("Query output:", output);
})


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

Метод connect устанавливает соединение, который затем используется функцией run. для выполнения запроса. Сам по себе запрос возвращает курсор, который является чем-то вроде открытого окошка в содержимое базы. Курсоры поддерживают «ленивую выборку» (lazy fetching) и предлагают эффективные способы перебора больших объёмов данных. В примере выше я просто решил конвертировать содержимое курсора в массив, так как размер результата относительно мал.

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

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

function paginate(table, index, limit, last) {
   return (!last ? table : table
      .between(last, null, {leftBound: "open", index: index}))
   .orderBy({index: index}).limit(limit)
}


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

Многие более продвинутые особенности ReQL, такие как вторичные индексы, соединение таблиц и использование анонимных функций, остаются за пределами этой статьи. Тем не менее, при желании вы можете ознакомиться с ними на странице документации ReQL API.

Создание real-time веб-приложений с помощью changefeeds

RethinkDB обладает встроенной системой уведомления об изменениях, которая значительно упрощает разработку приложений, работающих в режиме реального времени. Если вы вставите функцию changes в конец цепочки, то в качестве результата запроса будет запущен беспрерывный поток, отражающий все происходящие изменения. Такие потоки называются changefeeds (далее «ченджфид»).

Привычные нам запросы к БД хорошо подходят к традиционной веб-модели «запрос/ответ». Однако, постоянное опрашивание сервера не практично для real-time приложений, использующих постоянное подключение к серверу или потоковую передачу данных. Ченджфиды предоставляют альтернативу обычному опрашиванию, а именно возможность постоянной подачи обновленных результатов в приложение.

Вы можете прикрепить ченджфид прямо к таблице, чтобы отслеживать любые изменения её содержимого. Вы также можете использовать ченджфиды с более сложными запросами, чтобы получать обновления только необходимых вам данных. Например, можно прикрепить ченджфид к запросу, в котором используются функции orderBy и limit, чтобы создать динамическую таблицу рекордов для многопользовательской игры:

r.table("players").orderBy({index: r.desc("score")}).limit(5).changes()


Игроки сортируются по очкам и затем выводится первая пятёрка. Как только появятся какие-то изменения в этой пятёрке лидеров, ченджфид отправит вам обновлённые данные. Даже если игрок, который изначально не был в ТОП-5, наберёт достаточно очков и вытеснит другого игрока из пятёрки — ченджфид сообщит об этом и передаст все необходимые данные для обновления списка.

Ченджфид отправляет не только новое значение записи, но и предыдущее, позволяя нам сравнивать результаты. Если какая-то из записей удаляется, то её новое значение будет равно null. Также как и для только появившейся, новой записи, старое значение будет равно null. Кстати, вы можете добавлять в цепочку другие операции после changes, если необходимы какие-то манипуляции с поступающими данными.

Когда вы выполняете запрос с командой changes, — будет возвращён курсор, который останется открытым навсегда (помните про окошко, да?). Курсор будет отображать новые изменения как только они становятся доступными. Ниже можно увидеть пример, показывающий как можно получать обновления из ченджфида в Node.js:

r.connect().then(function(conn) {
   return r.table("data").changes().run(conn);
})
.then(function(cursor) {
   cursor.each(function(err, item) {
         console.log(item);
   });
});


Работа курсора ченджфида выполняется в фоновом режиме, а значит ваше приложения не блокируется. В исконно асинхронных окружениях, таких как Node.js, вам вовсе не нужно принимать какие-то дополнительные меры для корректной работы. Если вы работаете с другими языками, то вероятно понадобится установка фреймворков для асинхронного кода, или же ручное внедрение потоков. Официальные RethinkDB драйвера для Python и Ruby поддерживают такие популярные и широко используемые фреймворки как Tornado и EventMachine.

На данный момент, команда changes работает с функциями get, between, filter, map, orderBy, min и max. Поддержка других видов запросов запланирована на будущее.

При создании real-time веб-приложения с помощью RethinkDB, можно использовать WebSockets для трансляции обновлений на front-end. А такие библиотеки как Socket.io удобны в использовании и упростят этот процесс.

Ченджфиды особенно полезны для приложений, рассчитанных на горизонтальное расширение. Когда вы распределяете нагрузку между несколькими экземплярами своего приложения, то обычно прибегаете к помощи дополнительных механизмов, таких как очереди сообщений или in-memory db, чтобы распространить обновления на все сервера. RethinkDB переносит этот функционал на уровень вашего приложения, уплощая его архитектуру и избавляя вас от необходимости использования дополнительной инфраструктуры. Каждый экземпляр приложения подключается непосредственно к БД для получения новых изменений. Как только обновления доступны, каждый сервер транслирует их соответствующим WebSocket клиентам.

В дополнение к real-time приложениям, ченджфиды могут значительно упростить внедрение мобильных push-уведомлений и другого подобного функционала. Ченджфиды представляют событийно-ориентированную модель взаимодействия с базой данных и эта модель во многих случаях оказывается полезной.

Масштабирование и управление кластером RethinkDB

RethinkDB является распределённой базой данных, нацеленной на кластеризацию и простое расширение. Чтобы добавить новый сервер к кластеру, достаточно просто запустить его из командной строки с опцией --join и указанием адреса уже существующего сервера. Если у вас в распоряжении кластер с несколькими серверами, вы можете настраивать шардинг и копирование индивидуально для каждой таблицы. Любые настройки и особенности, работающие на одном экземпляре БД будут работать в точности также и на кластере.

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

RethinkDB позволяет применять ReQL-подход для конфигурации кластера, который идеально подходит для тонкой настройки и автоматизации. ReQL включается в себя простую функцию reconfigure, которую можно привязать к таблице для установки настроек шардинга. Также кластер предоставляет большую часть внутренней информации о своём состоянии и настройках через набор специальных таблиц в RethinkDB. Вы можете делать запросы к системным таблицам, чтобы изменять настройки или получать информацию для мониторинга. Практически весь функционал, предоставляемый через веб-интерфейс, построен на ReQL API.

Вы даже можете использовать ченджфиды в связке с ReQL monitoring API, чтобы получать поток данных о сервере. Например, можно было бы создать свой инструмент для мониторинга, который прикрепляет ченджфид к системной таблице со статистикой и в режиме реального времени передаёт данные для построения графика нагрузки чтения/записи.

RethinkDB 2.1, вышедшая недавно, имеет встроенную поддержку автоматического fail-over’a. Новый функционал улучшает доступность кластеров и уменьшает риск падения сервера БД. Если основной (primary) сервер неисправен, то остальные, вторичные рабочие сервера «выбирают» новый primary, который будет выполнять эту роль до тех пор, пока неисправный сервер заработает или будет удалён из кластера.
Поломки железа, или перебои в работе сети теперь не влияют на доступность данных до тех пор, пока большая часть серверов находится онлайн.

Установка RethinkDB

RethinkDB работает под Linux и MacOS X. Версия для Windows находится в активной разработке и еще не доступна для загрузки. В документации RethinkDB детально описан процесс установки. Мы подготовили APT и Yum репозитории для пользователей Linux, а также установщик для OS X. Вы также можете установить RethinkDB с помощью Docker или скомпилировать исходный код с Github. Чтобы в этом разобраться, вам поможет наша 10-минутная инструкция.

Оригинал: ссылка

© Habrahabr.ru