上一篇 下一篇 分享链接 返回 返回顶部

我把订单自动化搬到 Cloudflare Workers 后,总结出这套生产实践

发布人:慈云数据-客服中心 发布时间:1 天前 阅读量:4

Cloudflare 工作流自动化教程|生产环境实测

在过去几年里,Cloudflare 已经从“CDN + DNS + WAF”逐渐演进为一套完整的边缘计算平台。很多团队最早只是把域名接入 Cloudflare,用它做 HTTPS、缓存、DDoS 防护;但随着 Workers、Queues、R2、D1、KV、Durable Objects、Cron Triggers、Zero Trust 等产品成熟,Cloudflare 已经可以承担相当一部分后端自动化任务。

本文将围绕“Cloudflare 工作流自动化”展开,结合生产环境中的真实使用场景,讲解如何使用 Cloudflare Workers 及相关组件构建稳定、可观测、可扩展的自动化流程。文章不会只停留在概念层面,而是会从架构设计、代码示例、部署配置、生产经验、风险控制等角度完整说明。


一、什么是 Cloudflare 工作流自动化?

这里所说的“工作流自动化”,并不单指某一个具体产品,而是指基于 Cloudflare 平台,把原本需要人工处理或传统服务器定时运行的任务,迁移到 Cloudflare 边缘网络上执行。

常见场景包括:

  • 定时抓取第三方接口数据;
  • 自动同步数据到 R2、KV、D1;
  • 接收 Webhook 后触发业务逻辑;
  • 自动清理缓存、刷新 CDN;
  • 订单、通知、邮件、短信等异步处理;
  • 安全风控自动封禁 IP;
  • 站点健康检查与告警;
  • 自动生成报表;
  • SaaS 系统中的后台任务编排。

传统做法通常是:买一台服务器,写脚本,配置 crontab,接入数据库,再用日志系统监控。而 Cloudflare 的优势在于,它天然具备全球边缘节点、弹性扩展、按量计费、无需维护服务器等特点,非常适合轻量到中等复杂度的自动化任务。


二、为什么选择 Cloudflare 做自动化?

在生产环境中,选择 Cloudflare 的原因主要有以下几点。

1. 无服务器运维成本低

Cloudflare Workers 不需要你管理服务器、操作系统、Nginx、Node.js 运行环境,也不需要处理常规的安全补丁。代码部署后,由 Cloudflare 在全球边缘节点执行。

对于中小团队来说,这可以显著降低维护成本。很多自动化任务并不值得单独维护一台服务器,例如每天同步一次数据、每小时检查一次接口、收到 Webhook 后做简单处理等。

2. 天然适合事件驱动

Cloudflare 支持多种触发方式:

  • HTTP 请求触发;
  • Cron 定时触发;
  • Queue 消息触发;
  • Email Routing 触发;
  • Durable Objects 状态变化;
  • Webhook 调用触发。

这意味着你可以围绕事件来设计业务逻辑,而不是长期运行一个后台进程。

3. 与 Cloudflare 生态深度集成

如果你的网站本身已经接入 Cloudflare,那么 Workers 可以非常方便地操作:

  • CDN 缓存;
  • 防火墙规则;
  • DNS 记录;
  • R2 对象存储;
  • KV 键值存储;
  • D1 数据库;
  • Queues 消息队列;
  • Turnstile 人机验证;
  • Zero Trust 权限体系。

这种平台内集成比自己拼接多家云服务更简单。

4. 全球边缘网络带来的低延迟

对于面向全球用户的业务,Workers 在边缘节点运行,可以减少请求延迟。比如你需要处理来自不同地区的 Webhook、API 请求、前端边缘逻辑,Cloudflare 会尽量在离用户较近的节点执行。

当然,自动化任务不一定都需要低延迟,但在涉及用户请求链路时,边缘执行确实有优势。


三、生产环境常见架构

一个比较典型的 Cloudflare 自动化架构如下:

外部系统 / 用户请求 / Webhook
        ↓
Cloudflare Worker
        ↓
鉴权、参数校验、限流、日志
        ↓
Cloudflare Queue
        ↓
异步 Worker 消费
        ↓
R2 / D1 / KV / 第三方 API
        ↓
告警、回调、状态记录

