Содержание урока · 10 разделов
- QueueDeclare: объявление очереди в Go
- Очередь — это буфер, а не канал Go
- Четыре флага: durable, autoDelete, exclusive, noWait
- Флаги QueueDeclare одним взглядом
- Объявление идемпотентно: декларируйте перед использованием
- q.Messages и q.Consumers: очередь как диагноз
- Какую очередь заводить
- Песочница урока
- Коротко: что запомнить
- Вопросы и ответы
В первом уроке мы отправили сообщение в несуществующую очередь — и брокер молча выбросил его. Теперь сделаем очередь настоящей: именованной, с понятными свойствами и счётчиками, которую видно в 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.
Объявляем очередь и читаем счётчики
GOpackage 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 |
Кейс: локально очередь объявили с durable=false, а продюсер в тестовом контуре объявляет то же имя с durable=true. Брокер отвечает ошибкой 406 PRECONDITION_FAILED со словами inequivalent arg и закрывает канал — публикации падают разом. Чинится двумя путями: удалить очередь и дать создаться с новыми флагами (потеряв содержимое) или взять новое имя. Отсюда привычка: параметры очереди держат в одном месте кода, а не у каждого сервиса свои.
Объявление идемпотентно: декларируйте перед использованием#
QueueDeclare идемпотентен: если очередь с таким именем и эквивалентными параметрами уже существует, брокер вернёт её текущие счётчики и не создаст ничего заново. Из этого следует рабочая привычка — объявляйте очередь перед использованием: продюсер перед публикацией, потребитель перед подпиской. Кто встал первым, тот и создал очередь с финальными флагами; второй просто подключается. Так исчезает гонка старта, когда воркер запускается раньше продюсера и падает на публикации в несуществующую очередь. Но идемпотентность строгая по параметрам: флаги и аргументы должны совпасть один в один, любое расхождение — PRECONDITION_FAILED с закрытым каналом. Отсюда вторая привычка: параметры очереди выносят в общий пакет или константы, чтобы продюсер и потребитель не разъехались в правках. Отдельно про политики: часть свойств в продакшене задают не кодом, а policy на стороне брокера — те же TTL и лимиты; совпадающие свойства из кода и политики конфликтуют, поэтому прежде чем объявлять аргументы, посмотрите, что уже навешено политикой.
Аргументы amqp.Table: TTL и лимит длины
GOargs := 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 — число подписанных потребителей: ноль при растущей глубине означает, что воркеры не запустились или умерли, и очередь превращается в накопитель. Пара «глубина растёт, потребителей ноль» — самый частый сигнал инцидента, поэтому её же превращают в метрики: урок про наблюдаемость настраивает алерты на этих числах. В коде счётчики удобно логировать при старте: сервис печатает, сколько недоработки унаследовал, — и дежурный сразу видит, было ли затишье или копилось за ночь. Только не стройте на этих числах бизнес-логику: они снимаются в момент вызова и мгновенно устаревают; решение «брать ли задачу» по глубине на живой очереди всегда запаздывает.
Брокер из второго урока продолжает жить в Docker, и объявление очереди выполняется через go run на вашей машине: песочница сайта умеет только подмножество стандартной библиотеки без сети и сторонних пакетов, поэтому ch.QueueDeclare в браузере не запустится. Интерактивы ниже проверяют именно то, что обычно забывается: флаги, идемпотентность и порядок вызовов.
Какую очередь заводить#
- Задачная очередь воркеров: 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)Соберите объявление с аргументами
Допишите карту аргументов, вызов объявления и чтение имени очереди.
Соберите объявление очереди по строкам
Расставьте строки в порядке выполнения: соединение, проверка, закрытие, канал, объявление, вывод счётчиков.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Карточки: QueueDeclare
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +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. После этого вынесите параметры очереди в отдельный пакет с константами и убедитесь, что продюсер и потребитель объявляют очередь одинаково.
Личные заметкиПусто
Вопросы и ответы по уроку
Можно ли поменять флаги у существующей очереди?
Нет: флаги и аргументы фиксируются при создании, попытка объявить имя с другими параметрами даёт 406 PRECONDITION_FAILED. Путь один: удалить очередь и создать заново с потерей содержимого, либо завести новое имя и перевести на него продюсеров и потребителей.
Кто должен объявлять очередь — продюсер или потребитель?
Оба: объявление идемпотентно, и каждая сторона объявляет очередь перед использованием. Кто первый стартовал, тот и создал её с финальными флагами, вторая сторона просто подключится — так исчезает гонка старта между сервисами.
Зачем объявлять очередь с пустым именем?
Брокер сгенерирует уникальное имя с префиксом amq.gen- и вернёт его в поле Name. Смысл есть только вместе с exclusive=true: закрылось соединение — очередь исчезла. Так делают временные очереди ответов в RPC, где имя должно быть уникальным на каждый запрос.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Изучайте дальше
- Публикация: PublishWithContext
- Потребление: ConsumeWithContext
- Ack, Nack и Reject: гарантии доставки
- Долговечность: durable и persistent
- Connection и Channel: модель соединений
- Подключение amqp091-go: первый клиент
- Установка RabbitMQ: Docker и management UI
- Знакомство с RabbitMQ: зачем сервису очереди
- Все курсы по Go
Следующая ступень учебного пути
Excel в Go: чтение и генерация xlsx на excelize
Курс excelize на Go: создание xlsx, чтение, стили, формулы, диаграммы и StreamWriter для больших файлов. 25 уроков с примерами кода и проектом.
- Создавать и читать xlsx-файлы средствами excelize
- Работать с листами, ячейками, строками и столбцами
- Оформлять таблицы: стили, шрифты, границы, форматы чисел и дат
- Вставлять формулы, диаграммы и изображения