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

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

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

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

go-redis v9: установка и первое подключение

go-redis превращает подключение к Redis из Go в три шага: go get, redis.NewClient и один Ping для проверки связи. В этом уроке поднимем сервер в Docker, соберём рабочий main.go с полями Options и разберём первые ошибки — от connection refused до пустого ответа. Скелет, который здесь получится, будет повторяться во всех следующих уроках, поэтому соберём его аккуратно.

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

Точка старта: пустая папка, установленный Go и желание к вечеру положить в Redis первую строку. Чего не хватает — сервера Redis и клиентской библиотеки в модуле. Сервер собирать из исходников не будем: официальный образ в Docker поднимает полноценный Redis одной командой, и для разработки этого достаточно почти всегда — отдельная установка как системный сервис нужна, только если вы хотите жить без Docker. С клиентом есть ловушка, на которой спотыкаются все, кто пришёл по старым статьям: имя модуля менялось дважды, поэтому в сети легко найти импорты вида github.com/go-redis/redis, github.com/go-redis/redis/v8 и github.com/redis/go-redis/v9 — живым из них сейчас только последний. В этом уроке поставим правильную версию, напишем первое подключение с Ping и научимся читать ошибки, которые выдаёт клиент, когда сервер недоступен. И сразу честно о формате: запускать код будем локально — браузерная песочница сайта сеть не умеет, ей нужен чистый Go без пакетов.

Установка: go get github.com/redis/go-redis/v9#

Установка клиента — одна команда go get, как для любой библиотеки Go. Если модуля в папке ещё нет, сначала go mod init с именем модуля, например example.com/cache: без go.mod менеджер зависимостей не знает, куда записывать пакет. Сам путь импорта читается так: домен redis на GitHub — организация, в которую переехал проект в 2022 году; go-redis — репозиторий; /v9 — мажорная версия по правилам семантического версионирования Go, без неё компилятор не найдёт пакет. Имя пакета при этом осталось прежним — redis: импортируете github.com/redis/go-redis/v9, а в коде обращаетесь redis.NewClient, без алиасов и переименований. После go get в go.mod появится строка с версией вида v9.x.x, а в коде — готовый к использованию пакет. Старые туториалы с восьмой версией компилируются редко и учат API без контекстов, поэтому копировать импорты из статей до 2022 года — плохая идея.

Сервер в Docker и клиент в модуле

GO
docker run --name go-redis-course -p 6379:6379 -d redis:7

go mod init example.com/cache
go get github.com/redis/go-redis/v9

Первая команда поднимает официальный образ Redis седьмой версии и пробрасывает порт 6379 на localhost — после неё сервер уже принимает подключения. go mod init создаёт модуль, go get скачивает клиент v9 и фиксирует его в go.mod. Живость сервера проверяют без Go: docker exec -it go-redis-course redis-cli ping ответит PONG.

Подключение к Redis из Go: redis.NewClient#

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

main.go с Ping целиком

GO
package main

import (
    "context"
    "fmt"

    "github.com/redis/go-redis/v9"
)

func main() {
    rdb := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "", // пароль не задан
        DB:       0,
    })

    ctx := context.Background()

    pong, err := rdb.Ping(ctx).Result()
    if err != nil {
        panic(err)
    }

    fmt.Println(pong) // PONG
}

redis.NewClient принимает указатель на redis.Options: Addr — адрес и порт сервера, Password — пароль, если он задан, DB — номер логической базы от 0 до 15. Ping отправляет серверу одноимённую команду и возвращает StatusCmd, из которой метод Result достаёт строку и ошибку: живой сервер отвечает PONG. Если err не nil — связи нет, работать дальше бессмысленно. Импорт обязательно с /v9 на конце.

Основные поля redis.Options#

  • Addr — адрес и порт сервера, по умолчанию localhost:6379.
  • Password — пароль авторизации; пустая строка значит работать без него.
  • DB — номер логической базы: по умолчанию Redis держит 16 изолированных нумерованных баз.
  • DialTimeout — сколько ждать установления соединения, прежде чем сдаться.
  • ReadTimeout и WriteTimeout — дедлайны на чтение и запись в сокет.
  • PoolSize — максимум соединений в пуле; подробный разбор пула ближе к двадцатому уроку.

