Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

Урок 5 из 25 ~18 мин +20 XP

Очередь: QueueDeclare и свойства

Объявление rabbitmq очереди через QueueDeclare — первый вызов, который меняет состояние брокера: до него можно Dial хоть сто раз, после него у брокера появляется именованный буфер с правилами. Разберём сигнатуру queuedeclare go, четыре флага, идемпотентность и аргументы amqp.Table — всё, что отличает учебный пример от очереди, которую не стыдно завести в продакшене.

Содержание урока · 10 разделов

В первом уроке мы отправили сообщение в несуществующую очередь — и брокер молча выбросил его. Теперь сделаем очередь настоящей: именованной, с понятными свойствами и счётчиками, которую видно в management UI и не стыдно упоминать в ревью. Очередь — единственное место в RabbitMQ, где сообщения живут: буфер с именем, правилами жизни и потребителями, — и это первая сущность уровня канала, которую ваш код создаёт. Хорошая новость: вся политика умещается в один вызов QueueDeclare с четырьмя булевыми флагами и картой аргументов. Плохая: те же флаги становятся контрактом — поменять их на живой очереди нельзя, только удалить и создать заново, потеряв содержимое. Поэтому сегодняшний урок не про «как объявить» — это одна строка, — а про то, какие флаги выбрать и почему объявление превращается в контракт между продюсером и потребителем.

QueueDeclare: объявление очереди в Go#

Сигнатура ch.QueueDeclare(name string, durable, autoDelete, exclusive, noWait bool, args amqp.Table) умещает политику очереди в один вызов, поэтому разбор queuedeclare go стоит начинать с возвращаемого значения. Функция возвращает Queue с тремя полями: Name — как очередь названа на сервере, что важно при пустом имени; MessageCount — сколько сообщений готово к доставке прямо сейчас; ConsumerCount — сколько потребителей подписано. Оговорка честности: счётчики достаются бесплатно только потому, что объявление заодно запрашивает состояние; при noWait брокер ответа не шлёт, и поля останутся нулями — сделаем об этом отдельный вопрос ниже. Ошибка возвращается в двух главных случаях: имя занято очередью с другими флагами — про это честный warn ниже — или соединение уже закрыто. Имя по конвенции пишут без пробелов: точки и дефисы разрешены, слэш не используют, чтобы не путать с путями vhost. Пустое имя — валидный случай: брокер сгенерирует уникальное имя с префиксом amq.gen-, типичное для эксклюзивных очередей ответов.

Очередь — это буфер, а не канал Go#

Очередь брокера похожа на буферизованный канал Go ровно до первой разницы. Канал живёт в памяти процесса: процесс умер — буфер исчез вместе с несчитанными значениями. Очередь живёт в отдельном процессе со своей памятью и опционально на диске: сервис падает, очередь продолжает копить сообщения, воркер перезапускается — и догребает недообработанное. Канал знает только свои значения; очередь ведёт счётчики готовых сообщений и потребителей, помнит, что кому выдано без подтверждения, и возвращает недоставленное при обрыве соединения. Канал безымянный и не переживает деплой; очередь — именованный долгожитель, чьи свойства фиксируются при создании. Обратная сторона: канал отдаёт значение горутине мгновенно и без обещаний, а очередь берёт на себя обязательства доставки — потому и требует подтверждений, что стоит отдельного протокольного обмена. Похоже на одно, а живёт совсем по-другому; это честный способ запомнить разницу, прежде чем сравнивать брокер с решениями на Redis.

Объявляем очередь и читаем счётчики

GO
package main

import (
        "fmt"
        "log"

        amqp "github.com/rabbitmq/amqp091-go"
)

func main() {
        conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
        if err != nil {
                log.Fatal(err)
        }
        defer conn.Close()

        ch, err := conn.Channel()
        if err != nil {
                log.Fatal(err)
        }

        q, err := ch.QueueDeclare("orders", true, false, false, false, nil)
        if err != nil {
                log.Fatal(err)
        }

        fmt.Println("очередь:", q.Name)
        fmt.Println("сообщений:", q.Messages, "потребителей:", q.Consumers)
}

Один вызов — и очередь durable с именем orders существует на брокере. При первом запуске счётчики вернутся нулями, при повторном — настоящими: объявление заодно читает состояние. args передана как nil, значит особых свойств нет; четыре флага после имени разбираем по одному ниже. Зайдите в management UI после запуска: очередь появится на вкладке Queues с пометкой durable.

Четыре флага: durable, autoDelete, exclusive, noWait#

