跳到主要内容
Void

队列

用队列异步处理工作,例如发送邮件或处理上传。在 queues/ 中定义消费者,再从应用中发送带类型的消息。

预计阅读 4 分钟

定义队列

在 queues/ 中创建消费者。支持 TypeScript 与 JavaScript 文件(.ts、.mts、.js、.mjs)。它的路径决定队列名:queues/emails.ts 创建 emails,queues/order/notifications.ts 创建 order-notifications。每个队列必须有唯一的名字。

生成的队列名必须符合 Cloudflare 的队列命名规则:1-63 个字符,只包含字母、数字与 -,且以字母或数字开头和结尾。

导出一个用 defineQueue<T> 包装的默认处理器。T 为消费者与 send() 调用同时定义消息体:

import { defineQueue } from 'void';

interface Message {
  to: string;
  subject: string;
}

export default defineQueue<Message>(async (batch, env) => {
  for (const msg of batch.messages) {
    console.log(`发送给 ${msg.body.to}:${msg.body.subject}`);
    msg.ack(); // 确认处理成功
  }
});

以 _ 开头的文件或目录会被忽略。

发送消息

从 void/queues 导入带类型的队列代理来发送消息。每个队列都作为与名字相同的属性提供,其类型完全来自消费者的 defineQueue<T> 泛型。

import { defineHandler } from 'void';
import { queues } from 'void/queues';

export const POST = defineHandler(async (c) => {
  const order = await c.req.json();
  // 完全类型安全:body 必须匹配 queues/emails.ts 中的 Message 接口
  await queues.emails.send({
    to: order.email,
    subject: '订单已确认',
  });
  return c.json({ ok: true });
});

逐条消息确认

批次中的每条消息都有与 Cloudflare Queues API 对应的 ack() 与 retry() 方法:

export default defineQueue<Message>(async (batch, env) => {
  for (const msg of batch.messages) {
    try {
      await processMessage(msg.body);
      msg.ack(); // 从队列中移除
    } catch {
      msg.retry({ delaySeconds: 30 }); // 30 秒后重新投递
    }
  }
});
方法 作用
msg.ack() 确认消息并从队列中移除
msg.retry(options?) 重试消息,可选 { delaySeconds } 延迟
batch.ackAll() 确认批次中的所有消息
batch.retryAll(options?) 重试批次中的所有消息

每条消息以最先调用的为准。在 retry() 之后调用 ack()(或反过来)不会有任何效果。没有显式处理结果的消息,在处理器成功时会被确认,抛出异常时会被重试。

每条消息的 timestamp 字段是一个 Date 对象。

消费者选项

导出具名常量来配置 Cloudflare 如何向你的消费者投递批次:

import { defineQueue } from 'void';

interface Message {
  to: string;
  subject: string;
}

export const maxBatchSize = 50;
export const maxBatchTimeout = 10; // 秒
export const maxRetries = 5;
export const retryDelay = 30; // 秒

export default defineQueue<Message>(async (batch, env) => {
  for (const msg of batch.messages) {
    console.log(`发送给 ${msg.body.to}:${msg.body.subject}`);
  }
});
选项 说明 默认值
maxBatchSize 每个批次的最大消息数 10
maxBatchTimeout 投递不完整批次前的最长等待秒数 5
maxRetries 耗尽投递前的最大重试次数 3
retryDelay 重试之间的间隔秒数 0

部署时,Void 会开通队列并连接它们的生产者与消费者。

本地开发

在原生 Void 应用中,通过队列绑定发送消息会自动在 Miniflare 中调用消费者。本地投递使用与生产环境相同的处理器。

你也可以手动派发一个批次,向 Void 暴露的开发端点发送 POST 请求:

Content-Type: application/json

{ "queue": "<queue-name>", "messages": [{ "id": "1", "timestamp": <unix_ms>, "body": <any>, "attempts": 1 }] }

该端点需要一个本地开发触发令牌。Void 会在开发服务器启动时打印一条可直接粘贴的 curl 命令。

curl -X POST http://localhost:5173/__void/queue \
  -H "Content-Type: application/json" \
  -H "x-void-dev-trigger: <printed-token>" \
  -d '{"queue":"my-queue","messages":[{"id":"1","timestamp":'$(date +%s000)',"body":{"hello":"world"},"attempts":1}]}'

在受支持的元框架中,可以使用 /__void/queue 手动测试消费者。它运行在框架适配器的开发运行时中,使用该适配器的绑定。在这种模式下,向队列绑定发送消息不会自动调用消费者。

目录