在生产环境中,不建议把所有逻辑都放在一个同步请求里完成。尤其是以下操作:

  • 调用第三方接口;
  • 发送邮件或短信;
  • 处理大文件;
  • 写入多个数据源;
  • 需要重试的任务;
  • 批量处理任务。

更推荐的做法是:入口 Worker 只负责校验和入队,真正耗时的任务交给 Queue 消费者异步执行。

这样做有几个好处:

  1. 用户请求响应更快;
  2. 第三方接口异常不会直接拖垮入口服务;
  3. 失败任务可以重试;
  4. 工作流可以拆分为多个阶段;
  5. 更容易做日志和状态追踪。

四、准备工作

在开始之前,你需要准备以下内容:

  • 一个 Cloudflare 账号;
  • 一个已经接入 Cloudflare 的域名;
  • Node.js 环境;
  • Wrangler CLI;
  • Cloudflare Workers 付费计划或支持相关资源的套餐;
  • 如果使用 R2、D1、Queues,需要提前开通对应服务。

安装 Wrangler:

npm install -g wrangler

登录 Cloudflare:

wrangler login

创建项目:

npm create cloudflare@latest cloudflare-workflow-demo

进入项目目录:

cd cloudflare-workflow-demo

如果你选择的是 Workers 项目,通常会生成类似结构:

cloudflare-workflow-demo
├── src
│   └── index.ts
├── wrangler.toml
├── package.json
└── tsconfig.json

五、场景设计:自动同步订单并生成通知

为了让教程更具体,我们设计一个生产环境中常见的场景:

外部电商系统通过 Webhook 推送订单事件,Cloudflare Worker 接收后进行签名验证,然后把任务写入 Queue。异步消费者从 Queue 中取出订单,写入 D1 数据库,并根据订单状态发送通知。每天凌晨通过 Cron 触发任务,生成订单统计报表并保存到 R2。

这个工作流涉及:

  • HTTP Worker;
  • Queue;
  • D1;
  • R2;
  • Cron Trigger;
  • 环境变量;
  • 安全校验;
  • 错误重试;
  • 生产部署。

六、配置 wrangler.toml

一个示例配置如下:

name = "cloudflare-workflow-demo"
main = "src/index.ts"
compatibility_date = "2024-11-01"

[vars]
ENVIRONMENT = "production"

[[queues.producers]]
queue = "order-events"
binding = "ORDER_QUEUE"

[[queues.consumers]]
queue = "order-events"
max_batch_size = 10
max_batch_timeout = 5

[[d1_databases]]
binding = "DB"
database_name = "order_db"
database_id = "your-d1-database-id"

[[r2_buckets]]
binding = "REPORT_BUCKET"
bucket_name = "order-reports"

[triggers]
crons = ["0 18 * * *"]

这里需要注意,Cloudflare Cron 使用的是 UTC 时间。如果你希望每天北京时间凌晨 2 点执行,对应 UTC 时间是前一天 18 点,所以配置为:

crons = ["0 18 * * *"]

生产环境中,这是非常容易踩坑的地方。很多团队第一次上线定时任务时,都会因为时区问题导致任务在错误时间执行。


七、创建 D1 数据表

首先创建 D1 数据库:

wrangler d1 create order_db

创建 SQL 文件,例如 schema.sql

