POC очереди хроники вернул непредвиденную задержку

Одна из наших систем имеет микросервисную архитектуру, использующую Apache Kafka в качестве служебной шины. Низкая задержка — очень важный фактор, но еще важнее надежность и согласованность (ровно один раз).

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

На данный момент наши потребности обрабатывают 10 транзакций в секунду (TPS), а нагрузочный тест выполняет 20 TPS, но по мере развития системы и добавления дополнительных функций мы знаем, что достигнем стадии, когда потребность будет составлять 500TPS, поэтому мы начали волнуюсь, сможем ли мы добиться этого с Кафкой.

В качестве доказательства концепции я попытался переключиться на один из наших микросервисов, чтобы использовать хронику-очередь вместо темы Kafka. Было легко выполнить миграцию, следуя примеру avro из Chronicle-Queue-Demo git hub repo

public class MessageAppender {
    private static final String MESSAGES = "/tmp/messages";

    private final AvroHelper avroHelper;
    private final ExcerptAppender messageAppender;

    public MessageAppender() {
        avroHelper = new AvroHelper();
        messageAppender = SingleChronicleQueueBuilder.binary(MESSAGES).build().acquireAppender();
    }

    @SneakyThrows
    public long append(Message message) {
        try (var documentContext = messageAppender.writingDocument()) {
            var paymentRecord = avroHelper.getGenericRecord();
            paymentRecord.put("id", message.getId());
            paymentRecord.put("workflow", message.getWorkflow());
            paymentRecord.put("workflowStep", message.getWorkflowStep());
            paymentRecord.put("securityClaims", message.getSecurityClaims());
            paymentRecord.put("payload", message.getPayload());
            paymentRecord.put("headers", message.getHeaders());
            paymentRecord.put("status", message.getStatus());
            avroHelper.writeToOutputStream(paymentRecord, documentContext.wire().bytes().outputStream());
            return messageAppender.lastIndexAppended();
        }
    }
}

После настройки этого приложения мы запустили цикл для создания 100_000 сообщений в очередь хроники. Каждое сообщение имеет одинаковый размер, а окончательный размер файла составил 621 МБ. Для обработки записи всех сообщений потребовалось 22 минуты 20 секунд и 613 миллисекунд (~ 1341 секунда), поэтому в среднем около 75 сообщений в секунду.

Это было определенно не то, на что мы рассчитывали, и так далеко от задержек, рекламируемых в документации хроники, что заставило меня поверить, что мой подход был неправильным. Я признаю, что наши сообщения не маленькие, около 6,36 КБ на сообщение, но я не сомневаюсь, что хранить их в базе данных было бы быстрее, поэтому я все еще думаю, что делаю это неправильно.

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

Заранее благодарим вас за ваш вклад или предложения.


person Julian    schedule 16.04.2021    source источник
comment
Могу ли я предложить вам бросить вызов, если вам нужна распределенная микросервисная арка. Какую проблему он призван решить для вас? Реально ли решить эту проблему?! Помните, что вызов метода занимает наносекунды, а сетевой вызов занимает мс (в 1000 раз медленнее). Вы можете получить большинство преимуществ микросервисов, имея много проектов dll и внедрение зависимостей. Каждый проект должен иметь свое индивидуальное изолированное хранилище данных, если это необходимо. Возможно, вы добавите pub-sub backbone, например, guava, или используете akka (akka позволит вам довольно легко масштабироваться до распределенной системы, если вы расширите свое оборудование). Мои 2 цента надеюсь, что это поможет   -  person DarcyThomas    schedule 18.04.2021
comment
В моем POC я использовал только свою книгу Mac, так что это не было сетью. И да, наша микросервисная архитектура сделана таким образом, что каждый экземпляр микросервиса имеет собственное хранилище данных. Конечно, при передаче сообщений между службами miro, работающими на разных машинах, будет работать сеть, но в этом POC этого не было.   -  person Julian    schedule 18.04.2021
comment
Возможно, подключите профилировщик и посмотрите, не слишком ли много вы выделяете объектов, особенно плохо, если они продвигаются к более высоким поколениям. Посмотрите, являются ли они вашими объектами или объектами Chronicle Queue. Вы разбираете свою оперативную память или процессор (или сеть)?   -  person DarcyThomas    schedule 19.04.2021
comment
Ручное создание объекта Avro каждый раз кажется мне чем-то вроде запаха кода. Можете ли вы создать предопределенное сообщение -> сериализатор avro и использовать его для заполнения очереди? Или просто для тестирования создайте один объект avro вне цикла и подавайте этот объект в очередь много раз. Таким образом, вы можете увидеть, что является узким местом в здании или в очереди.   -  person DarcyThomas    schedule 19.04.2021
comment
Пожалуйста, опубликуйте свое последнее предложение в качестве ответа. Я сделал несколько записей в журнале и обнаружил, что при создании сообщения для игры с ним была цифровая подпись и операции сортировки, для завершения которых потребовалось около 12 миллионов, что, конечно, умножается на 100 000, что дает сложение. Повторное использование одной и той же подписи сократило время до трех секунд, что является совершенно другим результатом. Очень доволен результатом. Тем не менее, посмотрим, что произойдет, если очередь будет где-то в AWS на каком-то хранилище EBS, но пока это выглядит многообещающе.   -  person Julian    schedule 19.04.2021