Планировщик задач на Go (Golang): от cron до asynq
В современной серверной разработке на Go (Golang) часто возникает необходимость выполнять код по расписанию или с задержкой. Очистка кэша, рассылка уведомлений, генерация отчётов — все это требует надёжного планировщика задач. В этой статье мы подробно разберём три популярных подхода: стандартный пакет cron, легковесную библиотеку gocron и распределённую очередь asynq. Вы узнаете, какой инструмент выбрать для вашего проекта на PythonLib.
1. Стандартный cron для Go
Библиотека github.com/robfig/cron/v3 — это де-факто стандарт для планирования повторяющихся задач в Go. Она использует знакомый синтаксис cron-выражений (5 полей) и поддерживает секунды, часовые пояса и recovery от паник.
Установка и базовый пример
go get github.com/robfig/cron/v3
Пример планировщика, который запускает задачу каждые 30 секунд:
package main
import (
"fmt"
"github.com/robfig/cron/v3"
)
func main() {
c := cron.New()
// Добавляем задачу с cron-выражением: каждые 30 секунд
_, err := c.AddFunc("*/30 * * * * *", func() {
fmt.Println("Задача выполнена:", time.Now())
})
if err != nil {
panic(err)
}
c.Start()
// Блокируем main горутину
select {}
}
Ключевые особенности:
- Поддержка стандартных cron-выражений (5 или 6 полей с секундами)
- Автоматическое восстановление после паники в задаче
- Возможность задать часовой пояс через
cron.WithLocation() - Логирование через
cron.WithLogger()
Продвинутое использование: цепные джобы
c := cron.New(cron.WithSeconds())
// Задача с именем для управления
id, _ := c.AddFunc("@every 5m", func() {
fmt.Println("Выполняется каждые 5 минут")
})
// Удаление задачи по ID
c.Remove(id)
// Остановка планировщика
c.Stop()
Библиотека идеально подходит для простых периодических задач внутри одного процесса. Однако для распределённых систем (несколько реплик Go-сервера) потребуется внешняя блокировка, чтобы задача не выполнялась одновременно на всех нодах.
2. Gocron — лёгкий шедулер с цепочками
github.com/go-co-op/gocron предлагает более современный API без cron-выражений. Вместо строк вы используете цепочки методов. Это особенно удобно для новичков.
Пример с gocron
package main
import (
"fmt"
"time"
"github.com/go-co-op/gocron"
)
func main() {
s := gocron.NewScheduler(time.UTC)
// Задача выполняется каждые 2 минуты
_, _ = s.Every(2).Minutes().Do(func() {
fmt.Println("Прошло 2 минуты")
})
// Задача с задержкой после старта
_, _ = s.Every(1).Day().At("10:30").Do(func() {
fmt.Println("Ежедневно в 10:30")
})
s.StartBlocking()
}
Преимущества gocron:
- Человекочитаемый API:
.Every(10).Seconds() - Встроенная поддержка распределённых блокировок через Redis (опционально)
- Возможность задать лимит на количество запусков
- Глобальные и локальные обработчики ошибок
Если вам нужно простое решение для микросервиса без внешних зависимостей — gocron станет отличным выбором. Он легче cron'а и не требует знания синтаксиса cron-выражений.
3. Asynq — распределённая очередь отложенных задач
github.com/hibiken/asynq — это не просто планировщик, а полноценная система очередей на базе Redis. Она позволяет откладывать задачи, задавать приоритеты, повторять сбойные задачи и отслеживать их статус через веб-интерфейс.
Архитектура asynq
Asynq состоит из двух частей: клиента (отправляет задачи) и воркера (выполняет). Оба используют Redis как брокер сообщений.
// Клиент — отправка задачи
client := asynq.NewClient(asynq.RedisClientOpt{Addr: "localhost:637