CREATE TABLE IF NOT EXISTS orders (
  id TEXT PRIMARY KEY,
  user_id TEXT NOT NULL,
  amount INTEGER NOT NULL,
  status TEXT NOT NULL,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS workflow_logs (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  event_id TEXT,
  type TEXT NOT NULL,
  message TEXT,
  created_at TEXT NOT NULL
);

执行迁移:

wrangler d1 execute order_db --file=./schema.sql

在生产环境中,建议把 D1 的结构变更纳入版本管理,不要临时手工修改。即使 D1 适合轻量数据存储,也应保持数据库迁移流程规范。


八、编写 Worker 入口逻辑

下面是一个简化后的 src/index.ts 示例:

export interface Env {
  ORDER_QUEUE: Queue;
  DB: D1Database;
  REPORT_BUCKET: R2Bucket;
  WEBHOOK_SECRET: string;
  ENVIRONMENT: string;
}

export default {
  async fetch(request: Request, env: Env): Promise {
    const url = new URL(request.url);

    if (url.pathname === "/webhook/order" && request.method === "POST") {
      return handleOrderWebhook(request, env);
    }

    return new Response("Not Found", { status: 404 });
  },

  async queue(batch: MessageBatch, env: Env): Promise {
    for (const message of batch.messages) {
      try {
        await processOrderEvent(message.body, env);
        message.ack();
      } catch (error) {
        console.error("Queue processing failed:", error);
        message.retry();
      }
    }
  },

  async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise {
    ctx.waitUntil(generateDailyReport(env));
  },
};

这个 Worker 同时处理三类事件:

  1. HTTP 请求;
  2. Queue 消息;
  3. Cron 定时任务。

对于简单项目,可以放在同一个 Worker 中。但如果生产环境逻辑复杂,建议拆成多个 Worker:

  • API 入口 Worker;
  • Queue 消费 Worker;
  • 定时报表 Worker;
  • 管理后台 Worker。

拆分可以让职责更清晰,也更方便单独扩缩、调试和回滚。


九、Webhook 签名验证

Webhook 接口必须做安全校验,否则任何人都可以伪造请求触发你的自动化流程。

示例代码:

async function handleOrderWebhook(request: Request, env: Env): Promise {
  const signature = request.headers.get("X-Signature");

  if (!signature) {
    return new Response("Missing signature", { status: 401 });
  }

  const rawBody = await request.text();

  const valid = await verifySignature(rawBody, signature, env.WEBHOOK_SECRET);

  if (!valid) {
    return new Response("Invalid signature", { status: 403 });
  }

  let payload: any;

  try {
    payload = JSON.parse(rawBody);
  } catch {
    return new Response("Invalid JSON", { status: 400 });
  }

  if (!payload.id || !payload.user_id || !payload.amount || !payload.status) {
    return new Response("Invalid payload", { status: 400 });
  }

  await env.ORDER_QUEUE.send({
    event_id: crypto.randomUUID(),
    type: "order.updated",
    payload,
    received_at: new Date().toISOString(),
  });

  return Response.json({
    success: true,
    message: "Order event accepted",
  });
}

签名验证函数可以使用 HMAC:

async function verifySignature(
  body: string,
  signature: string,
  secret: string
): Promise {
  const encoder = new TextEncoder();

  const key = await crypto.subtle.importKey(
    "raw",
    encoder.encode(secret),
    {
      name: "HMAC",
      hash: "SHA-256",
    },
    false,
    ["sign"]
  );

  const digest = await crypto.subtle.sign("HMAC", key, encoder.encode(body));

  const expected = Array.from(new Uint8Array(digest))
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");

  return timingSafeEqual(expected, signature);
}

function timingSafeEqual(a: string, b: string): boolean {
  if (a.length !== b.length) return false;

  let result = 0;

  for (let i = 0; i < a.length; i++) {
    result |= a.charCodeAt(i) ^ b.charCodeAt(i);
  }

  return result === 0;
}

生产环境中,Webhook 密钥不要写入代码,也不要写入公开仓库。应通过 Wrangler Secret 设置:

wrangler secret put WEBHOOK_SECRET

十、处理 Queue 消息

Queue 消费者负责真正的业务逻辑:

async function processOrderEvent(event: any, env: Env): Promise {
  const order = event.payload;
  const now = new Date().toISOString();

  await env.DB.prepare(`
    INSERT INTO orders (id, user_id, amount, status, created_at, updated_at)
    VALUES (?, ?, ?, ?, ?, ?)
    ON CONFLICT(id) DO UPDATE SET
      user_id = excluded.user_id,
      amount = excluded.amount,
      status = excluded.status,
      updated_at = excluded.updated_at
  `)
    .bind(order.id, order.user_id, order.amount, order.status, now, now)
    .run();

  await env.DB.prepare(`
    INSERT INTO workflow_logs (event_id, type, message, created_at)
    VALUES (?, ?, ?, ?)
  `)
    .bind(event.event_id, event.type, "Order processed successfully", now)
    .run();

  if (order.status === "paid") {
    await sendPaidNotification(order);
  }
}

这里使用 ON CONFLICT 实现幂等写入。幂等性是生产环境中非常重要的一点。

为什么必须幂等?

因为 Queue 消息可能因为网络异常、运行超时、消费者错误等原因被重试。如果你的处理逻辑不是幂等的,就可能出现:

  • 同一个订单重复入库;
  • 用户收到多封通知;
  • 库存被重复扣减;
  • 余额被重复变更;
  • 报表数据重复统计。

对于订单、支付、库存等场景,幂等性不是优化项,而是必需项。


十一、发送通知的生产建议

示例函数:

async function sendPaidNotification(order: any): Promise {
  await fetch("https://api.example.com/notify", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Source": "cloudflare-worker",
    },
    body: JSON.stringify({
      user_id: order.user_id,
      order_id: order.id,
      amount: order.amount,
      message: "Your order has been paid successfully.",
    }),
  });
}

