اللاخوادم والعمليات المدفوعة بالأحداث

Step Functions والتنسيق

18 دقيقة الدرس 8 من 28

Step Functions والتنسيق

كل نظام serverless موزع يصطدم في نهاية المطاف بنفس المشكلة: سلسلة من دوال Lambda تستدعي بعضها عبر InvokeFunction تُنشئ اقتراناً غير مرئي وغير قابل للمراقبة. تفقد تاريخ التنفيذ، وتصبح عمليات إعادة المحاولة يدوية، وتعيش مسارات الأخطاء داخل كود التطبيق، فيضيع أي مهلة زمنية في صمت. AWS Step Functions يحل هذه المشكلة بجعل حالة سير العمل صريحة ودائمة وقابلة للمراقبة — رافعاً منطق التنسيق خارج دوالك تماماً.

التنسيق مقابل الكوريوغرافيا

يعكس هذان النمطان إجابتين مختلفتين جوهرياً على السؤال: "من يقرر ما يحدث بعد ذلك؟"

  • الكوريوغرافيا — تتفاعل كل خدمة مع الأحداث التي تعنيها. تصدر خدمة الطلب حدث order.placed، تستمع خدمة المخزون وتصدر inventory.reserved، تستمع خدمة الفوترة وتصدر payment.charged. لا يوجد مركز تحكم. اقتران ضعيف للغاية، يتوسع أفقياً، لكن سير العمل الكلي ضمني — موزع عبر مستهلكين متعددين. تتبع حادثة فشل يعني تتبع معرفات الارتباط عبر خمسة مجموعات سجلات CloudWatch وناقلَي EventBridge.
  • التنسيق — منسق مركزي (آلة الحالة) يستدعي كل مشارك بالترتيب، ويمرر المخرجات كمدخلات، ويتعامل مع الأخطاء، ويحافظ على الحالة الدائمة. سير العمل صريح وقابل للرؤية والإصدار. الاقتران أضعف من الاستدعاء المباشر Lambda-to-Lambda لكنه أقوى من الكوريوغرافيا الخالصة.
لا يوجد نمط أفضل على الإطلاق. في أنظمة بحجم Stripe، تعمل الكوريوغرافيا الخالصة لأن الفرق تمتلك سياقات محددة والاتساق التدريجي مقبول. أما للمعاملات التجارية متعددة الخطوات التي تحتاج منطق تعويض (نمط الـ saga)، يفوز تنسيق Step Functions على صعيد الرؤية وضمانات الصحة.
Orchestration vs Choreography comparison Choreography EventBridge Bus Order Service Inventory Svc Billing Service Notify Service Decoupled — no central brain Flow is implicit across services Orchestration (Step Functions) State Machine 1. ValidateOrder 2. ReserveInventory 3. ChargePayment Catch: Compensate Explicit — durable state, full history Failures handled in the machine
الكوريوغرافيا (يسار) تُوجّه الأحداث عبر ناقل دون منسق مركزي؛ التنسيق (يمين) يجعل سير العمل صريحاً في آلة حالة Step Functions مع مسارات تعويض مدمجة.

لغة Amazon States — أنماط الإنتاج

تُعرَّف آلات حالة Step Functions بـAmazon States Language (ASL)، وهي صيغة فوق JSON/YAML. أنواع الحالات الرئيسية هي: Task (استدعاء عمل)، وChoice (تفرع)، وParallel (توزيع المهام)، وMap (التكرار مع التحكم في التزامن)، وWait (انتظار)، وPass (تحويل البيانات). Standard Workflows تمنحك دلالات التنفيذ-مرة-واحدة، وتنفيذاً يصل إلى سنة كاملة، وتاريخ تنفيذ كامل في وحدة التحكم — استخدمها للمعاملات التجارية الحرجة. Express Workflows تعمل حتى خمس دقائق بتكلفة حوالي دولار لكل مليون انتقال حالة — مناسبة لخطوط بث عالية الإنتاجية حيث التسليم-مرة-على-الأقل مقبول.

