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

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

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

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

Спецсимволы: звёздочка, слэш, диапазоны и @daily

Спецсимволы cron превращают пять полей из загадки в точный инструмент: звёздочка означает «каждое значение», слэш задаёт шаг, дефис — диапазон, запятая — список, а дескрипторы вроде @daily и @every 1h30m заменяют собой целые выражения. В этом уроке пройдём по каждому символу на примерах AddFunc, поймём, к чему привязан шаг, и увидим, чем интервальные расписания отличаются от сетки часов.

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

Выражение из одних звёздочек — самая неточная команда, какую можно дать планировщику: «каждую минуту, всегда». Реальные задачи почти всегда требуют сужения: не каждую минуту, а каждые десять; не каждый день, а по будням; не в произвольный момент, а в полдень. Для этого в синтаксисе есть четыре спецсимвола и один особый случай — дескрипторы с собакой, которые robfig/cron поддерживает поверх классики. Символы не заучиваются: их ровно четыре, каждый читается одним словом, а путаница возникает в двух местах — к чему привязан шаг и чем @every отличается от выражения с полем часов. Эти два вопроса мы разберём отдельно и с примерами, потому что именно на них строятся неверные ожидания: задача запускается в правильные, но не те моменты, и код при этом безупречен. Хорошая новость в том, что словарь конечен: освоив восемь конструкций этого урока, вы сможете записать практически любое расписание, которое встретите в требованиях.

Звёздочка и слэш: шаг по расписанию#

Звёздочка в поле означает весь диапазон значений этого поля: каждая минута, каждый час, каждое число. Сама по себе она редко встречается во всех пяти полях — чаще её комбинируют, оставляя звёздочкой то, что не ограничиваем. Слэш задаёт шаг: выражение */5 в поле минут — это каждая пятая минута по сетке часа, то есть 00, 05, 10 и так до 55. Ключевая деталь, которую многие пропускают: шаг отсчитывается от начала поля, а не от момента запуска планировщика. Процесс мог подняться в 13:42, но задача с шагом в десять минут всё равно пойдёт в 13:50, а не через десять минут после старта. Сетка стабильна и одинакова у всех экземпляров сервиса — это удобно для задач, где важна синхронность: отчёты собираются по одним и тем же границам, какие бы рестарты ни случались. Минимальный шаг ограничен полем: в минутах меньше единицы не бывает, для более частой работы нужен секундный режим из отдельного урока.

Шпаргалка спецсимволов#

СимволЧитается какПример в поле минутЧто означает пример
звёздочкакаждое значениезвёздочкакаждая минута часа
слэшшаг*/500, 05, 10 — каждую пятую минуту
дефисдиапазон10-20с 10-й по 20-ю минуту включительно
запятаясписок0,15,30,45четыре момента каждый час
собакадескриптор@hourlyв начале каждого часа

Диапазоны, списки и имена полей#

Дефис превращает два числа в диапазон: 9-17 в поле часов — это с девяти утра до пяти вечера включительно. Запятая перечисляет значения: 0,15,30,45 — четыре момента каждый час. Оба приёма работают в любом поле и свободно комбинируются с шагом и звёздочкой, а в полях месяца и дня недели вместо чисел разрешены имена: JAN-DEC и MON-SUN — регистр не важен. Имена — не украшение: строка с MON-FRI читается без шпаргалки, а выражение с числом 1 в последнем поле постоянно провоцирует вопрос «понедельник или воскресенье?». Внутри диапазона и списка можно мешать имена с числами — парсер приведёт их к одному виду. Единственное правило вкуса: не собирайте в одном выражении всё сразу. Когда в строке одновременно живут шаг, диапазон, список и имена, она перестаёт читаться на ревью, и проще разбить задачу на две с двумя простыми выражениями — планировать их можно по-прежнему в одном планировщике.

Комбинируем спецсимволы в AddFunc

GO
// каждые 10 минут в рабочие часы по будням
c.AddFunc("*/10 9-17 * * MON-FRI", healthProbe)

// полдень и шесть вечера по выходным
c.AddFunc("0 12,18 * * SAT,SUN", weekendDigest)

// с 10-й по 45-ю минуту шагом в пять
c.AddFunc("10-45/5 * * * *", reportChunk)

// интервал без привязки к часам
c.AddFunc("@every 90m", syncRates)

