Durable Functions สำหรับเวิร์กโฟลว์ที่มีสถานะ
จัดการเวิร์กโฟลว์ที่ทำงานเป็นเวลานานโดยใช้รูปแบบตัวประสานงานของ Durable Functions (กระจายงาน/รวมผล การเชื่อมโยง การตรวจสอบ) และทำความเข้าใจวิธีบันทึกจุดตรวจสอบสถานะ
Durable Functions สำหรับเวิร์กโฟลว์ที่มีสถานะ เป็นบทเรียน Azure Fundamentals ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Azure Fundamentals และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Azure Fundamentals มีบทเรียนทั้งหมด 4 บทเรียน
เหตุใดจึงต้องใช้ Durable Functions
Azure Functions ทั่วไปไม่มีสถานะ — การเรียกใช้แต่ละครั้งทำงานอย่างอิสระและไม่มีหน่วยความจำจากการเรียกใช้ก่อนหน้า Durable Functions เพิ่มความสามารถให้ Azure Functions ในการเขียน เวิร์กโฟลว์ที่มีสถานะและทำงานเป็นเวลานาน ด้วยโค้ด async/await แบบปกติ Durable Task Framework จะบันทึกจุดตรวจสอบสถานะไปยัง Azure Storage โดยอัตโนมัติหลังจบแต่ละขั้นตอน ดังนั้นเวิร์กโฟลว์จึงทำงานต่อได้หลังเซิร์ฟเวอร์เริ่มการทำงานใหม่ หมดเวลา หรือมีการบำรุงรักษาตามแผน โดยกลับมาทำงานต่อจากจุดเดิมได้อย่างแม่นยำ
ฟังก์ชันสามประเภท
Durable Functions มีฟังก์ชันอยู่สามประเภท ฟังก์ชันตัวประสานงานจะควบคุมเวิร์กโฟลว์โดยรวม — เรียกใช้ฟังก์ชันกิจกรรมและรอผลลัพธ์ด้วย yield หรือ await โดยไม่บล็อกเธรดจริง ๆ ฟังก์ชันกิจกรรมทำงานหนึ่งหน่วย (เช่น เรียก API หรือเขียนข้อมูลลงฐานข้อมูล) และเป็นสถานที่เดียวที่ควรเกิดผลข้างเคียง ส่วน ฟังก์ชันเอนทิตีจะดูแลส่วนเล็ก ๆ ของสถานะที่คงอยู่ (เช่น ตัวนับและแฟล็ก) ข้ามการเรียกใช้แต่ละครั้ง
// Client function (HTTP trigger) — starts the orchestration
module.exports = async function (context, req) {
const client = df.getClient(context);
const orderId = req.body.orderId;
const instanceId = await client.startNew('OrderOrchestrator', undefined, { orderId });
return client.createCheckStatusResponse(context.bindingData.req, instanceId);
};รูปแบบการเชื่อมโยง
รูปแบบการเชื่อมโยงจะเรียกใช้ฟังก์ชันกิจกรรมตามลำดับ โดยส่งเอาต์พุตของฟังก์ชันหนึ่งไปเป็นอินพุตของฟังก์ชันถัดไป ตัวประสานงานจะรอแต่ละกิจกรรมตามลำดับ หากกิจกรรมใดล้มเหลว เวิร์กโฟลว์จะหยุดลงและสามารถเริ่มใหม่จากขั้นตอนที่ล้มเหลวได้ นี่เป็นรูปแบบ Durable Functions ที่ง่ายที่สุด และเหมาะสำหรับเวิร์กโฟลว์ที่แต่ละขั้นตอนต้องอาศัยผลลัพธ์จากขั้นตอนก่อนหน้า เช่น ไปป์ไลน์ประมวลผลคำสั่งซื้อ
// Orchestrator: chaining pattern
const df = require('durable-functions');
module.exports = df.orchestrator(function* (context) {
const orderId = context.df.getInput().orderId;
const validated = yield context.df.callActivity('ValidateOrder', orderId);
const charged = yield context.df.callActivity('ChargePayment', validated);
const shipped = yield context.df.callActivity('ShipOrder', charged);
return { status: 'shipped', trackingId: shipped.trackingId };
});รูปแบบ Fan-Out / Fan-In
รูปแบบ fan-out/fan-inจะเปิดใช้ฟังก์ชันกิจกรรมหลายรายการพร้อมกัน และรอให้ทั้งหมดทำงานเสร็จก่อนดำเนินการต่อ ตัวประสานงานจะเริ่มงานทั้งหมดพร้อมกันด้วย callActivity โดยไม่รอผลลัพธ์ทันที จากนั้นรวบรวมอ็อบเจ็กต์งานไว้ในอาร์เรย์ แล้วใช้ Task.all() เพื่อรอทั้งหมด วิธีนี้เร็วกว่าแบบประมวลผลตามลำดับอย่างมากสำหรับงานอิสระ เช่น การประมวลผลไฟล์หลายไฟล์ การเรียก API หลายรายการ หรือการดำเนินการแบบกลุ่ม
// Orchestrator: fan-out / fan-in
module.exports = df.orchestrator(function* (context) {
const items = context.df.getInput().items;
// Fan-out: start all tasks in parallel
const tasks = items.map(item => context.df.callActivity('ProcessItem', item));
// Fan-in: wait for all tasks to complete
const results = yield context.df.Task.all(tasks);
return results;
});รูปแบบการตรวจสอบ
รูปแบบการตรวจสอบจะสอบถามระบบภายนอกเป็นระยะจนกว่าเงื่อนไขจะเป็นจริง ซึ่งคล้ายกับลูปสอบถามสถานะ แต่มีความคงทนอย่างสมบูรณ์ ตัวประสานงานจะเรียกกิจกรรมเพื่อตรวจสอบสถานะ รอเป็นช่วงเวลาที่กำหนดค่าได้ด้วย createTimer แล้วจึงวนซ้ำ เนื่องจากสถานะถูกบันทึกจุดตรวจสอบลงในพื้นที่จัดเก็บระหว่างการสอบถามแต่ละครั้ง ตัวประสานงานจึงไม่ใช้ทรัพยากรการประมวลผลระหว่างรอ ทำให้มีประสิทธิภาพมากกว่าวิธีใช้ตัวจับเวลาที่หยุดรออย่างมาก
// Orchestrator: monitor pattern (poll until job completes)
module.exports = df.orchestrator(function* (context) {
const jobId = context.df.getInput().jobId;
const expiry = new Date(context.df.currentUtcDateTime);
expiry.setHours(expiry.getHours() + 24); // 24-hour timeout
while (context.df.currentUtcDateTime < expiry) {
const status = yield context.df.callActivity('GetJobStatus', jobId);
if (status === 'completed') return { jobId, status };
if (status === 'failed') throw new Error('Job failed');
// Wait 30 seconds before next poll
const nextCheck = new Date(context.df.currentUtcDateTime);
nextCheck.setSeconds(nextCheck.getSeconds() + 30);
yield context.df.createTimer(nextCheck);
}
throw new Error('Workflow timed out');
});รูปแบบการโต้ตอบกับมนุษย์
รูปแบบการโต้ตอบกับมนุษย์จะหยุดการประสานงานชั่วคราวและรอเหตุการณ์ภายนอก เช่น การอนุมัติจากผู้จัดการ ตัวประสานงานจะใช้ waitForExternalEvent เพื่อรอ ซึ่งอาจรอเป็นวันหรือสัปดาห์ได้โดยไม่ใช้ทรัพยากรการประมวลผล ระบบภายนอก (ลิงก์อนุมัติทางอีเมล แอปบนอุปกรณ์เคลื่อนที่ หรือเว็บฮุก) จะเรียก API HTTP ของ Durable Functions เพื่อส่งเหตุการณ์ ทำให้การประสานงานดำเนินต่อได้ หากไม่มีการตอบกลับ ให้ใช้ร่วมกับตัวจับเวลาเพื่อหมดเวลาและยกระดับการแจ้งเตือนโดยอัตโนมัติ
// Orchestrator: wait for human approval with timeout
module.exports = df.orchestrator(function* (context) {
const request = context.df.getInput();
yield context.df.callActivity('SendApprovalEmail', request);
const timeout = df.Task.createTimer(context, new Date(Date.now() + 48 * 3600 * 1000));
const approval = context.df.waitForExternalEvent('ApprovalResponse');
const winner = yield context.df.Task.any([approval, timeout]);
if (winner === approval) {
const approved = winner.result;
return approved ? 'Approved' : 'Rejected';
} else {
return 'Timed out — escalated';
}
});ข้อจำกัดของตัวประสานงาน
ฟังก์ชันตัวประสานงานมีข้อจำกัดสำคัญ เนื่องจากอาจถูกเล่นซ้ำจากประวัติหลายครั้งเพื่อสร้างสถานะขึ้นใหม่ ฟังก์ชันเหล่านี้ต้อง ให้ผลลัพธ์แน่นอน — ห้ามใช้ Date.now(), Math.random() หรือดำเนินการ I/O โดยตรง แต่ให้ใช้ context.df.currentUtcDateTime สำหรับการประทับเวลา และเรียกฟังก์ชันกิจกรรมสำหรับ I/O ทั้งหมด การบันทึกข้อมูลในตัวเนื้อหาของตัวประสานงานจะทำให้มีรายการบันทึกซ้ำระหว่างการเล่นซ้ำ ดังนั้นควรใช้ฟังก์ชันกิจกรรมสำหรับการบันทึกข้อมูลแทน
// WRONG — non-deterministic, will cause replay bugs
module.exports = df.orchestrator(function* (context) {
const now = new Date(); // Don't use Date()
const rand = Math.random(); // Don't use Math.random()
const data = await fetch('/api'); // Don't make HTTP calls directly
});
// CORRECT
module.exports = df.orchestrator(function* (context) {
const now = context.df.currentUtcDateTime; // OK
const data = yield context.df.callActivity('FetchData', null); // OK
});การจัดการอินสแตนซ์: สถานะและการยุติการทำงาน
การทำงานประสานงานแต่ละครั้งมี รหัสอินสแตนซ์ ที่ไม่ซ้ำกัน ซึ่งคุณสามารถใช้สอบถามสถานะ ส่งเหตุการณ์ หรือยุติการทำงานได้ API การจัดการผ่าน HTTP ของ Durable Functions มีปลายทางสำหรับตรวจสอบสถานะ (GET /instances/{id}) ส่งเหตุการณ์ (POST /instances/{id}/raiseEvent/{name}) และยุติการทำงาน (POST /instances/{id}/terminate) ใช้การเชื่อมโยงไคลเอ็นต์ Durable ในฟังก์ชันของคุณเพื่อเข้าถึงการดำเนินการเหล่านี้ผ่านโปรแกรม
// Client function: check orchestration status
module.exports = async function (context, req) {
const client = df.getClient(context);
const instanceId = req.params.instanceId;
const status = await client.getStatus(instanceId, true, true, true);
return {
status: 200,
body: {
instanceId,
runtimeStatus: status.runtimeStatus,
customStatus: status.customStatus,
output: status.output
}
};
};แบ็กเอนด์พื้นที่จัดเก็บและประสิทธิภาพ
Durable Functions จัดเก็บประวัติการประสานงาน สถานะอินสแตนซ์ และคิวข้อความระหว่างฟังก์ชันไว้ใน บัญชี Azure Storage (หรือแบ็กเอนด์ Azure SQL/เนเธอไรต์สำหรับปริมาณงานที่สูงขึ้น) จุดตรวจสอบทุกจุดจะเขียนข้อมูลลง Azure Table Storage และ Azure Queue Storage สำหรับสถานการณ์ที่มีปริมาณงานสูง (การประสานงานพร้อมกันหลายพันรายการ) แบ็กเอนด์พื้นที่จัดเก็บเนเธอไรต์จะใช้ Azure Event Hubs เพื่อให้มีประสิทธิภาพดีขึ้นอย่างมาก ควรตรวจสอบความลึกของคิวการประสานงานเพื่อค้นหาคอขวด
// host.json: configure the Durable Task storage provider
{
'version': '2.0',
'extensions': {
'durableTask': {
'hubName': 'MyTaskHub',
'storageProvider': {
'type': 'azure',
'connectionStringName': 'AzureWebJobsStorage',
'controlQueueBatchSize': 32,
'maxQueuePollingInterval': '00:00:02'
}
}
}
}การจัดการข้อผิดพลาดและการลองใหม่
ฟังก์ชันกิจกรรมสามารถโยนข้อยกเว้น ซึ่งจะส่งต่อกลับไปยังตัวประสานงานในรูปแบบ TaskFailedException ใช้บล็อก try-catch ในตัวประสานงานเพื่อจัดการความล้มเหลวอย่างเหมาะสม สำหรับข้อผิดพลาดชั่วคราว ให้กำหนดค่า การลองใหม่โดยอัตโนมัติ พร้อมการเพิ่มช่วงเวลารอด้วย callActivityWithRetry โดยระบุจำนวนครั้งสูงสุด ช่วงเวลาก่อนลองใหม่ครั้งแรก และค่าสัมประสิทธิ์การเพิ่มช่วงเวลารอ นี่เป็นรูปแบบที่แนะนำสำหรับกิจกรรมที่เรียก API หรือฐานข้อมูลภายนอก
// Orchestrator: retry an activity with exponential backoff
module.exports = df.orchestrator(function* (context) {
const retryOptions = new df.RetryOptions(
5000, // firstRetryIntervalInMilliseconds
3 // maxNumberOfAttempts
);
retryOptions.backoffCoefficient = 2; // 5s, 10s, 20s
try {
const result = yield context.df.callActivityWithRetry(
'CallExternalAPI',
retryOptions,
context.df.getInput()
);
return result;
} catch (e) {
yield context.df.callActivity('SendFailureAlert', e.message);
throw e;
}
});เอนทิตีแบบ Durable
เอนทิตีแบบ Durable (ฟังก์ชันเอนทิตี) ใช้สำหรับจัดการส่วนเล็ก ๆ ของสถานะที่คงอยู่และเข้าถึงได้ด้วยข้อมูลระบุตัวตน ซึ่งคล้ายกับตัวแสดงเสมือน เอนทิตีมีรหัสและสถานะที่คงอยู่ระหว่างการเรียกใช้แต่ละครั้ง คุณสามารถเรียกการดำเนินการบนเอนทิตีจากตัวประสานงานหรือไคลเอ็นต์ได้ และเอนทิตีจะประมวลผลทีละรายการตามลำดับ การใช้งานทั่วไป ได้แก่ ตัวนับ เครื่องสถานะการอนุมัติ ตัวจำกัดอัตรา และตะกร้าสินค้า — กล่าวคือ สถานการณ์ใด ๆ ที่ต้องการสถานะซึ่งคงอยู่และอัปเดตได้โดยไม่ต้องใช้ฐานข้อมูล
// Counter entity function
const df = require('durable-functions');
module.exports = df.entity(function (context) {
let count = context.df.getState(() => 0);
const operation = context.df.operationName;
if (operation === 'add') count += context.df.getInput();
if (operation === 'reset') count = 0;
if (operation === 'get') context.df.return(count);
context.df.setState(count);
});
// From orchestrator, increment counter entity
// const entityId = new df.EntityId('Counter', 'myCounter');
// yield context.df.callEntity(entityId, 'add', 1);ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจแนวคิด Microsoft Azure Fundamentals (AZ-900) จากบทเรียนนี้
สรุปบทเรียน
ในบทเรียนนี้ คุณได้เรียนรู้ว่า Durable Functions ช่วยให้สร้างเวิร์กโฟลว์ที่มีสถานะและทำงานเป็นเวลานานได้ โดยบันทึกจุดตรวจสอบสถานะของตัวประสานงานลงใน Azure Storage รูปแบบสำคัญ ได้แก่ การเชื่อมโยง, fan-out/fan-in, การตรวจสอบ และการโต้ตอบกับมนุษย์ และ ตัวประสานงานต้องให้ผลลัพธ์แน่นอน — การดำเนินการ I/O และการเรียกใช้ที่ไม่ให้ผลลัพธ์แน่นอนทั้งหมดต้องทำผ่านฟังก์ชันกิจกรรม ต่อไปเราจะศึกษา Azure Logic Apps
คำถามที่พบบ่อย
บทเรียน “Durable Functions สำหรับเวิร์กโฟลว์ที่มีสถานะ” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “Durable Functions สำหรับเวิร์กโฟลว์ที่มีสถานะ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Azure Fundamentals ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Azure Fundamentals มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “Durable Functions สำหรับเวิร์กโฟลว์ที่มีสถานะ”
จัดการเวิร์กโฟลว์ที่ทำงานเป็นเวลานานโดยใช้รูปแบบตัวประสานงานของ Durable Functions (กระจายงาน/รวมผล การเชื่อมโยง การตรวจสอบ) และทำความเข้าใจวิธีบันทึกจุดตรวจสอบสถานะ คุณปฏิบัติ Azure Fundamentals ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Azure Fundamentals หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Azure Fundamentals บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “Durable Functions สำหรับเวิร์กโฟลว์ที่มีสถานะ” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Azure Fundamentals นี้ได้ไหม
ได้ บทเรียน Azure Fundamentals ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- ทริกเกอร์และการผูกของ Azure Functions
- Durable Functions สำหรับเวิร์กโฟลว์ที่มีสถานะ
- Azure Logic Apps
- Event Grid และสถาปัตยกรรมที่ขับเคลื่อนด้วยเหตุการณ์