Библиотека cron для Go: Полное руководство
1. Что это и зачем нужна
Библиотека cron для Go (репозиторий github.com/robfig/cron/v3) — это мощный и гибкий планировщик задач, написанный на чистом Go. Она предоставляет разработчикам простой, но выразительный способ запуска функций (задач) по расписанию, используя стандартный синтаксис cron-выражений (как в Unix-системах). Зачем это нужно? В современной разработке серверных приложений, микросервисов и утилит командной строки часто возникает необходимость выполнять повторяющиеся действия: очистка кэша, отправка email-рассылок, создание резервных копий, синхронизация данных с внешними API, сбор метрик и многое другое. Вместо того чтобы писать сложные циклы с time.Sleep() или полагаться на внешние системные cron-демоны (которые не всегда доступны в контейнерных средах, таких как Docker или Kubernetes), библиотека cron позволяет встроить планировщик непосредственно в ваше Go-приложение.
Ключевое преимущество cron перед аналогами (например, time.Ticker) — это поддержка сложных расписаний. Вы можете задать задачу, которая будет выполняться "в 3:15 утра каждый вторник" или "каждые 5 минут с 9 до 17 часов в будние дни" с помощью одной строки. Библиотека также поддерживает дополнительные расширения, такие как секунды (стандартный cron их не поддерживает), именованные расписания (@every 5s, @daily, @weekly) и безопасную остановку планировщика через контекст. Версия v3, которая является текущей стабильной, полностью переработана для потокобезопасности и корректной обработки паник в задачах, что делает её идеальным выбором для production-систем.
На PythonLib мы стремимся предоставлять лучшие руководства по современным инструментам. Данная статья — исчерпывающее руководство по библиотеке cron для Go. Вы узнаете, как её установить, как создавать и управлять задачами, как обрабатывать ошибки и как строить надёжные системы с фоновыми процессами. Мы разберём все основные методы, приведём реальные примеры кода и дадим советы по производительности.
2. Установка
Установка библиотеки cron для Go выполняется стандартным способом через менеджер пакетов Go (go modules). Убедитесь, что у вас установлен Go версии 1.16 или выше (рекомендуется 1.21+). Выполните следующую команду в корне вашего проекта (там, где находится файл go.mod):
go get github.com/robfig/cron/v3@latest
Эта команда скачает библиотеку и добавит её в ваш go.mod и go.sum. После этого вы можете импортировать её в своём коде:
import "github.com/robfig/cron/v3"
Если вы используете старую версию Go (1.15 и ниже) или хотите установить конкретную версию (например, v3.0.1), укажите её явно:
go get github.com/robfig/cron/v3@v3.0.1
Важно: Библиотека не имеет внешних зависимостей, кроме стандартной библиотеки Go. Это делает её лёгкой и безопасной для включения в любой проект.
3. Быстрый старт — минимальный рабочий пример
Давайте создадим простейший планировщик, который будет печатать "Hello, World!" каждую минуту. Этот пример демонстрирует основные концепции: создание планировщика, добавление задачи и её запуск.
package main
import (
"fmt"
"time"
"github.com/robfig/cron/v3"
)
func main() {
// 1. Создаём новый планировщик cron с часовым поясом UTC
c := cron.New()
// 2. Добавляем задачу, которая выполняется каждую минуту
// Выражение "* * * * *" означает: каждая минута каждого часа каждого дня
_, err := c.AddFunc("* * * * *", func() {
fmt.Println("Hello, World! Текущее время:", time.Now().Format(time.RFC3339))
})
if err != nil {
fmt.Println("Ошибка добавления задачи:", err)
return
}
// 3. Запускаем планировщик в фоновом режиме (в отдельной горутине)
c.Start()
// 4. Даём программе поработать 2 минуты, чтобы увидеть выполнение задачи
time.Sleep(2 * time.Minute)
// 5. Останавливаем планировщик (ждём завершения всех активных задач)
ctx := c.Stop()
<-ctx.Done()
fmt.Println("Планировщик остановлен.")
}
Запустите программу. Вы увидите, что сообщение "Hello, World!" печатается один раз в минуту. Через 2 минуты программа завершится. Обратите внимание на метод Stop() — он возвращает контекст, который завершится, когда все запущенные задачи закончат своё выполнение. Это гарантирует корректное завершение программы.
4. Основные методы и функции
Библиотека cron предоставляет богатый API для управления задачами. Ниже приведены 8 ключевых методов и функций с описанием и примерами.
4.1 cron.New()
Сигнатура: func New(opts ...Option) *Cron
Что делает: Создаёт новый экземпляр планировщика Cron. Принимает опциональные параметры (опции), такие как WithSeconds(), WithLocation(), WithParser(), WithLogger() и WithChain(). Без опций планировщик использует стандартное 5-польное cron-выражение (без секунд) и часовой пояс UTC.
// Пример: создание планировщика с поддержкой секунд и часовым поясом Москвы
loc, _ := time.LoadLocation("Europe/Moscow")
c := cron.New(
cron.WithSeconds(), // Включаем поддержку 6-польного выражения (с секундами)
cron.WithLocation(loc), // Устанавливаем часовой пояс
)
4.2 (c *Cron) AddFunc()
Сигнатура: func (c *Cron) AddFunc(spec string, cmd func()) (EntryID, error)
Что делает: Добавляет новую задачу в планировщик. Принимает cron-выражение (spec) и функцию (cmd), которая будет выполняться. Возвращает уникальный идентификатор задачи (EntryID) и ошибку, если выражение невалидно. Это самый простой способ добавить задачу.
c := cron.New()
// Задача выполняется каждые 10 секунд (требуется WithSeconds())
id, err := c.AddFunc("*/10 * * * * *", func() {
fmt.Println("Прошло 10 секунд")
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Задача добавлена с ID: %d\n", id)
4.3 (c *Cron) AddJob()
Сигнатура: func (c *Cron) AddJob(spec string, cmd Job) (EntryID, error)
Что делает: Добавляет задачу, реализующую интерфейс Job. Интерфейс Job содержит один метод Run(). Это более гибкий способ по сравнению с AddFunc, так как позволяет инкапсулировать состояние и зависимости внутри структуры, реализующей Job.
// Определяем структуру, реализующую интерфейс Job
type GreetingJob struct {
Name string
}
func (g GreetingJob) Run() {
fmt.Printf("Привет, %s!\n", g.Name)
}
func main() {
c := cron.New()
// Добавляем задачу, которая выполняется каждую минуту
c.AddJob("@every 1m", GreetingJob{Name: "Алиса"})
c.Start()
time.Sleep(2 * time.Minute)
c.Stop()
}
4.4 (c *Cron) Start()
Сигнатура: func (c *Cron) Start()
Что делает: Запускает планировщик в фоновом режиме. Планировщик начинает отслеживать время и запускать задачи в соответствии с их расписанием. Этот метод неблокирующий — он запускает внутреннюю горутину и возвращает управление немедленно. Если планировщик уже запущен, повторный вызов Start() игнорируется.
c := cron.New()
c.AddFunc("@every 5s", func() { fmt.Println("Tick") })
c.Start() // Запускаем
// Программа продолжает работу...
time.Sleep(20 * time.Second)
c.Stop()
4.5 (c *Cron) Stop()
Сигнатура: func (c *Cron) Stop() context.Context
Что делает: Останавливает планировщик. Возвращает контекст (context.Context), который будет завершён (вызовет ctx.Done()), когда все запущенные в данный момент задачи завершат своё выполнение. Это важно для graceful shutdown. После вызова Stop() планировщик больше не запускает новые задачи, но даёт текущим задачам закончить работу.
c := cron.New()
c.AddFunc("@every 1s", func() {
fmt.Println("Работаю...")
time.Sleep(3 * time.Second) // Долгая задача
fmt.Println("Закончил")
})
c.Start()
time.Sleep(2 * time.Second)
ctx := c.Stop() // Запрашиваем остановку
<-ctx.Done() // Ждём, пока все задачи завершатся
fmt.Println("Все задачи завершены, программа закрывается")
4.6 (c *Cron) Entries()
Сигнатура: func (c *Cron) Entries() []Entry
Что делает: Возвращает срез ([]Entry) всех задач, зарегистрированных в планировщике. Каждый элемент Entry содержит информацию о задаче: ID, Schedule (расписание), Next (следующее время запуска), Prev (предыдущее время запуска) и Job (саму задачу). Полезно для отладки и мониторинга.
c := cron.New()
c.AddFunc("@every 1m", func() { fmt.Println("Задача 1") })
c.AddFunc("@every 5m", func() { fmt.Println("Задача 2") })
c.Start()
// Получаем список задач
for _, entry := range c.Entries() {
fmt.Printf("ID: %d, Следующий запуск: %s\n", entry.ID, entry.Next.Format(time.RFC3339))
}
4.7 (c *Cron) Remove()
Сигнатура: func (c *Cron) Remove(id EntryID)
Что делает: Удаляет задачу из планировщика по её идентификатору (EntryID). Если задача с таким ID не найдена, метод ничего не делает. После удаления задача больше не будет запускаться. Это позволяет динамически управлять задачами во время работы приложения.
c := cron.New()
id, _ := c.AddFunc("@every 2s", func() {
fmt.Println("Эта задача будет удалена")
})
c.Start()
time.Sleep(5 * time.Second)
fmt.Println("Удаляем задачу...")
c.Remove(id) // Задача больше не выполняется
time.Sleep(5 * time.Second)
c.Stop()
4.8 cron.WithSeconds()
Сигнатура: func WithSeconds() Option
Что делает: Опция для cron.New(), которая включает поддержку 6-польного cron-выражения (секунды, минуты, часы, день месяца, месяц, день недели). Стандартный cron использует 5 полей (без секунд). Если вы хотите задавать расписание с точностью до секунды, используйте эту опцию.
// Без WithSeconds() выражение "* * * * *" — каждую минуту
// С WithSeconds() выражение "* * * * * *" — каждую секунду!
c := cron.New(cron.WithSeconds())
c.AddFunc("*/5 * * * * *", func() {
fmt.Println("Каждые 5 секунд")
})
c.Start()
time.Sleep(15 * time.Second)
c.Stop()
4.9 cron.WithLocation()
Сигнатура: func WithLocation(loc *time.Location) Option
Что делает: Опция для cron.New(), которая устанавливает часовой пояс для планировщика. Все cron-выражения будут интерпретироваться относительно этого часового пояса. По умолчанию используется UTC. Это критически важно для