生产环境不建议直接这样写完就上线,还需要考虑:

  1. 第三方接口是否有超时;
  2. 第三方接口是否有频率限制;
  3. 请求失败是否重试;
  4. 是否需要记录通知状态;
  5. 是否要避免重复通知;
  6. 通知接口返回非 2xx 时如何处理;
  7. 是否需要死信队列。

如果通知非常关键,建议把“订单处理”和“通知发送”拆成两个 Queue:

order-events queue
        ↓
订单入库成功
        ↓
notification queue
        ↓
发送邮件 / 短信 / 站内信

这样即使通知服务异常,也不会影响订单主流程。


十二、定时生成日报并保存到 R2

每天凌晨执行报表任务:

async function generateDailyReport(env: Env): Promise {
  const now = new Date();
  const date = now.toISOString().slice(0, 10);

  const result = await env.DB.prepare(`
    SELECT
      status,
      COUNT(*) as count,
      SUM(amount) as total_amount
    FROM orders
    GROUP BY status
  `).all();

  const report = {
    date,
    generated_at: now.toISOString(),
    data: result.results,
  };

  await env.REPORT_BUCKET.put(
    `daily/order-report-${date}.json`,
    JSON.stringify(report, null, 2),
    {
      httpMetadata: {
        contentType: "application/json",
      },
    }
  );

  await env.DB.prepare(`
    INSERT INTO workflow_logs (event_id, type, message, created_at)
    VALUES (?, ?, ?, ?)
  `)
    .bind(crypto.randomUUID(), "daily.report", `Report generated: ${date}`, now.toISOString())
    .run();
}

R2 很适合存储自动生成的报表、日志归档、导出文件、备份数据等。相比把大文件塞进数据库,R2 更便宜、更合适。

在生产环境中,可以进一步增强:

  • 报表按日期分区;
  • 报表生成后通知管理员;
  • 支持下载签名 URL;
  • 失败后重试;
  • 报表生成状态写入 D1;
  • 历史报表定期归档。

十三、部署到生产环境

部署前可以先本地运行:

wrangler dev

测试 Webhook:

curl -X POST http://localhost:8787/webhook/order \
  -H "Content-Type: application/json" \
  -H "X-Signature: your-signature" \
  -d '{
    "id": "order_10001",
    "user_id": "user_abc",
    "amount": 9900,
    "status": "paid"
  }'

部署:

wrangler deploy

查看日志:

wrangler tail

如果是生产环境,建议至少准备两个环境:

  • staging;
  • production。

示例:

[env.staging]
name = "cloudflare-workflow-demo-staging"

[env.production]
name = "cloudflare-workflow-demo-production"

部署到指定环境:

wrangler deploy --env production

十四、生产环境实测经验

下面是一些在生产环境中非常实用的经验。

1. 不要让入口 Worker 做太多事

入口 Worker 应尽量只做:

  • 鉴权;
  • 参数校验;
  • 基础限流;
  • 写入 Queue;
  • 快速响应。

耗时任务放到 Queue 里处理。这样可以显著降低超时和错误率。

2. 所有外部调用都要设置超时

fetch 默认行为可能不符合你的预期。建议用 AbortController 设置超时:

async function fetchWithTimeout(url: string, options: RequestInit, timeout = 5000) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeout);

  try {
    return await fetch(url, {
      ...options,
      signal: controller.signal,
    });
  } finally {
    clearTimeout(timer);
  }
}

第三方 API 不稳定时,超时控制可以保护你的工作流。

3. Queue 任务必须考虑重试

Cloudflare Queue 支持消息重试,但你需要保证业务逻辑能承受重试。核心原则是:

  • 写数据库要幂等;
  • 发通知要有去重;
  • 扣款、扣库存等操作必须使用唯一业务键;
  • 失败日志要可查询;
  • 多次失败后进入人工处理或死信流程。

4. 定时任务要注意 UTC 时区