نمط التكامل waitForTaskToken لا غنى عنه في الإنتاج: تستلم Lambda رمز المهمة، تُنجز عملاً غير متزامن (تستدعي API خارجياً أو تنتظر موافقة بشرية)، ثم تستدعي SendTaskSuccess أو SendTaskFailure بالرمز عند الانتهاء. تُوقف آلة الحالة نفسها دون أي تكلفة استطلاع، وتستأنف بالضبط من حيث توقفت — حتى بعد أيام.

# order-pipeline.asl.yaml — Standard Workflow: saga طلب التجارة الإلكترونية مع التعويض Comment: "Order fulfillment with saga compensation on failure" StartAt: ValidateOrder States: ValidateOrder: Type: Task Resource: arn:aws:lambda:us-east-1:123456789012:function:ValidateOrder ResultPath: $.validation Retry: - ErrorEquals: [Lambda.ServiceException, Lambda.AWSLambdaException] IntervalSeconds: 2 MaxAttempts: 3 BackoffRate: 2 Catch: - ErrorEquals: [ValidationError] ResultPath: $.error Next: FailOrder Next: ReserveInventory ReserveInventory: Type: Task Resource: arn:aws:states:::lambda:invoke.waitForTaskToken Parameters: FunctionName: arn:aws:lambda:us-east-1:123456789012:function:ReserveInventory Payload: orderId.$: $.orderId taskToken.$: $$.Task.Token HeartbeatSeconds: 30 Catch: - ErrorEquals: [States.HeartbeatTimeout, InsufficientStock] Next: FailOrder Next: ChargePayment ChargePayment: Type: Task Resource: arn:aws:states:::lambda:invoke Parameters: FunctionName: arn:aws:lambda:us-east-1:123456789012:function:ChargePayment Payload.$: $ Catch: - ErrorEquals: [PaymentDeclined, States.TaskFailed] Next: ReleaseReservation Next: FanOutNotifications ReleaseReservation: Type: Task Resource: arn:aws:lambda:us-east-1:123456789012:function:ReleaseReservation Next: FailOrder FanOutNotifications: Type: Parallel Branches: - StartAt: SendEmail States: SendEmail: Type: Task Resource: arn:aws:states:::ses:sendEmail Parameters: Destination: ToAddresses.$: States.Array($.customerEmail) Message: Subject: Data: "Order Confirmed" Body: Text: Data.$: States.Format('Order {} confirmed.', $.orderId) Source: orders@example.com End: true - StartAt: PublishSNS States: PublishSNS: Type: Task Resource: arn:aws:states:::sns:publish Parameters: TopicArn: arn:aws:sns:us-east-1:123456789012:OrderEvents Message.$: States.JsonToString($) End: true End: true FailOrder: Type: Fail Error: OrderFailed Cause: "Order could not be completed; compensation applied"

النشر عبر Terraform

في بيئات الإنتاج، تُخزَّن تعريفات ASL بجانب كود Lambda في المستودع. مع Terraform، يأخذ مورد aws_sfn_state_machine تعريف الآلة كسلسلة قالب. يتضمن سير العمل أدناه نشر التغييرات ومتابعة سجلات التنفيذ فوراً — حلقة تطوير سريعة لتصحيح انتقالات الحالة.

# Terraform — نشر Standard Workflow من ملف ASL resource "aws_sfn_state_machine" "order_pipeline" { name = "order-pipeline-${var.env}" role_arn = aws_iam_role.sfn_exec.arn type = "STANDARD" definition = templatefile("${path.module}/order-pipeline.asl.yaml", { validate_fn = aws_lambda_function.validate_order.arn reserve_fn = aws_lambda_function.reserve_inventory.arn charge_fn = aws_lambda_function.charge_payment.arn release_fn = aws_lambda_function.release_reservation.arn notify_topic = aws_sns_topic.order_events.arn }) logging_configuration { log_destination = "${aws_cloudwatch_log_group.sfn.arn}:*" include_execution_data = true level = "ERROR" } tracing_configuration { enabled = true # X-Ray عبر جميع انتقالات الحالة } } # متابعة آخر تنفيذ فاشل للتصحيح EXEC_ARN=$(aws stepfunctions list-executions \ --state-machine-arn arn:aws:states:us-east-1:123456789012:stateMachine:order-pipeline-prod \ --status-filter FAILED --max-results 1 \ --query "executions[0].executionArn" --output text) aws stepfunctions get-execution-history \ --execution-arn "$EXEC_ARN" \ --query "events[?type=='TaskFailed']" \ --output json

