訂閱以接收新文章的通知:

Cloudflare Workflows 現已正式推出:可用於生產環境的持久執行

2025-04-07

閱讀時間:5 分鐘
本貼文還提供以下語言版本:EnglishFrançaisDeutsch日本語한국어Español (Latinoamérica)Nederlands简体中文

測試版對於接收意見回饋和進行反覆運作很有用,但歸根結底,並非每個人都願意做實驗品,或者能忍受測試版軟體偶爾出現的尖銳問題。有時您需要大而閃亮的「正式推出」標籤(或部落格文章),現在輪到 Workflows 了。

我們的無伺服器持久執行引擎 Workflows 現已正式推出,您可在 Workers 上構建長期執行、多步驟的應用程式(有些人稱之為「步驟函數」)。

簡而言之,這意味著它可用於生產環境,但並不意味著 Workflows 將會僵化。我們將繼續擴展 Workflows(包括更多並行執行個體),推出新功能(例如全新的 waitForEvent API),並使用我們的 Agents SDK 和 Workflows,更輕鬆地構建 AI 代理程式。

如果您更喜歡程式碼而非散文,可以快速安裝 Workflows 入門專案,並使用單一命令來開始探索程式碼和 API :

npm create cloudflare@latest workflows-starter -- 
--template="cloudflare/workflows-starter"

Workflows 如何運作?我可以用來構建哪些內容?如何看待使用 Workflows 和 Agents SDK 來構建 AI 代理程式?請繼續閱讀。

使用 Workflows 構建

Workflows 是在 Cloudflare Workers 基礎上構建的持久執行引擎,可讓您構建具有復原能力的多步驟應用程式。

Workflows 的核心是實作一個基於步驟的架構,其中應用程式中的每個步驟都可獨立重試,其狀態會在步驟之間自動保留。這意味著,即使某個步驟因暫時性錯誤或網路問題失敗,Workflows 可以僅重試該步驟,而無需從頭開始重新啟動整個應用程式。

定義 Workflow 後,您將應用程式分解為合乎邏輯的步驟。

  • 每個步驟都可執行程式碼 (step.do),讓您的 Workflow 進入休眠狀態 (step.sleep 或 step.sleepUnutil),或者等待某個事件 (step.waitForEvent)。

  • 在 Workflow 執行時,它會自動保留從每個步驟傳回的狀態,從而確保您的應用程式可從中斷的位置繼續執行,即使在失敗或休眠期之後亦是如此。 

  • 對於在多個系統之間協調運作、按順序處理資料,或者需要處理可能持續數分鐘、數小時甚至數天的長時間執行任務的應用程式,這種持久的執行模型尤其強大。

需要處理傳統無狀態功能難以處理的複雜業務程序時,Workflows 尤其有用。

例如,電子商務訂單處理工作流程可能會在單獨的步驟中完成庫存檢查、以某種付款方式收費、傳送電子郵件確認,以及更新資料庫等操作。如果付款處理步驟因暫時性中斷而失敗,Workflows 會在付款服務再次可用時自動重試該步驟,而無需重複庫存檢查或重新啟動整個程序。 

您可在下方看到其運作方式:對服務的每次呼叫都可建模為一個步驟,獨立重試,並在需要時從該步驟開始復原:

import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';

// The params we expect when triggering this Workflow
type OrderParams = {
	orderId: string;
	customerId: string;
	items: Array<{ productId: string; quantity: number }>;
	paymentMethod: {
		type: string;
		id: string;
	};
};

