Содержание урока · 10 разделов
Брокер из второго урока отвечает на /api/overview, но ваш Go-код ещё не умеет с ним разговаривать: закрываем эту дыру. План урока простой и повторяется в каждом библиотечном проекте: объявить модуль, добавить зависимость, открыть соединение и проверить, что оно живое. Звучит скучно ровно до первого инцидента, когда выясняется, что «просто Dial» без таймаута вешает старт сервиса на несколько минут, а пароль из строки подключения оседает в логах CI. Поэтому первый клиент мы напишем сразу во взрослом виде: с контекстом таймаута, честной обработкой ошибок и пониманием, что именно происходит на проводе, пока функция притворяется мгновенной. Очереди, публикации и потребители пойдут следом — сначала фундамент. Если брокера под рукой нет, вернитесь к установке RabbitMQ в Docker и держите контейнер запущенным: каждая строка этого урока стучится в localhost:5672.
Подключение amqp091-go: go get и импорт#
Начинается подключение amqp091-go как всегда: go mod init example.com/orderservice, затем go get github.com/rabbitmq/amqp091-go. Библиотека прописывается в go.mod, а в коде импортируется с алиасом: import amqp "github.com/rabbitmq/amqp091-go" — алиас нужен, потому что имя пакета внутри репозитория не совпадает с последним сегментом пути, и без него каждый импорт тянет за собой длиннющий префикс. Конвенция сообщества железная: пакет по-прежнему зовётся amqp, поэтому код со streadway/amqp и современный код выглядят одинаково — меняется только строка импорта. Версии библиотеки живут в первой мажорной линии: миграции между ними почти незаметны, но changelog стоит читать при обновлении, особенно если опираетесь на поведение контекста в публикациях. Всё, что делает библиотека, — перевод Go-вызовов в кадры протокола AMQP 0-9-1 и обратно, без магии и генерации кода: сигнатуры повторяют команды брокера, и это делает amqp go клиент лучшим учебником самого протокола.
Обновление зависимости и что смотреть в changelog#
Зависимость ставится один раз, а живёт долго, поэтому пара слов про обновления. Команда go get github.com/rabbitmq/amqp091-go@latest поднимает версию до самой свежей, go list -m -versions github.com/rabbitmq/amqp091-go показывает весь список доступных тегов, а точную версию фиксируют через собаку с номером — приём полезный, когда changelog между версиями важнее новизны. В go.sum попадают контрольные суммы модулей: это защита от подмены зависимости, и отключать её не стоит. При апгрейде я смотрю на три вещи: не поменялись ли сигнатуры вызовов с контекстом, не устарел ли используемый метод — библиотека честно помечает такие в комментариях, — и что случилось с таймаутами по умолчанию. Мажорных перестроек у amqp091-go не было: API стабилен с момента переезда под организацию RabbitMQ, и это одна из причин выбрать именно его. В проекте с несколькими брокерами зависимость всё равно одна: разные адреса — это данные конфигурации, а не отдельный код.
Первый клиент: Dial и Close
GOpackage main
import (
"log"
amqp "github.com/rabbitmq/amqp091-go"
)
func main() {
url := "amqp://guest:guest@localhost:5672/"
conn, err := amqp.Dial(url)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
log.Println("соединение установлено:", !conn.IsClosed())
}Минимальный amqp go клиент умещается в пятнадцать строк: amqp.Dial открывает TCP-соединение, договаривается о параметрах протокола и возвращает *amqp.Connection, готовую к работе. Ошибка обрабатывается до любых других шагов — без брокера делать дальше нечего, поэтому честнее упасть сразу. defer conn.Close() освобождает соединение при выходе, а conn.IsClosed() даёт быструю проверку состояния. Пока это всё; каналы и очереди — вопросы следующих уроков.
URL подключения по частям#
Строка amqp://guest:guest@localhost:5672/ — не произвольный текст, а полный адрес с правами доступа, и читать её стоит как конверт. Схема amqp задаёт протокол и шифрование: amqps вместо неё означает «тот же протокол поверх TLS» и другой порт по умолчанию. Дальше учётные данные до собачки — логин и пароль, которые брокер проверит на этапе аутентификации; пароль со спецсимволами URL-кодируется, иначе строка развалится на части. Хост и порт говорят, куда идти: localhost:5672 для локального контейнера, 5671 для TLS. Завершает адрес путь к vhost — виртуальному пространству имён брокера из прошлого урока; пустой путь после порта равен корневому vhost со слэшем. Один нюанс, на котором спотыкаются: vhost / в URL записывается как /, а vhost с именем production — как /production; когда vhost не существует, Dial падает с ошибкой доступа, и не факт, что вы поймёте это без чтения логов брокера.
Анатомия строки подключения#
| Фрагмент | Что значит | Типовая ошибка |
|---|---|---|
| amqp:// | протокол без шифрования | amqps против открытого порта брокера |
| guest:guest@ | логин и пароль для аутентификации | пароль в git и логах CI |
| localhost:5672 | хост и AMQP-порт брокера | 5672 вместо 15672 — порт панели |
| / | путь к vhost, по умолчанию корневой | vhost не создан — отказ доступа |
| amqps://…:5671 | то же поверх TLS | TLS без доверия к сертификату |
Что происходит внутри amqp.Dial#
amqp.Dial выглядит как одна строка, но под ней прогоняется весь протокольный танец AMQP 0-9-1. Сначала обычное TCP-подключение к хосту и порту — здесь рождаются ошибки вида connection refused, когда на порту никто не слушает. Клиент и сервер обмениваются заголовками протокола и приходят к согласию о версии AMQP 0-9-1. Затем сервер предлагает параметры безопасности, клиент отвечает учётными данными из URL — так появляется отказ авторизации при неверной паре. Потом стороны договариваются о рамках соединения: максимальный размер кадра, число каналов и интервал heartbeat, по которому обе стороны будут проверять живость друг друга. Только после этого Dial возвращает управление с готовым объектом соединения. Из этого следует практическое правило: Dial блокируется до полной готовности брокера, поэтому таймаут на него — не украшение, а страховка от зависшего старта сервиса. Как устроены соединения и каналы после рукопожатия — тема следующего урока.
Таймаут подключения через DefaultDial
GOconn, err := amqp.DialConfig(url, amqp.Config{
Dial: amqp.DefaultDial(5 * time.Second),
})
if err != nil {
log.Fatal(err)
}
defer conn.Close()DialConfig принимает amqp.Config, а поле Dial — это функция открытия сетевого соединения. amqp.DefaultDial оборачивает стандартный net.DialTimeout в нужную обёртку: попытка длится не дольше пяти секунд, после чего возвращается ошибка таймаута. Пять секунд — рабочий компромисс: меньше — ложно падает на медленной сети, больше — сервис зависает на старте с мёртвым брокером. Точную настройку heartbeat и свойств клиента аналогично делают в том же Config.
URL с паролем — самый короткий путь пароля в git: закоммиченную строку подключения потом вычищают силами всего репозитория. Держите её в переменной окружения или конфиге вне кода, а дефолт для разработки — только с guest на localhost. Вторая дыра — логи: log.Fatal(err) печатает ошибку без пароля, но логирование «строки подключения для отладки» пароль отдаёт целиком. Логируйте хост, порт и имя пользователя; пароль — никогда.
Ошибки первого запуска: refused, auth, timeout#
Три ошибки встретят вас раньше всех остальных, и различать их стоит по месту возникновения. Connection refused — TCP-уровень: по этому адресу никто не слушает, значит контейнер не поднят, порт не проброшен или хост неверный. Отказ авторизации — уже после TCP и рукопожатия: брокер отвергает пару логин-пароль или не пускает guest из сети; причину ищите в docker logs rabbitmq, там она прописана словами. Таймаут — самый коварный вариант: соединение молчит до предела, обычно виноваты файрвол, security group в облаке или опечатка в хосте, которая уехала в другую сеть. Диагностика идёт слоями: сначала telnet или nc на порт — отвечает ли вообще; затем логи брокера — доходит ли до аутентификации; и только потом код. Если соединение живёт, но рвётся в работе — это история про heartbeat и восстановление соединения, к ней вернёмся с рабочим кодом в руках.
Код этого курса выполняется локально: брокер — контейнер Docker, клиент — обычный go run. Песочница сайта умеет только подмножество стандартной библиотеки без сети и сторонних пакетов, поэтому amqp.Dial в браузере не запустится — зато все интерактивы ниже работают прямо на странице и повторяют сигнатуры настоящей библиотеки.
Чек-лист первого клиента#
- go.mod содержит github.com/rabbitmq/amqp091-go после go get.
- Строка подключения приходит из переменной окружения, а не из исходника.
- У amqp.Dial есть таймаут через amqp.DefaultDial — старт не может зависнуть навсегда.
- Ошибка Dial обрабатывается фейл-фастом: без брокера сервис не должен продолжать.
- defer conn.Close() стоит сразу после успешного подключения.
- В логи пишутся хост, порт и пользователь — но никогда не пароль.
Соединение есть — самое время проверить его под нагрузкой. В следующем уроке разберём, почему поверх одного TCP-соединения открывают много каналов, где у этой модели лимиты и почему канал привязывают к одной горутине. Проверку соединения в тестах, кстати, делают тем же Dial: локальный брокер из Docker поднимается за секунды, а табличные тесты с ним уже дают интеграционную уверенность без моков. Держите в голове и обратную сторону: тест против живого брокера требует окружения, поэтому unit-тесты логики пишут без сети — к этой теме вернёмся ближе к концу курса. А если хочется увидеть сообщения своими глазами раньше четвёртого урока — закройте соединение и посмотрите в management UI: Connections покажет пустоту, и вы сразу почувствуете, за чем следит IsClosed.
Проверка: что делает Dial
Что обязан сделать amqp.Dial перед тем, как вернуть соединение?
Проверка: vhost в URL
Как выглядит строка подключения к vhost с именем production на локальном брокере?
Что выведет программа?
Прочитайте код и выберите, что он выведет:
conn, err := amqp.Dial(
"amqp://guest:guest@127.0.0.1:5672/")
if err != nil {
fmt.Println("dial:", err)
return
}
defer conn.Close()
fmt.Println("ok")Соберите подключение с таймаутом
Допишите вызов DialConfig, конструктор таймаута и закрытие соединения.
Соберите первый клиент по строкам
Расставьте строки в порядке выполнения: строка подключения, соединение с таймаутом, проверка ошибки, закрытие, канал, проверка состояния.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Карточки: первый клиент
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- go get github.com/rabbitmq/amqp091-go и алиас amqp — конвенция, общая для всей экосистемы.
- URL подключения: схема, учётные данные, хост с портом и путь к vhost.
- amqp.Dial прогоняет рукопожатие AMQP 0-9-1 целиком и блокируется до готовности брокера.
- Таймаут ставят через amqp.DialConfig и amqp.DefaultDial — старт сервиса не должен висеть вечно.
- Connection refused, отказ авторизации и таймаут диагностируются слоями: TCP, потом логи брокера, потом код.
- Пароль живёт в переменных окружения и никогда не попадает в логи.
Домашнее задание
Соберите модуль orderservice с amqp091-go: подключайтесь к брокеру из переменной окружения RABBITMQ_URL с таймаутом пять секунд, печатайте состояние IsClosed и доведите до ошибки connection refused, остановив контейнер. Потом запустите брокера снова и убедитесь, что клиент подключается без правок кода.
Личные заметкиПусто
Вопросы и ответы по уроку
Нужен ли отдельный пакет для аутентификации в amqp091-go?
Нет: логин и пароль передаются прямо в URL подключения, и Dial передаёт их брокеру на этапе согласования безопасности. Для особых схем аутентификации у amqp.Config есть поле SASL, но в типовом проекте достаточно пары user:pass в строке подключения.
Почему Dial зависает, хотя брокер вроде доступен?
Без явного таймаута Dial упирается в таймауты операционной системы, которые на сетевых проблемах тянут минуты. Ставьте amqp.DefaultDial с разумным пределом — единицы секунд; если соединение не прошло за это время, значит проблема в сети, файрволе или адресе, а не в коде.
Куда деть пароль, если URL нельзя коммитить?
В переменную окружения или в файл конфигурации вне git, а в compose-файле — в environment сервиса. В логах печатайте только хост, порт и имя пользователя; сами ошибки amqp.Dial пароль не раскрывают, но строку подключения целиком логировать всё равно не стоит.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Изучайте дальше
Следующая ступень учебного пути
Excel в Go: чтение и генерация xlsx на excelize
Курс excelize на Go: создание xlsx, чтение, стили, формулы, диаграммы и StreamWriter для больших файлов. 25 уроков с примерами кода и проектом.
- Создавать и читать xlsx-файлы средствами excelize
- Работать с листами, ячейками, строками и столбцами
- Оформлять таблицы: стили, шрифты, границы, форматы чисел и дат
- Вставлять формулы, диаграммы и изображения