Флаг durable=true делает очередь переживаемой: определение сохраняется на диск, и после рестарта брокера очередь снова готова принимать. Важно не путать с сохранностью сообщений: durable-очередь не делает сообщения долговечными — у сообщений свой флаг DeliveryMode, и разбор у девятого урока. Флаг autoDelete=true удаляет очередь, когда отключается последний потребитель: вариант для трансляций, где подписчики приходят и уходят, а копить нечего. Флаг exclusive=true ограничивает очередь одним соединением: другой клиент не объявит её и не подпишется, а при закрытии соединения брокер удалит очередь сам — так работают очереди ответов в RPC. Флаг noWait=true просит брокера не отвечать на объявление: экономится один сетевой рейс, но вместе с ним уходят счётчики и мгновенные ошибки — флаг ставят, когда уверены в параметрах. Флаги независимы, но сочетания осмысленны: durable вместе с exclusive — странная пара, ведь эксклюзивная очередь умрёт вместе с соединением, чего бы ни обещал durable. Выбирайте флаги под ответ на один вопрос: сколько должна жить очередь и кто имеет право её видеть.

Флаги QueueDeclare одним взглядом#

ФлагЧто делаетТиповое применение
durableопределение очереди переживает рестарт брокерарабочие очереди задач
autoDeleteудаление, когда ушёл последний потребительтрансляции и подписки
exclusiveочередь только одного соединения, удаление при его закрытииочереди ответов RPC
noWaitне ждать ответа брокера на объявлениестартовые объявления с известными параметрами
argsкарта особых свойств вида x-...TTL, лимиты длины, dead letter

Объявление идемпотентно: декларируйте перед использованием#

QueueDeclare идемпотентен: если очередь с таким именем и эквивалентными параметрами уже существует, брокер вернёт её текущие счётчики и не создаст ничего заново. Из этого следует рабочая привычка — объявляйте очередь перед использованием: продюсер перед публикацией, потребитель перед подпиской. Кто встал первым, тот и создал очередь с финальными флагами; второй просто подключается. Так исчезает гонка старта, когда воркер запускается раньше продюсера и падает на публикации в несуществующую очередь. Но идемпотентность строгая по параметрам: флаги и аргументы должны совпасть один в один, любое расхождение — PRECONDITION_FAILED с закрытым каналом. Отсюда вторая привычка: параметры очереди выносят в общий пакет или константы, чтобы продюсер и потребитель не разъехались в правках. Отдельно про политики: часть свойств в продакшене задают не кодом, а policy на стороне брокера — те же TTL и лимиты; совпадающие свойства из кода и политики конфликтуют, поэтому прежде чем объявлять аргументы, посмотрите, что уже навешено политикой.

Аргументы amqp.Table: TTL и лимит длины

GO
args := amqp.Table{
        "x-message-ttl": int32(60000),
        "x-max-length":  int32(1000),
}

q, err := ch.QueueDeclare("notifications",
        true, false, false, false, args)
if err != nil {
        log.Fatal(err)
}

Аргументы — карта amqp.Table с ключами вида x-...: здесь каждому сообщению отводится минута жизни, а очередь ограничена тысячей сообщений. Значения передаются строго типизированно — числа идут как int32, иначе брокер отвергнет объявление. Полный каталог аргументов — dead letter, лимиты, режимы — пригодится по ходу курса; сейчас важно, что args становятся частью контракта очереди и меняются так же болезненно, как флаги.

q.Messages и q.Consumers: очередь как диагноз#

Счётчики из QueueDeclare — бесплатная диагностика, встроенная в самый первый вызов. MessageCount показывает сообщения в состоянии ready: лежат и ждут потребителя. Не вошедшие в это число unacknowledged — выданные, но неподтверждённые — в объявлении не видны; полную картину ready и unacked даёт management UI на вкладке Queues. ConsumerCount — число подписанных потребителей: ноль при растущей глубине означает, что воркеры не запустились или умерли, и очередь превращается в накопитель. Пара «глубина растёт, потребителей ноль» — самый частый сигнал инцидента, поэтому её же превращают в метрики: урок про наблюдаемость настраивает алерты на этих числах. В коде счётчики удобно логировать при старте: сервис печатает, сколько недоработки унаследовал, — и дежурный сразу видит, было ли затишье или копилось за ночь. Только не стройте на этих числах бизнес-логику: они снимаются в момент вызова и мгновенно устаревают; решение «брать ли задачу» по глубине на живой очереди всегда запаздывает.

Какую очередь заводить#

  • Задачная очередь воркеров: durable=true, остальные флаги false — переживает рестарты и не удаляется сама.
  • Эксклюзивная очередь ответов RPC: exclusive=true и пустое имя — брокер сам сгенерирует amq.gen-...
  • Очередь трансляции: autoDelete=true — живёт, пока есть подписчики, исчезает вместе с последним.
  • Прототип для экспериментов: все флаги false — очередь, которую не жалко удалить вечером.
  • Очередь с политикой жизни: аргументы вроде x-message-ttl в amqp.Table.
  • DLQ: обычная durable-очередь для неудачных сообщений — узнаете её в уроке про dead letter.