Первая строка — типовой продакшен-профилинг: шаг поверх диапазона часов, будни в поле дней. Вторая показывает список в поле часов и список дней: поля независимы, задача идёт на пересечении. Третья — шаг, применённый к диапазону минут: 10, 15, 20 и так до 45, после чего тишина до следующего часа. Четвёртая — интервальный дескриптор, о нём подробнее ниже.

Шаг поверх диапазона и особый случай N-со-слэшем#

Шаг можно вешать не только на звёздочку, но и на диапазон: 10-45/5 в поле минут означает 10, 15, 20 и так до 45 — включительно, потому что цикл парсера идёт до конца диапазона. Это удобно для окон: полчаса интенсивной проверки внутри часа, потом тишина. Отдельного упоминания заслуживает форма из одного числа и слэша. В robfig/cron v3 она документирована прямо в исходнике парсера: N/шаг трактуется как N-максимум/шаг, то есть 3/10 в минутах — это 3, 13, 23 и далее до 53. Не все планировщики трактуют эту форму одинаково, поэтому в переносимых конфигах надёжнее писать диапазон целиком: 3-59/10 говорит без двусмысленностей и читается одинаково у всех. Правило то же, что с ИЛИ в дневных полях: неоднозначные формы лучше не использовать не потому, что они сломаны, а потому что их смысл варьируется от системы к системе, а расписания живут дольше, чем команда, которая их писала.

Дескрипторы: @daily, @hourly и @every#

Поверх пяти полей robfig/cron v3 поддерживает дескрипторы — читаемые сокращения для типовых расписаний. @yearly и его синоним @annually — раз в год, первого января в полночь; @monthly — первое число каждого месяца; @weekly — воскресенье в полночь; @daily и @midnight — каждый день в 00:00; @hourly — в начале каждого часа. Дескриптор разворачивается в обычное выражение в зоне планировщика, поэтому @daily в сервисе с московской зоной идёт в полночь по Москве — про пояса подробно в уроке про часовые пояса. Отдельно стоит @every — единственный дескриптор, который не привязан к часам вообще: после него пишется длительность в формате time.Duration, например @every 1h30m или @every 90m, и задача ходит с этим интервалом. Минимальный шаг — одна секунда, меньшие значения парсер округлит вверх. Дескрипторы не добавляют новых возможностей — то же самое пишется пятью полями, — но выражение «@daily» читается быстрее, чем пять нулей и звёздочек, а в конфигах читаемость напрямую снижает число ошибок. Внутри библиотеки каждый дескриптор собирает ту же битовую маску значений поля, что и разобранная строка: разницы в поведении и производительности нет.

Шесть дескрипторов одним списком

GO
c.AddFunc("@yearly", archiveYear)      // 1 января, 00:00
c.AddFunc("@annually", archiveYear)    // синоним @yearly
c.AddFunc("@monthly", billCustomers)   // 1-го числа, 00:00
c.AddFunc("@weekly", weeklyDigest)     // воскресенье, 00:00
c.AddFunc("@daily", cleanupSessions)   // каждый день, 00:00
c.AddFunc("@midnight", cleanupSessions) // синоним @daily
c.AddFunc("@hourly", refreshCache)     // в начале каждого часа
c.AddFunc("@every 1h30m", syncRates)   // интервал, без привязки к часам

Все дескрипторы принимаются тем же AddFunc и тем же парсером — ParseStandard тоже их понимает, так что проверка выражений из конфига покрывает и этот случай. Синонимы @annually и @midnight даны ради читаемости: выбирайте одну форму и держите её по всему проекту, иначе конфиг превратится в коллекцию стилей.

Когда дескриптор, а когда выражение#

  • Суточные и более редкие запуски в полночь — @daily и @weekly: читается мгновенно, ошибиться негде.
  • Запуск «в начале часа» — @hourly, если вам не нужен контроль над минутой.
  • Неизвестный заранее интервал из конфига — @every с Duration: парсинг расписания не нужен.
  • Конкретное время, шаги, окна и дни недели — только пять полей: дескриптор «в 09:30» не умеет.
  • Смешивать стили в одном конфиге можно, но лучше выбрать один стиль расписаний на проект.

Проверка: привязка шага

Процесс стартовал в 13:42. Когда выполнится задача с выражением */10 в поле минут?

Проверка: @every против поля часов

Чем @every 1h отличается от выражения с нулевой минутой каждый час?

Во сколько выполнится задача?

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

