Миша… нет, Серёжа… нет, Полина! Node-Polina!

в 11:59, , рубрики: beanstalk, driver, javascript, node.js, redis, Блог компании LiveTex, сервер, метки: , , , ,

image

Проектируя архитектуру сервиса вы выбираете инструменты, наиболее подходящие для решаемых вами задач. Но чтобы использовать их по максимуму, необходимо найти самый надёжный и удобный драйвер. Конечно, если вы программируете на Python или, к примеру, PHP, найти нужный драйвер не проблема, ведь за много лет разработчики понаписали всякого, что проверено годами и стабильно работает. Но если вы программируете для node.js — это становится проблемой, драйверы скрипят, утекают и отказываются стабильно работать.
В данной статье мы расскажем о проблемах, с которыми столкнулись при выборе драйверов, и как их решили.

LiveTex — сервис, которым ежедневно пользуются более 10 миллионов посетителей. Как оперативную память нашего приложения мы используем базу данных Redis, в которой за день создаётся более двух миллионов ключей при обработке 34000 команд в секунду. Также используем Beanstalkd как очередь задач в которой каждую секунду обрабатывается более 10000 тасков. Мы хотим, чтобы наш сервис работал быстро и стабильно, от драйверов требуем того же.
Изначально мы планировали использовать для наших задач уже написанные клиенты, но пытаясь «прикрутить» их к нашему сервису, столкнулись со многими проблемами:

  1. Проблема чанков.
    Не учитывается фрагментация данных tcp пакета. Этим страдают beanstalk_client, fivebeans, а также node-amqp, клиент для RabbitMQ, который мы изначально планировали использовать вместо Beanstalkd, и node-thrift для hbase.
  2. Смещение ответов на запрос.
    При определенных нагрузках происходило смещение ответов. На запрос 2 нам приходил ответ на запрос 1. То есть система возвращала неправильные ответы.
  3. Не предназначены для высоких нагрузок.
    При нагрузках в более миллиона запросов для Redis и более 100000 задач для Beanstalkd с payload около 10Кб многие клиенты просто не возвращали результат и вешали всю систему.
  4. Отсутствие fallback
    Для нашего сервиса было необходимо, чтобы все задачи и запросы поставленные в очередь на выполнение не терялись в процессе передачи, даже если некоторое время не было соединения с сетью.

Не найдя готового надёжного клиента для Redis и Beanstalkd, мы решили написать свой.
Любой клиент, обобщённо, это инструмент реализации запросов к сервису и получения от него результатов. Он должен устанавливать соединение и обеспечивать передачу данных между вашим приложением и используемым сервисом. С помощью диаграммы классов UML это можно представить следующим образом:

Миша… нет, Серёжа… нет, Полина! Node Polina!

Модель очень проста и понятна. Именно по такой схеме и работает наш модуль Node-Polina. Пока что, в рамках данной модели мы реализовали клиент для Redis и Beanstalkd.

Node-Polina. Redis-Client.

О возможностях драйвера.

На данный момент в драйвере реализованы такие команды к базе данных Redis, как: set, get, mget, incrby, incr, decr, setex, expire, keys, del, sismember, sadd, srem, smembers. Благодаря тому, что мы не привязывались к конкретной реализации команд, а разделили их по типам на возвращающие числа, строки или массивы, то добавить новую команду в драйвер очень просто. При необходимости вы можете написать нам на gitHub в issues с пометкой enhancement, и мы добавим нужный функционал. Кроме того, в Node-Polina есть поддержка использования connection pooling и shard.

Использование

Простой клиент

// для начала необходимо подключить модуль:
var polina = require('livetex-polina');

// создать клиент с помощью конструктора Client, передав в него порт и // опционально хост:
var client = new polina.redis.Client(6397, '127.0.0.1');

// теперь можно выполнить любую команду:
client.set('key', 'value', function() {
  console.log('Complete.');
}, function(error, opt_code) {
  console.error(error);
});

Connection pooling

// создать клиент с помощью конструктора Bundle, передав в него максимальное 
// количество одновременных подключений, порт и опционально хост:
var client = new polina.redis.Bundle(100, 6397, '127.0.0.1');

Shard

// создать Bucket, его указав размер:
var bucket = new polina.redis.Bucket(9);

// зарегистрировать клиенты, вызвав команду registerClient, передав в // неё значения интервала ключей, клиент и идентификатор: 
bucket.registerClient(0, 3,  new polina.redis.Bundle(3, 6397), 'alpha');
bucket.registerClient(3, 6,  new polina.redis.Bundle(3, 6398), 'beta');
bucket.registerClient(6, 9,  new polina.redis.Bundle(3, 6399), 'gamma');

Node-Polina VS Redis VS nodejs-redis

Сравнение по времени

Миша… нет, Серёжа… нет, Полина! Node Polina!
Из графика видно, что на высоких нагрузках Node-Polina гораздо лучше справляется со своей работой. Данные графики актуальны для node версии 0.10.15. Мы также тестировали драйверы на версии 0.8.24, в которой даже на больших нагрузках Node-Polina продолжает отдавать результаты на запросы, когда node_redis с этим уже не справляется.

image
При обработке данных в 10Кбайт на малых нагрузках отличие Node-Polina от других драйверов почти незаметно. При запросах около 300 000 — 450 000, обработка запросов с помощью Node-Polina требует больше времени, так как внутри драйвера реализуется механизм failover. При нагрузках более 230 000 запросов Node-Polina опять же быстрее.

Сравнение по памяти

image
По расходуемой памяти Node-Polina практически сопоставима с node_redis, но на больших нагрузках опять же требует больше для реализации failover.

image
При обработке данных в 10Кбайт Node-Polina, очевидно, лучше Hiredis. Также, видно, что Полина более стабильна, чем node_redis, её график возрастает равномерно, без значительных флуктуаций.

Написав свой драйвер мы решили проблемы с фрагментацией tcp-пакетов, смещением ответов на запросы и проблему падения с ошибкой при кратковременном разрыве соединения. Мы написали Node-Polina так, чтобы она выдерживала высокие нагрузки, была проста в использовании и имела возможность быстрого и простого добавления функционала. Кроме того, наш драйвер обеспечивает создание пула соединений и шардинг, что часто необходимо при работе с базой данных Redis.

Исходники можно найти в нашем git-репозитории: https://github.com/LiveTex/Node-Polina
Установить через npm можно с помощью команды: npm install livetex-polina

Автор: LiveTex

Источник

Поделиться

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