Как в rabbitmq накопить определённое кол-во строк и запустить эту пачку в callback?
На данный момент работает так - идёт запрос с множеством данных на сервис А, с него данные посылаются в очередь в rabbitmq, откуда читаются по одному сообщению сервисом В и пишутся в CLickHouse. Нужно сделать так, чтобы в сервисе В сообщения накапливались до заданного размера (формировалась пачка) и разом записывались в ClickHouse.
Используется php/Laravel.
public function listen(ClickHouseServer $houseServer, string $queue)
{
$connection = new AMQPStreamConnection(
env('AMQP_SERVER'),
env('AMQP_HOST'),
env('AMQP_USER'),
env('AMQP_PASSWORD')
);
$channel = $connection->channel();
$channel->queue_declare(
$queue, #queue name - Имя очереди может содержать до 255 байт UTF-8 символов
false, #passive - может использоваться для проверки того, инициирован ли обмен, без того, чтобы изменять состояние сервера
true, #durable - убедимся, что RabbitMQ никогда не потеряет очередь при падении - очередь переживёт перезагрузку брокера
false, #exclusive - используется только одним соединением, и очередь будет удалена при закрытии соединения
false #autodelete - очередь удаляется, когда отписывается последний подписчик
);
/**
* не отправляем новое сообщение на обработчик, пока он
* не обработал и не подтвердил предыдущее. Вместо этого
* направляем сообщение на любой свободный обработчик
*/
$channel->basic_qos(
null, #размер предварительной выборки - размер окна предварительнйо выборки в октетах, null означает “без определённого ограничения”
config('app.BASIC_QOS'), #количество предварительных выборок - окна предварительных выборок в рамках целого сообщения
null #глобальный - global=null означает, что настройки QoS должны применяться для получателей, global=true означает, что настройки QoS должны применяться к каналу
);
/**
* оповещает о своей заинтересованности в получении
* сообщений из определённой очереди. В таком случае мы
* говорим, что они регистрируют получателя, или устанавливают
* подписку на очередь. Каждый получатель (подписка) имеет
* идентификатор, называемый “тег получателя”.
*/
$channel->basic_consume(
$queue, #очередь
'', #тег получателя - Идентификатор получателя, валидный в пределах текущего канала. Просто строка
false, #не локальный - TRUE: сервер не будет отправлять сообщения соединениям, которые сам опубликовал
false, #без подтверждения - false: подтверждения включены, true - подтверждения отключены. отправлять соответствующее подтверждение обработчику, как только задача будет выполнена
false, #эксклюзивная - к очереди можно получить доступ только в рамках текущего соединения
false, #не ждать - TRUE: сервер не будет отвечать методу. Клиент не должен ждать ответа
array($houseServer, 'push') #функция обратного вызова - метод, который будет принимать сообщение
);
while (count($channel->callbacks)) {
$channel->wait();
}
$channel->close();
$connection->close();
}
$channel->basic_qos - не помогло. Какие есть варианты?
Ответы (1 шт):
Для этого необходимо читать сообщения из очереди и записывать их в массив пока не накопится пачка нужного размера на стороне сервиса В.
Но я бы предложил Вам полностью отказаться от сервиса B и выполнять чтение очереди непосредственно в ClickHouse. Благо там все для этого есть. А именно - движок RabbitMQ
Пример создания таблицы для чтения очереди. Параметры описан по ссылке выше. rabbitmq_max_block_size и rabbitmq_flush_interval_ms - то что Вам нужно для регулирования размера блоков для записи.
CREATE TABLE queue (
key UInt64,
value UInt64,
date DateTime
) ENGINE = RabbitMQ SETTINGS rabbitmq_host_port = 'localhost:5672',
rabbitmq_exchange_name = 'exchange1',
rabbitmq_format = 'JSONEachRow',
rabbitmq_num_consumers = 5,
date_time_input_format = 'best_effort';
Просто чтение из данно таблицы не эффективно, так как каждое сообщение можно прочитать 1 раз, это скорее полезно для отладки. Для записи данных из очереди в таблицу используйте материализованное представление.
Для начала необходимо задать саму целевую таблицу.
CREATE TABLE daily (key UInt64, value UInt64)
ENGINE = MergeTree();
После этого создаете материализованное представление которое будет читать таблицу очереди и инсертить данные в целевую.
CREATE MATERIALIZED VIEW consumer TO daily
AS SELECT key, value FROM queue;
SELECT key, value FROM daily ORDER BY key;