c := cron.New()
_, err := c.AddFunc("@daily", nightlyReport)
if err != nil {
        log.Fatal(err)
}
c.Start()

// процесс стартовал в 14:00
// во сколько выполнится nightlyReport?

Соберите три расписания

Впишите звёздочку, диапазон будней и ключевое слово интервального дескриптора.

c.AddFunc("/15 9-17 * * ", officePing) c.AddFunc("@ 1h30m", syncRates)

Соберите регистрацию полечасовой проверки

Расставьте строки в порядке выполнения: от создания планировщика до вечного ожидания в main.

Строки

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

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

Карточки: спецсимволы и дескрипторы

Термин

Звёздочка

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

Весь диапазон значений поля: каждая минута, каждый час. Комбинируется со шагом и остальными символами.

1 / 6

Четыре спецсимвола и шесть дескрипторов закрывают почти весь словарь расписаний, который нужен сервису: шаги, окна, списки и читаемые сокращения. Дальше выражения станут параметром задач: в уроке про AddFunc разберём, какие функции годятся в задачи, как передавать им параметры через замыкания и почему тело задачи должно быть маленьким. Если хочется сразу увидеть дескрипторы в деле против сетки часов — загляните в урок про сравнение с Ticker, там эта разница решает выбор инструмента. Про секунды и шестипольные конфиги из старых статей — урок про NewWithSeconds. В домашнем задании — перевод человеческих формулировок в выражения со спецсимволами и проверка себя через ParseStandard.

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

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

Вывод

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

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

  • Звёздочка — весь диапазон поля; слэш — шаг от начала поля, а не от старта процесса.
  • Дефис даёт диапазон включительно, запятая — список; в месяцах и днях недели работают имена JAN-DEC и MON-SUN.
  • Шаг вешается и на диапазон: 10-45/5 — это 10, 15, 20 и так до 45; форма N/шаг в robfig v3 означает N-максимум/шаг.
  • Дескрипторы @yearly, @monthly, @weekly, @daily, @hourly — читаемые замены типовых выражений в зоне планировщика.
  • @every — интервал Duration от предыдущего срабатывания: плывёт после рестартов и не привязан к круглым часам.
  • Для отчётов и сравнимых данных выбирайте сетку часов; @every — для фоновой подметки без привязки ко времени суток.

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

Переведите пять человеческих формулировок в выражения: «каждые двадцать минут по будням с 8 до 20», «полночь воскресенья», «полдень и полночь по выходным», «окно с 10 по 40 минуту шагом в десять», «раз в сорок пять минут без привязки к часам». Проверьте все пять через cron.ParseStandard в отдельной программе, затем зарегистрируйте их в одном планировщике из урока и убедитесь по логу, что первые тики совпали с ожиданиями.

Личные заметкиПусто
Хранятся только в вашем браузере (Cron · Спецсимволы: звёздочка, слэш, диапазоны и @daily)

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

Чем @daily лучше выражения 0 0 * * *?

Только читаемостью: дескриптор разворачивается ровно в это выражение. В коде и конфигах @daily читается без декодирования полей, но как только появится время отличное от полуночи, дескриптор придётся заменить на пять полей — некоторые команды сразу пишут выражения ради единого стиля.

Почему шаг не совпадает с моментом запуска процесса?

Шаг отсчитывается по сетке значений поля от его начала: минуты с шагом десять всегда кратны десяти, независимо от того, когда поднялся планировщик. Это делает расписания синхронными между инстансами и рестартами. Интервальное поведение «каждые N минут от старта» реализует @every — но там сетка плывёт.

Можно ли задать шаг меньше минуты?

Не в стандартном парсере: пять полей заканчиваются минутами. Для секундного расписания планировщик создаётся с опцией секунд — cron.NewWithSeconds, и тогда первым полем становятся секунды. Помните, что частые тики — это постоянный дожим на зависимости: перед включением секунд оцените, что задача делает.

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

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

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

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

Логирование в Go: структурные логи на slog

Курс по slog в Go: уровни и атрибуты, JSON-логи, свой Handler, производительность, секреты и прод. 25 уроков с примерами и проектом.

  • Логировать структурированно: уровни, ключи-атрибуты, группы
  • Выбирать формат вывода: TextHandler, JSONHandler, свой Handler
  • Мигрировать со старого пакета log без переписывания всей кодовой базы
  • Держать производительность: Enabled, предвычисление, горячий путь