Типичная ошибка: клиент go-redis на каждый запрос#

Типичная ошибка, которая проявляется не сразу: redis.NewClient вызывается в хендлере при каждом обращении. Несколько дней ничего не происходит, потом сервис начинает жить странно — растёт число открытых сокетов, в логах мелькают задержки на установку связи, а в пики соединения упираются в лимит операционной системы. Причина в том, что каждый клиент тащит собственный пул, и большинство его соединений простаивает: на сотню одновременных запросов приходит сотня пулов по несколько связей. Клиент в go-redis потокобезопасен именно затем, чтобы его создавали один раз — на старте приложения, в main или в конструкторе зависимостей — и передавали всем, кому нужен доступ. Отдельный клиент оправдан разве что для второго сервера или изолированной задачи с другими таймаутами. Увидели NewClient вне инициализации — вопрос на code review: зачем?

Спорное мнение: паника при старте — честное решение#

Спорное мнение из моих привычек: panic на провале первого Ping — нормально, и убирать его из скелета не нужно. Скрипт или утилита без Redis бессмысленны, и честнее упасть сразу с понятной ошибкой, чем отрабатывать впустую и падать дальше с запутанным стеком: фейл-фаст экономит время разбора. Граница применимости — долгоживущие сервисы: там паника на старте превращается в цикл рестартов, поэтому вместо неё ставят ретраи с дедлайном, а после порога всё-таки паникуют, чтобы оркестратор перезапустил контейнер. В HTTP-хендлерах панике вообще не место: живость проверяет Ping на /healthz, и он же даёт балансировщику сигнал вывести под из ротации. Одно правило объединяет все три мира: ошибки старта нельзя глотать — сервис, который притворяется живым, вредит больше упавшего.

Ленивое подключение и Close#

redis.NewClient не устанавливает соединение в момент вызова: клиент создаётся мгновенно, а первое реальное подключение произойдёт при первой команде. Это удобно — конфигурацию можно строить заранее, а живость сервера проверять отдельно; для этого и существует Ping, дешёвый health-check, который часто вешают на эндпоинт /healthz. Пул соединений клиент открывает и закрывает сам: если сервер ночью перезапустился, он восстановит связи без вашего участия — переподключение его работа, а не ваша. Ваш долг один — закрыть клиент при завершении приложения через rdb.Close(), чаще всего с defer сразу после создания. В тестах этот же факт спасает: NewClient с несуществующим адресом не упадёт до первой команды, поэтому конструктор спокойно живёт в setup-функции.

Кейс из продакшена: Redis поднялся, а Ping не проходит#

Кейс, который ломает уверенность в скелете урока: docker-compose поднимает приложение и Redis вместе, контейнер базы уже зелёный, а Ping в приложении всё равно падает с connection refused. Причина — два разных события: контейнер запущен и сервер готов принимать команды. Redis инициализирует свои структуры не мгновенно, и в этот промежуток порт либо ещё не слушается, либо слушается с отказами. Лечится по-разному в зависимости от окружения: в docker-compose есть depends_on с условием service_healthy и healthcheck, в Kubernetes — readiness-пробы и init-контейнеры, в простом скрипте — цикл с ретраями и паузой. Смысл один: старт приложения не должен зависеть от того, успел ли сосед приготовиться. Пара ретраев на Ping с задержкой в полсекунды почти всегда закрывает вопрос и стоит три строки кода. Без них старт иногда проходит, а иногда падает — самая неприятная разновидность бага, воспроизводимая только на деплое.

Контекст в каждом вызове: отличие v9#

В v9 каждый метод клиента первым параметром принимает context.Context — принципиальное отличие от восьмой версии, где контекста не было вовсе. Контекст даёт единую механику отмены и таймаутов: если клиент запроса ушёл, отменяется и обращение к Redis; если сервер завис, context.WithTimeout оборвёт ожидание и вернёт ошибку дедлайна. В примерах курса стоит context.Background() — контекст без дедлайна, нейтральный для скриптов; в реальном HTTP-сервисе вместо него передают контекст запроса, полученный от хендлера, а точечные таймауты навешивают там, где важна скорость ответа. Привычка одна: контекст — не формальность, а ручка, за которую операцию можно выключить. Мертвой и живой сети с точки зрения кода не отличить, и спасает только дисциплина: подробный разбор — в отдельном уроке про контекст и таймауты.

