سفارشی کردن رسیدگی به خطا در توابع مرحله
ما میتوانیم مدیریت خطا را در توابع مرحله با استفاده از فیلدهای Retry و Catch داخلی سفارشی کنیم. هنگام انتقال مسئولیت رسیدگی به خطا به توابع Step، میتوانیم کدهای کوتاهتر و تمیزتر را در برنامههای خود بنویسیم.
1. رسیدگی به خطا بدون توابع مرحله
فرض کنید میخواهیم یک گردش کاری ایجاد کنیم که از چندین تابع Lambda و شاید برخی خدمات AWS دیگر تشکیل شده باشد.
ما می توانیم از چندین نوع مختلف روش های ارکستراسیون استفاده کنیم. برخی از آنها بهتر هستند و برخی از آنها کمتر مطلوب هستند.
در هر صورت، ما باید خطاها را مدیریت کنیم، و معمولاً این کار را در کد برنامه با نوشتن چیزی شبیه به این انجام می دهیم:
export async function handler(event) {
const { taskId } = event;
// get the task based on its ID from a 3rd party endpoint - pseudo code:
let task;
try {
task = await getTaskById(taskId);
return task;
} catch (error) {
logger.error('error while getting task', { taskId });
throw error;
}
}
وقتی از صفی استفاده می کنیم که تابع برای پیام ها نظرسنجی می کند، معمولا مقداری a را اعمال می کنیم دوباره امتحان کنید سازوکار. پس از بروز خطا، تابع تلاش می کند تا کار را از نقطه پایانی دوباره با همان بار یا پارامترها دریافت کند. اگر خوش شانس باشیم، تماس برای بار دوم یا سوم موفقیت آمیز خواهد بود.
اما اگر تکلیف باشد پیدا نشد و نقطه پایانی شخص 3 با a پاسخ می دهد 404
خطا؟ یا اعتبارسنجی برای پارامترهای رویداد با شکست مواجه می شود؟
این تابع می تواند هر چند بار که بخواهیم دوباره امتحان کند، اما نتیجه همیشه یکسان خواهد بود – یک خطا. آیا منطقی است که آن را دوباره با همان ورودی امتحان کنیم؟ احتمالا نه.
در این صورت میتوانیم بررسی کنیم که آیا خطا دارای کد یا نام وضعیت خاصی است یا خیر. یک رویکرد ممکن می تواند به این صورت باشد:
class NotFoundError extends Error {
constructor(message) {
super(message);
this.name = 'NotFoundError';
}
}
async function getTaskById(taskId) {
try {
const task = await getTaskFromThirdParty(taskId);
return task;
} catch (error) {
if (error.statusCode === 404) {
throw new NotFoundError(`Task with ID ${taskId} is not found.`)
}
// otherwise rethrow the original error
throw error
}
}
export async function handler(event) {
const { taskId } = event;
let task;
try {
task = await getTaskById(taskId);
return task;
} catch (error) {
if (error instanceof NotFoundError) {
logger.error('task not found, no point trying again');
return;
}
// We'll retry when getting other errors
logger.error('error while getting task', { taskId });
throw error;
}
}
این کد زشت می شود و خواندن آن با آن سخت می شود if
شرایط در catch
مسدود کردن. تصور کنید که باید چندین نوع خطا را به طور متفاوت مدیریت کنیم. ممکن است منجر به کدهای پیچیده تر شود.
2. رسیدگی به خطا با توابع مرحله
اگر مجبور به هماهنگی چند تابع فراخوانی باشیم، میتوانیم از توابع مرحله AWS برای هماهنگ کردن گردش کار استفاده کنیم. توابع مرحله ای با بسیاری از سرویس های AWS دیگر ادغام می شود، اما در اینجا من روی توابع Lambda تمرکز می کنم.
2.1. مدیریت خطا را محول کنید
یکی از بسیاری از ویژگی های عالی Step Function این است که ما می توانیم بلند کردن مدیریت خطا از کد به ماشین حالت.
ما می توانیم یک وضعیت Task ایجاد کنیم که از آن توابع Step می توانند یک تابع Lambda را فراخوانی کنند. Workflow Studio ساخت یک ماشین حالت را با ابزار کشیدن و رها کردن عالی خود آسان می کند (خوب، آسان تر).
وقتی یک تابع لامبدا را به آن اضافه می کنیم Task
ایالت، ما یک Retry
فیلد در UI و تعریف ASL مربوطه (زبان ایالت آمازون، اساساً JSON با برخی ویژگیهای خاص). به نظر می رسد این است:
"Retry": [
{
"ErrorEquals": [
"States.TaskFailed"
],
"BackoffRate": 2,
"IntervalSeconds": 2,
"MaxAttempts": 3,
"Comment": "Some interesting comments about the field"
}
]
2.2. فیلدها را دوباره امتحان کنید
این جایی است که میتوانیم نحوه پردازش مجدد تلاشها توسط توابع Step را مشخص کنیم.
را ErrorEquals
آرایه حاوی انواع خطاهایی است که شی تکرار داده شده مربوط به آنهاست. States.TaskFailed
شامل تقریباً تمام خطاهایی است که می تواند در حین اجرای یک کار رخ دهد، به عنوان مثال، استثناهایی که در برنامه، شبکه یا خطاهای زمان اجرا ایجاد می کنیم.
بنابراین هنگامی که هنگام اجرای تابع لامبدا خطایی رخ می دهد، سه بار دیگر امتحان می کنیم (MaxAttempts
). IntervalSeconds
و BackoffRate
یک الگوی عقب نشینی نمایی ایجاد می کند. اولین تلاش مجدد در 2 ثانیه انجام می شود (IntervalSeconds
، سپس توابع مرحله فاصله بین هر تلاش مجدد را دو برابر می کند ("BackoffRate": 2
) تا زمانی که به حداکثر تعداد تلاش ها برسد.
2.3. رسیدگی به خطاهای سفارشی
همانطور که در بالا ذکر کردم، همه انواع خطاها نیازی به امتحان مجدد ندارند. میتوانیم یک شی رسیدگی به خطای دیگر را به آن اضافه کنیم Retry
آرایه به این صورت:
"Retry": [
{
"ErrorEquals": [
"NotFoundError"
],
"BackoffRate": 1,
"IntervalSeconds": 0,
"MaxAttempts": 0,
"Comment": "No retries on NotFoundError"
},
{
"ErrorEquals": [
"States.TaskFailed"
],
"BackoffRate": 2,
"IntervalSeconds": 2,
"MaxAttempts": 3,
"Comment": "Some interesting comments about the field"
}
]
تنظیم کردیم 0
به IntervalSeconds
و MaxAttempts
. این بدان معناست که ما نمیخواهیم توابع Step مجدداً تابع Lambda را فراخوانی کند. BackoffRate
دارای حداقل مقدار اجباری است 1
، بنابراین می توانیم آن را همانطور که هست رها کنیم.
را Retry
عنصر یک آرایه است و توابع Step به ترتیب اشیاء آن را ارزیابی می کند. بنابراین ابتدا باید خطاهای سفارشی خود را بنویسیم و States.TaskFailed
، که با هر خطای دیگری مطابقت دارد (برای موارد استثنا به ادامه مطلب مراجعه کنید)، در آخر خواهد آمد.
2.4. صید زمینه ها
اگر تمام تلاشهای مجدد تمام شده باشد، و همچنان در یک نقطه از گردش کار خطایی وجود داشته باشد، اجرای ماشین حالت با شکست مواجه میشود. در بسیاری از موقعیتها میتواند مطلوب باشد، اما ممکن است موارد کاربردی وجود داشته باشد که بخواهیم استثنا را مدیریت کنیم.
به عنوان مثال، ما می خواهیم در صورت وجود a به تیم اطلاع دهیم NotFoundError
. ما می خواهیم چیزی را که وجود ندارد پردازش کنیم و باید آن را درست کنیم!
یا، میخواهیم از مرحله شکست خورده بگذریم و خوشحالیم که بقیه جریان کار را ادامه دهیم.
در این موارد، ما مجبور خواهیم شد گرفتن خطاها
یکی از راه حل های ممکن این است که عوارض جانبی را در آن قرار دهید try/catch
همانطور که در بالا دیدیم، در کد بلوک می شود. اما ما میتوانیم این مسئولیت را به Step Functions که همراه با داخلی است، محول کنیم Catch
رشته.
Catch
در تعریف ماشین حالت در همان سطح است Retry
. مشابه دوستش، آرایه ای نیز خواهد بود.
می تواند به این شکل باشد:
"Catch": [
{
"ErrorEquals": [
"NotFoundError"
],
"Comment": "Handling not found errors",
"Next": "Send email notification",
"ResultPath": "$.error"
}
]
را ErrorEquals
آرایه مانند بالا در است Retry
، و خطاهایی را که می خواهیم با کنترل کننده سفارشی در اینجا پیدا کنیم را مشخص می کنیم.
را Next
فیلد شامل حالت بعدی بعد از اینکه توابع مرحله ای هر کدام را گرفت می باشد NotFoundError
. در این مورد، آن خواهد بود Send email notification
حالت، که می تواند هر چیزی منطقی برای مورد استفاده باشد، به عنوان مثال، فراخوانی یک تابع Lambda دیگر یا انتشار یک پیام به یک موضوع SNS برای اعلان.
می توانیم شی خطا را به آن اضافه کنیم error
دارایی در خروجی حالت (ResultPath
). اگر ما این کار را انجام دهیم، دولتی که پرتاب می کند NotFoundError
استثنا خروجی مشابه این خواهد داشت:
{
"taskId": "TASK_ID",
// ... other input properties here
"error": {
"Error": "NotFoundError",
"Cause": "{\"errorType\":\"NotFoundError\",\"errorMessage\":\"Task \\
with ID TASK_ID is not found\",\"trace\":[\"NotFoundError: Task with \\
ID TASK_ID is not found\",\" at Runtime.handler \\
(file:///var/task/index.mjs:12:11)\",\" at Runtime.handleOnceNonStreaming \\
(file:///var/runtime/index.mjs:1086:29)\"]}"
}
}
البته این شی JSON وضعیت بعدی خواهد بود ورودی، در مورد ما، Send email notification
. این ایالت قرار است یک ایمیل با پیام خطا برای اعضای تیم ارسال کند.
ما می توانیم اشیاء بیشتری را به آن اضافه کنیم Catch
یا فقط یکی با States.TaskFailed
یا States.ALL
برای اجرای منطق سفارشی زمانی که (تقریبا) هر نوع خطایی رخ می دهد.
3. همه چیز را کنار هم قرار دهید
ماشین حالت فوق العاده ساده ما به این شکل است:
وقتی اولین حالت (Get task from 3rd party
) الف را پرتاب می کند NotFoundError
، Step Functions قبل از اینکه اجرا ناموفق باشد، یک اعلان ایمیل با پیام خطا برای مشترکین ارسال می کند. فراموش نکنید که ALLOW
را SNS:Publish
اگر بخواهید اعلان ارسال کنید، در نقش IAM تابع Step عمل کنید.
اگر خطایی رخ ندهد، توابع مرحله ای رخ خواهد داد Do something with the task
در حالت بعدی
اگر در هر یک از مراحل خطای دیگری رخ دهد، اجرا با شکست مواجه خواهد شد.
یک مزیت بزرگ استفاده از رویکرد مورد بحث در بالا این است که کنترل کننده تابع Lambda ما بسیار ساده می شود:
export async function handler(event) {
const { taskId } = event;
const task = await getTaskById(taskId);
return task;
}
خودشه! توابع مرحله بقیه را اداره می کند!
4. خلاصه
ما می توانیم به جای ایجاد معماری های پیچیده و نوشتن کدهای خوانا، مدیریت خطا را به توابع Step واگذار کنیم.
را Retry
فیلد مشخص میکند که چگونه میخواهیم توابع Step در صورت بروز خطا در وضعیت، تلاشهای مجدد را انجام دهند.
ما می توانیم استفاده کنیم Catch
فیلد برای اجرای برخی از منطق سفارشی زمانی که انواع خطاهای خاص در حالت قبلی رخ می دهد. را Next
ویژگی موجود در شی تعیین می کند که پس از اینکه توابع مرحله ای خطاها را دریافت کردند، کدام حالت اجرا شود.
5. مطالعه بیشتر
مدیریت خطا در توابع مرحله – States.TaskFailed
همه خطاها را پوشش نمی دهد، در اینجا استثناها همراه با سایر گزینه ها وجود دارد
فراخوانی Lambda با توابع Step – عنوان همه چیز را نشان می دهد
تماس با آمازون SNS با توابع مرحله – نحوه پیکربندی توابع مرحله برای انتشار پیام ها به موضوعات