这是最常见的问题之一。所有 Cron 配置都应明确记录目标时区和 UTC 换算结果。例如:

目标执行时间:北京时间每日 02:00
Cloudflare Cron:UTC 每日 18:00
配置:0 18 * * *

5. 日志不要只靠 console

console.log 可以用于排查,但生产环境最好把关键事件写入 D1 或外部日志系统。尤其是:

  • Webhook 接收记录;
  • Queue 处理结果;
  • 报表生成结果;
  • 第三方接口调用失败;
  • 重试次数;
  • 异常堆栈。

否则一旦任务失败,你很难追踪原因。

6. Secrets 必须分环境管理

不要在 staging 和 production 使用同一套密钥。建议分别设置:

wrangler secret put WEBHOOK_SECRET --env staging
wrangler secret put WEBHOOK_SECRET --env production

同时,API Token 权限应遵循最小权限原则。比如只需要刷新缓存,就不要给 DNS 写权限。

7. 注意平台限制

Cloudflare Workers、D1、R2、Queues 都有各自限制,例如执行时长、请求数量、消息大小、数据库写入能力等。生产前应阅读最新官方文档,并根据业务量做压测。

如果任务属于长时间、大计算量、强数据库事务型任务,Cloudflare Workers 未必是最佳选择。它更适合事件驱动、边缘计算、轻量自动化、异步任务编排。


十五、适合与不适合的场景

适合的场景

Cloudflare 自动化非常适合:

  • Webhook 接收与转发;
  • API 网关轻量处理;
  • 定时同步数据;
  • CDN 缓存刷新;
  • 安全规则自动化;
  • 小型后台任务;
  • 静态站点动态增强;
  • 全球低延迟请求处理;
  • 文件上传后处理;
  • 报表导出和归档。

不适合的场景

以下场景不建议完全依赖 Cloudflare Workers:

  • 复杂长事务;
  • 大规模 CPU 密集计算;
  • 长时间视频转码;
  • 高频金融交易核心系统;
  • 强一致性数据库业务;
  • 需要长期驻留内存的服务;
  • 非常复杂的工作流编排系统。

如果业务流程非常复杂,可以将 Cloudflare 作为边缘入口和自动化触发层,核心计算仍然放在传统云服务器、Kubernetes 或专门的工作流系统中。


十六、一个推荐的生产级实践清单

上线前可以按照下面清单检查:

  • [ ] Webhook 是否有签名验证;
  • [ ] 是否限制请求方法和 Content-Type;
  • [ ] 是否校验 JSON 参数;
  • [ ] 是否设置 Secrets;
  • [ ] 是否区分 staging 和 production;
  • [ ] Queue 消费是否幂等;
  • [ ] 是否记录关键日志;
  • [ ] 是否处理第三方 API 超时;
  • [ ] 是否考虑重试和死信;
  • [ ] Cron 时区是否正确;
  • [ ] D1 表结构是否纳入版本管理;
  • [ ] R2 文件路径是否有日期分区;
  • [ ] API Token 是否最小权限;
  • [ ] 是否做过本地测试;
  • [ ] 是否通过 wrangler tail 观察过生产日志;
  • [ ] 是否准备回滚方案。

十七、总结

Cloudflare 已经不只是 CDN 服务商,而是一套成熟的边缘应用平台。利用 Workers、Queues、D1、R2、Cron Triggers 等组件,我们可以构建一套低成本、高弹性、事件驱动的工作流自动化系统。

从生产环境实测来看,Cloudflare 非常适合处理轻量到中等复杂度的自动化任务,例如 Webhook 接收、异步消息处理、定时报表生成、缓存刷新、数据同步和安全联动。它的优势在于部署简单、扩展方便、运维成本低,并且可以与 Cloudflare 生态无缝集成。

但同时也要注意,生产环境不能只关注“能跑起来”,更要关注安全、幂等、重试、日志、时区、权限和平台限制。尤其是涉及订单、支付、通知这类关键业务时,必须设计好失败处理和重复执行保护。

如果你的网站已经使用 Cloudflare,那么从一个简单的自动化任务开始迁移,是非常值得尝试的。先把定时任务、Webhook 转发、缓存刷新这类低风险流程放到 Workers 上,逐步验证稳定性,再扩展到更复杂的业务场景。这样既能享受 Cloudflare 边缘平台带来的便利,也能控制生产风险。

目录结构
全文