أنماط الفشل في الإنتاج والمقايضات

تُقدِّم Step Functions أنماط فشل خاصة بها تواجهها الفرق الجديدة على التنسيق في الإنتاج:

  • حدود حجم الحمولة — مدخلات ومخرجات آلة الحالة محدودة بـ256 كيلوبايت. للحمولات الكبيرة (نتائج تعلم الآلة، محتوى الوثائق)، خزّن البيانات في S3 ومرر مرجع S3 عبر آلة الحالة. استخدم ResultSelector لاختزال استجابات Lambda إلى الحقول التي تحتاجها فقط قبل دخولها الحالة.
  • دلالات التسليم-مرة-على-الأقل في Express — إذا لم يكن عملك البعيد幂ني (idempotent)، يمكن لـ Express Workflow المُعاد تنفيذه أن يُحمَّل العميل مرتين أو يُنشئ سجلات مكررة. دائماً نفِّذ مفاتيح idempotency عند استخدام Express.
  • توزيع Map مع التزامنMaxConcurrency: 0 تعني توازياً غير محدود. عند 10,000 عنصر لكل تنفيذ Map، يمكنك استنزاف حصص تزامن Lambda أو إغراق API بعيد. اضبط MaxConcurrency بما يتوافق مع حدود خدمتك، عادةً 40-100 للامبدا و10-20 لواجهات برمجة الطرف الثالث.
  • تكلفة Standard Workflow بالتردد العالي — كل انتقال حالة يكلف 0.000025 دولار. سير عمل من 10 حالات يعمل 100,000 مرة يومياً يكلف ~25 دولاراً في الانتقالات وحدها. للتدفقات عالية التردد القصيرة، تُقلِّل Express Workflows التكلفة بنحو 10 أضعاف.
عند مزج التنسيق مع الكوريوغرافيا — نمط saga الهجين — تُنسِّق Step Functions نطاق المعاملة المحلية (التحقق، الحجز، الشحن) بينما يحمل EventBridge حدث order.fulfilled النهائي إلى المستهلكين البعيدين المفصولين (التحليلات، المستودع، CRM). هذا يمنحك صحة المعاملة حيث يهم وفصل الاقتران حيث يتوسع.
لا تضع أسراراً أو بيانات شخصية داخل مدخلات أو مخرجات آلة الحالة. يُخزَّن تاريخ تنفيذ Standard Workflows في CloudWatch Logs ويمكن الوصول إليه من وحدة التحكم لأي شخص لديه states:GetExecutionHistory. مرّر معرف العميل، لا رقم بطاقة الائتمان؛ استرجع البيانات الحساسة داخل Lambda من Secrets Manager باستخدام المعرف.

المراقبة: تاريخ التنفيذ مقابل مقاييس CloudWatch

تمنحك Standard Workflows رسماً بيانياً مرئياً لتنفيذ العملية في وحدة التحكم — لا غنى عنه للتصحيح. للتنبيه التشغيلي على نطاق واسع، اضبط تنبيهات CloudWatch على المقاييس المدمجة: ExecutionsFailed وExecutionsTimedOut وExecutionThrottled. فعِّل include_execution_data: true فقط في بيئات غير الإنتاج أو للتنفيذات المُعيَّنة — عند الحجم العالي يرفع تكاليف CloudWatch Logs بشكل ملحوظ. في الإنتاج، سجِّل عند مستوى ERROR واعتمد على تتبعات X-Ray لإعادة بناء رسم الاستدعاء الكامل عبر استدعاءات Lambda داخل سير العمل.