// Our Workflow definition
export class OrderProcessingWorkflow extends WorkflowEntrypoint<Env, OrderParams> {
	async run(event: WorkflowEvent<OrderParams>, step: WorkflowStep) {
		// Step 1: Check inventory
		const inventoryResult = await step.do('check-inventory', async () => {
			console.log(`Checking inventory for order ${event.payload.orderId}`);

			// Mock: In a real workflow, you'd query your inventory system
			const inventoryCheck = await this.env.INVENTORY_SERVICE.checkAvailability(event.payload.items);

			// Return inventory status as state for the next step
			return {
				inStock: true,
				reservationId: 'inv-123456',
				itemsChecked: event.payload.items.length,
			};
		});

		// Exit workflow if items aren't in stock
		if (!inventoryResult.inStock) {
			return { status: 'failed', reason: 'out-of-stock' };
		}

		// Step 2: Process payment
		// Configure specific retry logic for payment processing
		const paymentResult = await step.do(
			'process-payment',
			{
				retries: {
					limit: 3,
					delay: '30 seconds',
					backoff: 'exponential',
				},
				timeout: '2 minutes',
			},
			async () => {
				console.log(`Processing payment for order ${event.payload.orderId}`);

				// Mock: In a real workflow, you'd call your payment processor
				const paymentResponse = await this.env.PAYMENT_SERVICE.processPayment({
					customerId: event.payload.customerId,
					orderId: event.payload.orderId,
					amount: calculateTotal(event.payload.items),
					paymentMethodId: event.payload.paymentMethod.id,
				});

				// If payment failed, throw an error that will trigger retry logic
				if (paymentResponse.status !== 'success') {
					throw new Error(`Payment failed: ${paymentResponse.message}`);
				}

				// Return payment info as state for the next step
				return {
					transactionId: 'txn-789012',
					amount: 129.99,
					timestamp: new Date().toISOString(),
				};
			},
		);

		// Step 3: Send email confirmation
		await step.do('send-confirmation-email', async () => {
			console.log(`Sending confirmation email for order ${event.payload.orderId}`);
			console.log(`Including payment confirmation ${paymentResult.transactionId}`);
			return await this.env.EMAIL_SERVICE.sendOrderConfirmation({ ... })
		});

		// Step 4: Update database
		const dbResult = await step.do('update-database', async () => {
			console.log(`Updating database for order ${event.payload.orderId}`);
			await this.updateOrderStatus(...)

			return { dbUpdated: true };
		});

		// Return final workflow state
		return {
			orderId: event.payload.orderId,
			processedAt: new Date().toISOString(),
		};
	}
}

由於集耐用性、自動重試和狀態持久性於一身,Workflows 成為構建可靠的分散式應用程式的理想選擇,這些應用程式可從容地處理現實世界出現的失敗問題。

人機互動

Workflows 即程式碼,這使得其變得極其強大:您可以動態、即時地定義步驟、有條件地分支,並對您需要的任何系統進行 API 呼叫。但有時您也需要一個 Workflow 來等待現實世界中發生的事情。

例如:

  • 取得人工核准以繼續。

  • 傳入的 Webhook,例如,Stripe 付款或 GitHub 事件。

  • 狀態變更,例如,檔案上傳至 R2 會觸發事件通知,然後將檔案引用推送至 Workflow,以便其處理檔案(或透過 AI 模型執行)。

Workflows 中的全新 waitForEvent API 可讓您完成以下操作: 

let event = await step.waitForEvent<IncomingStripeWebhook>("receive invoice paid webhook from Stripe", { type: "stripe-webhook", timeout: "1 hour" }) 

然後,您可透過能發出 HTTP 請求的任何外部服務,將事件傳送至特定執行個體:

curl -d '{"transaction":"complete","id":"1234-6789"}' \
  -H "Authorization: Bearer ${CF_TOKEN}" \
\ "https://api.cloudflare.com/client/v4/accounts/{account_id}/workflows/{workflow_name}/instances/{instance_id}/events/{event_type}"

… 或透過 Worker 本身內的 Workers API 完成:

interface Env {
  MY_WORKFLOW: Workflow;
}

interface Payload {
  transaction: string;
  id: string;
}

export default {
  async fetch(req: Request, env: Env) {
    const instanceId = new URL(req.url).searchParams.get("instanceId")
    const webhookPayload = await req.json<Payload>()

    let instance = await env.MY_WORKFLOW.get(instanceId);
    // Send our event, with `type` matching the event type defined in
    // our step.waitForEvent call
    await instance.sendEvent({type: "stripe-webhook", payload: webhookPayload})
    
    return Response.json({
      status: await instance.status(),
    });
  },
};

您甚至可使用 type 參數來等待多個事件,及/或使用 Promise.race 競爭多個事件來繼續操作,具體取決於先收到哪個事件:

export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
	async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
		let state = await step.do("get some data", () => { /* step call here */ })
		// Race the events, resolving the Promise based on which event
// we receive first
		let value = Promise.race([
step.waitForEvent("payment success", { type: "payment-success-webhook", timeout: "4 hours" ),
step.waitForEvent("payment failure", { type: "payment-failure-webhook", timeout: "4 hours" ),
])
// Continue on based on the value and event received
	}
}

為了更詳細地視覺化 waitForEvent,假設我們有一個 Workflow,由監視 GitHub 存放庫的程式碼審查代理程式觸發。