Проверка: Ping

Что вернёт rdb.Ping(ctx).Result() на живом сервере?

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

Когда redis.NewClient открывает сетевое соединение?

Проверка: недоступный сервер

Сервер не запущен. Что вернёт Ping?

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

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

rdb := redis.NewClient(&redis.Options{
    Addr: "localhost:6379",
})
ctx := context.Background()

pong, err := rdb.Ping(ctx).Result()
fmt.Println(pong, err)

Соберите конфигурацию клиента

Допишите конструктор, стандартный адрес, нулевую базу и команду проверки связи.

rdb := redis.(&redis.Options{ Addr: "", Password: "", DB: , }) pong, err := rdb.(ctx).Result()

Соберите минимальный main()

Расставьте строки минимальной программы: контекст, клиент, закрытие, вывод Ping.

Строки

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

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

Карточки: установка и подключение

Термин

go-redis v9

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

Актуальная версия клиента: импорт github.com/redis/go-redis/v9, пакет называется redis, каждая команда принимает context.Context.

1 / 6

Скелет готов: сервер в Docker, клиент в go.mod, PONG в консоли. Дальше начинается содержательная часть — в уроке про типы данных Redis разберём пять структур и выбор под задачу; полный план из двадцати пяти уроков — на странице курса. Если чувствуете, что подводит база — go.mod, обработка ошибок, контекст — вернитесь к базовому курсу Go: всё дальнейшее опирается именно на него. Напомню про формат: весь код курса запускается только локально, песочница не поднимает ни Docker, ни TCP, поэтому домашнее задание выполняется на вашей машине. Если цепочка где-то порвалась — возвращайтесь к врезке про connection refused: это самая частая точка старта и на курсе, и в реальных проектах.

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

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

Вывод

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

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

  • Установка: go mod init, затем go get github.com/redis/go-redis/v9 — путь с /v9 обязателен.
  • Сервер для разработки поднимает Docker: docker run -p 6379:6379 -d redis:7.
  • redis.NewClient(&redis.Options{Addr, Password, DB}) создаёт потокобезопасный клиент один раз на приложение.
  • Клиент ленив: первое соединение откроется на первой команде; живость проверяет Ping → PONG.
  • v9 требует context.Context в каждой команде — отличие от v8 и рычаг для таймаутов.
  • connection refused — не запущен сервер или забыт проброс порта в Docker.
  • Код курса запускается локальным Go: песочница сети не умеет.

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

Соберите скелет урока по памяти: docker run с пробросом порта, go mod init, go get, main.go с NewClient и Ping. Запустите локально и убедитесь, что в консоли PONG. Затем сломайте связку специально — остановите контейнер docker stop go-redis-course — и посмотрите на ошибку connection refused своими глазами: её надо узнавать в лицо.

Личные заметкиПусто
Хранятся только в вашем браузере (Redis · go-redis v9: установка и первое подключение)

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

Нужен ли пароль для локального Redis в Docker?

Нет: официальный образ без флагов поднимается без авторизации, поэтому в Options достаточно пустой строки Password. Пароль задают флагом --requirepass и файлом конфигурации в продакшене — тогда клиент передаёт его в поле Password, а без него сервер отвечает ошибкой NOAUTH.

Что такое DB 0-15 и зачем нужны нумерованные базы?

Redis по умолчанию держит 16 логически изолированных пространств ключей; клиент выбирает их полем DB в Options. Для разделения окружений это плохая практика — берите разные инстансы или префиксы ключей; нумерованные базы остаются наследием и удобны в маленьких проектах.

Почему NewClient не падает, если Redis ещё не запущен?

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

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

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

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

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

JWT-аутентификация в Go: golang-jwt и bcrypt

Разбираем JWT в Go: устройство токена, golang-jwt v5, подписи HMAC и RSA, refresh-токены, bcrypt, middleware авторизации и лучшие практики.

  • Понимать анатомию JWT и почему payload не зашифрован
  • Выпускать и проверять токены через golang-jwt v5
  • Хешировать пароли bcrypt и сравнивать безопасно
  • Писать middleware аутентификации для chi и Gin