Apache Kafka и миллионы сообщений в секунду

w7x_zoluzc7p8iyfoa-d-juw1r4.png

Мы в компании любим и уважаем Apache Kafka, и в ознаменование выхода ее недавнего обновления я решил подготовить статью про ее производительность. А еще рассказать немного про то, как выжать из нее максимум.

Зачем нам это?


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

Чтобы все работало, нам необходимо осуществлять обмен данными между разными приложениями. Причем обмен должен происходить достаточно быстро и без потерь, ведь, в конечном счете, это выливается в эффективность подбора персонала.

Для решения этой задачи нам предстояло выбрать среди нескольких доступных на рынке брокеров сообщений, и мы остановились на Apache Kafka. Почему? Потому что она быстрая и поддерживает семантику гарантированно единственной доставки сообщения (exactly-once semantic). В нашей системе важно, чтобы в случае отказа сообщения все равно доставлялись, и при этом не дублировали бы друг друга.

Как все устроено


Все в Apache Kafka построено вокруг концепции логов. Не тех логов, что вы выводите куда-либо для чтения человеком, а других, абстрактных логов. Логи, если взглянуть шире, это наиболее простая абстракция для хранения информации. Это очередь данных, которая отсортирована по времени, и куда данные можно только добавлять.

Для Apache Kafka все сообщения — это логи. Они передаются от производителей (producer) к потребителям (consumer) через кластер Apache Kafka. Вы можете адаптировать кластер Apache Kafka под свои задачи для повышения производительности. Помимо изменения настроек брокеров (машин в кластере), настройки можно менять у производителей и потребителей. В статье пойдет речь об оптимизации только производителей.

Есть несколько важных концепций, которые нужно понять, чтобы знать, что и зачем «тюнить»:

  1. Нет потребителей — скорость падает


    Если новые сообщения тут же никто не забирает, они сохраняются на диск. А это очень дорогая операция. Поэтому если потребители внезапно отключились или «залагали», пропускная скорость упадет.
  2. Чем больше размер сообщения, тем выше пропускная способность


    Факт, что гораздо «легче» записать на диск 1 файл размером в 100 байт, чем 100 файлов в 1 байт. А ведь Apache Kafka при необходимости скидывает сообщения на диск. Интересный график с сайта Linkedin:
    _wp-dp8wxyzlfck523s7uhjmqxo.png

  3. Новые производители/потребители почти линейно увеличивают производительность


    Но не забываем о пункте №1.
  4. Асинхронное реплицирование может потерять ваши данные


    В отличие от синхронного механизма реплицирования, при асинхронном главная нода Apache Kafka не дожидается подтверждения получения сообщения от дочерних нод. И при сбое мастер-ноды данные могут быть утеряны. Так что надо решать — или скорость, или живучесть.

Про параметры производителей


Вот основные параметры конфигурации производителей, которые влияют на их работу:

  • Batch.size
    Размер пакета сообщений, который отправляется от производителя к брокеру. Производители умеют собирать эти «паки», чтобы не отправлять сообщения по одному, т.к. они могут быть достаточно маленькими. В общем случае, чем больше этот параметр, тем:
    • (Плюс) Больше степень сжатия, а значит выше пропускная способность.
    • (Минус) Больше задержка в общем случае.

  • Linger.ms
    По умолчанию равно 0. Обычно продюсер начинает собирать следующий пакет сообщений сразу после того, как отправляет предыдущий. Параметр linger.ms говорит продюсеру, сколько нужно подождать времени, начиная с предыдущей отправки пакета и до следующего момента комплектации нового пакета (batch) сообщений.
  • Compression.type
    Алгоритм для сжатия сообщений (lzip, gzip, и т.д). Этот параметр сильно влияет на задержку.
  • Max.in.flight.requests.per.connection
    Если этот параметр больше 1, то мы находится в так называемом режиме «трубы» («pipeline»). Вот к чему это ведет в общем случае
    • (Плюс) Более хорошую пропускную способность.
    • (Минус) Возможность нарушение порядка сообщений в случае отказа .

  • Acks
    Влияет на «живучесть» сообщений в случае отказа чего-либо. Может принимать четыре параметра: -1, 0, 1, all (то же самое, что -1). В таблице ниже сведения о том, как и на что он влияет:
    zjtpuu3gslx6yi7etagodbf3t6o.png

Как выжать максимум


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

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

Понять в чем «затык» можно с помощью файла bin\windows\kafka-producer-perf-test.bat. Это достаточно гибкий инструмент для профилирования Apache Kafka, и для построения графиков я использовал именно его. А если его пропатчить (git pull github.com/becketqin/kafka KAFKA-3554), в нем можно выставлять два дополнительных параметра: --num-threads (кол-во потоков производителей) и --value-bound (диапазон случайных чисел для нагрузки компрессора).

Есть два варианта того, что можно изменить в производителях, чтобы все ускорить:

  1. Найти оптимальный размер пакета (batch.size).
  2. Увеличить кол-во производителей и кол-во разделов в топике (partitions).

Мы воспроизвели это все у себя. И вот что получилось:
q2aun5lw7vkqkraamjeqz8o4rt8.png
nuk2rypg8s662pwpmliak-fdpzw.png
Как видно, при увеличении дефолтного размера пакета сообщений, увеличивается пропускная способность и уменьшается задержка. Но всему есть предел. В моем случае, когда размер пакета перевалил за 200 Кб, функция почти «легла»:
wsbqiat0y2yuvgmh53d6do7cl6s.png
lp2_ujhrm6muak0aj3smhonxuyk.png
Другим вариантом является увеличение количества разделов в топике при одновременном увеличении количества потоков. Проведем те же тесты, но с уже 16 разделами в топике и 3-мя разными величинами –num-threads (теоретически это должно повысить эффективность):
gyahc_fcuaey1cja-7xkpved56c.png
asgpvmwr1yqkjo8v0dftutlbfvc.png
Пропускную способность это немного подняло, а задержку немного уменьшило. Видно, что при дальнейшем увеличении количества потоков производительность падает из-за издержек на время переключения контекста между потоками. На другой машине график, естественно, будет немного иным, но общая картина скорее всего не изменится.

Заключение


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

Список использованной литературы


  1. Официальный сайт Apache Kafka
  2. Статья о производительности Apache Kafka
  3. Презентация о производительности от главного инженера LinkedIn

© Habrahabr.ru