cron для Go: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Библиотека 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. Это критически важно для

Рекомендаций