← Cloudflare Durable Objects / durable-objects / api
Будильники
Контекст
Будильники Durable Objects позволяют запланировать пробуждение Durable Object в заданный момент в будущем. Когда наступает время срабатывания будильника, alarm()-обработчик будет вызван. Alarm изменяется с помощью Storage API, а операции с будильником подчиняются тем же правилам, что и другие операции хранилища.
Обратите внимание:
- Каждый Durable Object может запланировать не более одного будильника одновременно, вызвав
setAlarm(). - Alarms гарантированно выполняются как минимум один раз и автоматически повторяются, когда
alarm()-обработчик выбрасывает исключение. - Повторные попытки выполняются с использованием экспоненциальной задержки, начиная с 2 секунд после первого сбоя, при этом допускается не более 6 повторов.
Alarms можно использовать для построения распределённых примитивов, например очередей или пакетной обработки задач поверх Durable Objects. Alarms также дают гарантию завершения операций внутри Durable Object без необходимости полагаться на входящие запросы для поддержания его активности. Полный пример смотрите в Используйте Alarms API.
Планирование нескольких событий с помощью одного будильника
Хотя у каждого Durable Object в любой момент времени может быть установлен только один alarm, можно управлять множеством запланированных и повторяющихся событий, сохраняя расписание в хранилище и поручив alarm()-обработчик обрабатывает наступившие события, а затем сам планирует следующее.
import { DurableObject } from "cloudflare:workers";
export class AgentServer extends DurableObject {
// Schedule a one-time or recurring event
async scheduleEvent(id, runAt, repeatMs = null) {
await this.ctx.storage.put(`event:${id}`, { id, runAt, repeatMs });
const currentAlarm = await this.ctx.storage.getAlarm();
if (!currentAlarm || runAt < currentAlarm) {
await this.ctx.storage.setAlarm(runAt);
}
}
async alarm() {
const now = Date.now();
const events = await this.ctx.storage.list({ prefix: "event:" });
let nextAlarm = null;
for (const [key, event] of events) {
if (event.runAt <= now) {
await this.processEvent(event);
if (event.repeatMs) {
event.runAt = now + event.repeatMs;
await this.ctx.storage.put(key, event);
} else {
await this.ctx.storage.delete(key);
}
}
// Track the next event time
if (event.runAt > now && (!nextAlarm || event.runAt < nextAlarm)) {
nextAlarm = event.runAt;
}
}
if (nextAlarm) await this.ctx.storage.setAlarm(nextAlarm);
}
async processEvent(event) {
// Your event handling logic here
}
}Методы хранилища
getAlarm
-
getAlarm():number | null-
Если alarm установлен, возвращается текущее время его срабатывания в виде количества миллисекунд, прошедших с начала эпохи UNIX. В противном случае возвращается
null. -
Если
getAlarmвызывается, покаalarmуже выполняется, он возвращаетnullесли только неsetAlarmтакже вызывался с момента запуска обработчика alarm.
-
setAlarm
-
setAlarm(scheduledTimeMs:number)void- Задайте время срабатывания будильника. Укажите его как количество миллисекунд, прошедших с начала эпохи UNIX.
- Если вы вызовете
setAlarmкогда уже запланирован один alarm, он заменит существующий alarm.
deleteAlarm
-
deleteAlarm():void-
Отмените будильник, если он уже установлен.
-
Вызов
deleteAlarm()внутриalarm()-обработчик может по возможности предотвращать повторы, но это не гарантируется.
-
Методы обработчиков
alarm
-
alarm(alarmInfo:Object)void-
Вызывается системой при наступлении запланированного времени будильника.
-
Необязательный параметр
alarmInfoобъект содержит два свойства:retryCountnumber: количество попыток повтора этого события будильника.isRetryboolean: логическое значение, показывающее, повторялся ли будильник. Это значениеtrue, если это событие alarm является повтором.
-
Существует только один экземпляр
alarm()будет выполняться в любой момент времени в расчете на один экземпляр Durable Object. -
alarm()-обработчик гарантирует выполнение как минимум один раз и при сбое будет повторяться с экспоненциальной задержкой, начиная с 2 секунд, до 6 повторов. Это относится только к самому последнемуsetAlarm()вызов. Повторные попытки будут выполняться, если метод завершится необработанным исключением. -
Этот метод можно
async.
-
Пример
В этом примере показано, как устанавливать alarms с помощью setAlarm(timestamp) метод и обрабатывайте будильники с помощью alarm()-обработчик внутри вашего Durable Object.
-
alarm()-обработчик будет вызываться каждый раз при срабатывании alarm. - Если непредвиденная ошибка приводит к завершению работы Durable Object,
alarm()-обработчик может быть пересоздан на другой машине. - После небольшой задержки
alarm()-обработчик запустится заново на другой машине.
import { DurableObject } from "cloudflare:workers";
export default {
async fetch(request, env) {
return await env.ALARM_EXAMPLE.getByName("foo").fetch(request);
},
};
const SECONDS = 1000;
export class AlarmExample extends DurableObject {
constructor(ctx, env) {
super(ctx, env);
this.storage = ctx.storage;
}
async fetch(request) {
// If there is no alarm currently set, set one for 10 seconds from now
let currentAlarm = await this.storage.getAlarm();
if (currentAlarm == null) {
this.storage.setAlarm(Date.now() + 10 * SECONDS);
}
}
async alarm() {
// The alarm handler will be invoked whenever an alarm fires.
// You can use this to do work, read from the Storage API, make HTTP calls
// and set future alarms to run using this.storage.setAlarm() from within this handler.
}
}import time
from workers import DurableObject, WorkerEntrypoint
class Default(WorkerEntrypoint):
async def fetch(self, request):
return await self.env.ALARM_EXAMPLE.getByName("foo").fetch(request)
SECONDS = 1000
class AlarmExample(DurableObject):
def __init__(self, ctx, env):
super().__init__(ctx, env)
self.storage = ctx.storage
async def fetch(self, request):
# If there is no alarm currently set, set one for 10 seconds from now
current_alarm = await self.storage.getAlarm()
if current_alarm is None:
self.storage.setAlarm(int(time.time() * 1000) + 10 * SECONDS)
async def alarm(self):
# The alarm handler will be invoked whenever an alarm fires.
# You can use this to do work, read from the Storage API, make HTTP calls
# and set future alarms to run using self.storage.setAlarm() from within this handler.
passСледующий пример показывает, как использовать alarmInfo свойство, позволяющее определить, предпринималась ли попытка обработки этого события alarm ранее.
class MyDurableObject extends DurableObject {
async alarm(alarmInfo) {
if (alarmInfo?.retryCount != 0) {
console.log(
"This alarm event has been attempted ${alarmInfo?.retryCount} times before.",
);
}
}
}class MyDurableObject(DurableObject):
async def alarm(self, alarm_info):
if alarm_info and alarm_info.get('retryCount', 0) != 0:
print(f"This alarm event has been attempted {alarm_info.get('retryCount')} times before.")Дополнительные материалы
- Узнайте, как используйте Alarms API в сквозном примере.
- Прочитайте статья в блоге с анонсом будильников Durable Objects ↗.
- Просмотрите Storage API документацию по Durable Objects.