如果不能等待事件,Workflow 就無法輕鬆取得人工核准來寫回建議(甚至無法提交自己的 PR)。它可能會輪詢某些已更新的狀態,但這意味著我們必須在任意時段內呼叫 step.sleep,輪詢儲存服務以取得更新值,以及在不存在時重複操作。程式碼數量龐大,而且容易出錯:

如果沒有 waitForEvent,則更難以向執行中的 Workflow 執行個體傳送資料

如果修改相同範例以整合新的 waitForEvent API,我們可將其用於等待人工核准,再進行變更:

將 waitForEvent 新增至我們的程式碼審查 Workflow,以便其尋求明確的核准。

在這裡,您甚至可以想像 AI 代理程式本身代表人類傳送及/或採取行動:waitForEvent 只是為 Workflow 提供了一種方法,以在繼續(或不繼續)操作之前擷取和暫停某些要變更的內容。

關鍵是,您可以像 Workflows 中的任何其他步驟一樣呼叫 waitForEvent:您可以有條件地呼叫、及/或多次呼叫、及/或在互動中呼叫。Workflows 就像是 Workers:您擁有程式設計語言的全部功能,並且不受領域特定語言 (DSL) 或設定語言的限制。

定價

好消息:自發佈原始測試版以來,我們並未做出太多變更!我們將依據 Workflows 儲存的狀態新增儲存定價,並保留基於 CPU 和基於請求(叫用)的定價,如下所示:

單位

Workers Free

Workers Paid

CPU 時間(毫秒)

每個 Workflow 10 毫秒

每月含 3000 萬 CPU 毫秒

+ 每增加 100 萬 CPU 毫秒 0.02 美元

請求

每天 100,000 次 Workflow 叫用(與 Workers 共用

每月包含 1,000 萬個

每增加 100 萬個 + 0.3 美元

儲存 (GB)

1 GB

每月含 1 GB + 每月每 GB 0.20 美元

由於儲存定價為新的定價,我們不會在 2025 年 9 月 15 日之前主動計費。在收取儲存費用之前,我們將通知超過 1 GB 限制的使用者,依預設,Workflows 的儲存狀態將在三 (3) 天(免費方案)或三十 (30) 天(付費方案)後到期。

這裡的「CPU 時間」是指:您的 Workflow 主動取用運算資源的時間。這包括等待 API 呼叫、推理 LLM 或其他 I/O(例如寫入資料庫)所花費的時間。這看起來似乎是一件小事,但在實踐中,它會共同產生影響:大多數應用程式的 CPU 時間只有幾毫秒,而操作時間有幾秒:一個或兩個 API 需要 100 - 250 毫秒才能回應!

Workflow 空閒或等待時依 CPU 計費,而不是花費的時間。

特別是 Workflow 引擎,往往需要花費大量時間等待:從物件儲存(例如 Cloudflare R2)讀取資料、呼叫第三方 API 或 LLM(例如 o3-mini 或 Claude 3.7),甚至查詢 D1、Postgres 或 MySQL 等資料庫。使用 Workflows 就像Workers一樣:您不必為應用程式等待的時間付費。

開始構建

現在您已非常瞭解 Workflows 及其運作方式,並想要開始構建。接下來做什麼?

  1. 瀏覽 Workflows 文件以瞭解其運作方式、 Workflows API 和最佳做法

  2. 檢閱入門專案中的程式碼

  3. 最後,只需按幾下滑鼠,即可將啟動程式部署至您自己的 Cloudflare 帳戶中:

部署到 Cloudflare

我們保護整個企業網路,協助客戶有效地建置網際網路規模的應用程式,加速任何網站或網際網路應用程式抵禦 DDoS 攻擊,阻止駭客入侵,並且可以協助您實現 Zero Trust

從任何裝置造訪 1.1.1.1,即可開始使用我們的免費應用程式,讓您的網際網路更快速、更安全。

若要進一步瞭解我們協助打造更好的網際網路的使命,請從這裡開始。如果您正在尋找新的職業方向,請查看我們的職缺
Developer WeekCloudflare WorkersWorkflows開發人員平台

在 X 上進行關注

Sid Chatterjee|@chatsidhartha
Matt Silverlock|@elithrar
Cloudflare|@cloudflare

相關貼文

2025年11月05日 下午2:00

How Workers VPC Services connects to your regional private networks from anywhere in the world

Workers VPC Services enter open beta today. We look under the hood to see how Workers VPC connects your globally-deployed Workers to your regional private networks by using Cloudflare's global network, while abstracting cross-cloud networking complexity....