Очередь объявлена — но пуста, пока в неё никто не пишет. В шестом уроке отправим первые сообщения через PublishWithContext и разберёмся, почему публикация без потребителя всё равно возвращает nil; седьмой — про чтение из очереди через ConsumeWithContext. Если флаг durable показался важным, девятый урок полностью про долговечность очередей и сообщений — там же найдёте, чем persistent-сообщение отличается от durable-очереди. По механике всё знакомо: как и в списках Redis, здесь тот же producer-consumer, но с подтверждениями и гарантиями; близкий родственник — Streams, а карта всего курса — на его странице. Домашнее задание ниже фиксирует рефлекс, который спасает от утренних сюрпризов с флагами.

Проверка: идемпотентность

ch.QueueDeclare("tasks", true, false, false, false, nil) вызывают второй раз — очередь уже есть с теми же флагами. Что произойдёт?

Проверка: noWait

Что вернёт QueueDeclare, вызванный с noWait=true?

Что выведет программа?

Прочитайте код и выберите, что он выведет:

q1, err1 := ch.QueueDeclare("orders",
        false, false, false, false, nil)
q2, err2 := ch.QueueDeclare("orders",
        true, false, false, false, nil)

fmt.Println(q1.Name, err1, q2.Name, err2)

Соберите объявление с аргументами

Допишите карту аргументов, вызов объявления и чтение имени очереди.

args := amqp.{"x-message-ttl": int32(30000)} q, err := ch.("tasks", true, false, false, false, args) fmt.Println("очередь:", q.)

Соберите объявление очереди по строкам

Расставьте строки в порядке выполнения: соединение, проверка, закрытие, канал, объявление, вывод счётчиков.

Строки

Ваша программа

Нажимайте на строки слева, чтобы собрать программу

Карточки: QueueDeclare

Термин

QueueDeclare

нажмите, чтобы перевернуть
Определение

Объявление очереди на канале: имя, четыре флага, аргументы. Возвращает Queue с именем, числом готовых сообщений и потребителей. Идемпотентен при эквивалентных параметрах.

1 / 6

Песочница урока

Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.

Вывод

Запустите код, чтобы увидеть вывод.

Коротко: что запомнить

  • QueueDeclare — один вызов с четырьмя флагами и картой args; вся политика очереди живёт в нём.
  • Флаги: durable переживает рестарт, autoDelete умирает с последним потребителем, exclusive принадлежит соединению, noWait не ждёт ответа.
  • Объявление идемпотентно при совпадающих параметрах — объявляйте очередь и на стороне продюсера, и потребителя.
  • Расхождение флагов даёт 406 PRECONDITION_FAILED и закрытый канал; параметры держат в общем пакете.
  • q.Messages и q.Consumers — мгновенная диагностика: растущая глубина при нуле потребителей — сигнал инцидента.
  • Аргументы amqp.Table с ключами x-... задают TTL и лимиты и становятся частью контракта очереди.

Домашнее задание

Объявите очередь orders с durable=true, запустите программу дважды и сравните счётчики, затем попробуйте изменить durable на false и получите PRECONDITION_FAILED. После этого вынесите параметры очереди в отдельный пакет с константами и убедитесь, что продюсер и потребитель объявляют очередь одинаково.

Личные заметкиПусто
Хранятся только в вашем браузере (RabbitMQ · Очередь: QueueDeclare и свойства)

Вопросы и ответы по уроку

Можно ли поменять флаги у существующей очереди?

Нет: флаги и аргументы фиксируются при создании, попытка объявить имя с другими параметрами даёт 406 PRECONDITION_FAILED. Путь один: удалить очередь и создать заново с потерей содержимого, либо завести новое имя и перевести на него продюсеров и потребителей.

Кто должен объявлять очередь — продюсер или потребитель?

Оба: объявление идемпотентно, и каждая сторона объявляет очередь перед использованием. Кто первый стартовал, тот и создал её с финальными флагами, вторая сторона просто подключится — так исчезает гонка старта между сервисами.

Зачем объявлять очередь с пустым именем?

Брокер сгенерирует уникальное имя с префиксом amq.gen- и вернёт его в поле Name. Смысл есть только вместе с exclusive=true: закрылось соединение — очередь исчезла. Так делают временные очереди ответов в RPC, где имя должно быть уникальным на каждый запрос.

Термины урока

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

Следующая ступень учебного пути

Excelize25 уроков~10 чпрактика25 уроков уже доступны

Excel в Go: чтение и генерация xlsx на excelize

Курс excelize на Go: создание xlsx, чтение, стили, формулы, диаграммы и StreamWriter для больших файлов. 25 уроков с примерами кода и проектом.

  • Создавать и читать xlsx-файлы средствами excelize
  • Работать с листами, ячейками, строками и столбцами
  • Оформлять таблицы: стили, шрифты, границы, форматы чисел и дат
  • Вставлять формулы, диаграммы и изображения