محتويات الدورة

Spring Boot / المتوسط: البيانات والأمان وأدلة الإطار / الدرس 12

العمل غير المتزامن والجدولة وعمر الحدث

حدد قبول المهمة وتنفيذها وسياقها وفشلها وإغلاقها، ولا تعتبر Async أو Scheduled ضمان معالجة خلفية دائمة.

وقت القراءة التقريبي: 4 دقيقةتاريخ مراجعة المحتوى
في هذه الصفحة

أهداف الدرس

  • تقييد المنفذ وملاحظة النتيجة المؤجلة.
  • شرح حد الوكيل ونقل السياق.
  • فصل الحدث والجدولة المحليين من التسليم الدائم.

القبول والاكتمال نتيجتان مختلفتان

تنقل Async استدعاءً مُداراً مناسباً إلى منفذ ولا تجعله دائماً أو ناجحاً عند عودة المستدعي. حدد القبول والطابور والنتيجة والمهلة والإلغاء والمالك. يجب أن تشرح 202 متابعة المقبول لا أن تدعي اكتمال العمل.

تنطبق حدود الوكيل: قد تتجاوز الدعوة الذاتية النصيحة ولا يعترض new تلقائياً. تحتاج void سياسة فشل غير ممسوك، وقد تخفي future الخطأ إن أُهملت. مرر أمراً ثابتاً محدوداً لا طلب Servlet محتجزاً أوحقلاً عاماً متغيراً؛ فالنقل للخلفية لا يحل ملكية البيانات.

اختر منفذاً مسمى محدوداً بقصد

يمكّن الجزء Async ويعرف منفذاً مسمى بعمال وطابور محدودين، وتعمل الدالة فيه دون إرسال خفي ثان إلىالمجمع العام. لم ينفذ، وعلى التطبيق تحديد الرفض والإغلاق لعمله.

قد يلغي طابور غير محدود افتراضات maxPoolSize وحده. اجعل الامتلاء ضغطاً عكسياً أورفضاً موثقاً لا إعادة بلا نهاية. احترم اتصال القاعدة والموارد التابعة؛ فلا يخلق الخيط سعة خارجية أو يصلح دورة قفل. نسق المواعيد والموارد في العملية كلها بدلاً من ضبط رقم منفصل.

java حد منفذ مسمى توضيحي
package training.catalog;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

@Configuration(proxyBeanMethods = false)
@EnableAsync
class AsyncConfiguration {
    @Bean("catalogTasks")
    ThreadPoolTaskExecutor catalogTasks() {
        var executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(4);
        executor.setQueueCapacity(32);
        executor.setThreadNamePrefix("catalog-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        return executor;
    }
}

@Service
class SummaryJobs {
    private final SummaryBuilder builder;
    SummaryJobs(SummaryBuilder builder) { this.builder = builder; }
    @Async("catalogTasks")
    public CompletableFuture<Summary> build(SummaryCommand command) {
        return CompletableFuture.completedFuture(builder.build(command));
    }
}

تهيئه دورة Spring عند الدمج؛ ولم يبدأ هنا كائن أوعامل أوجدولة أوخدمة.

راقب نتيجة واحدة وحرر العمال المملوكين

يرسل نموذج Java17 مهمة خالصة محدودة ويجلب 7 ويغلق منفذه في finally. يثبت ملاحظة النتيجة والعمر بلا Spring أومستمع أونظام خارجي أوحمل. لا يثبت اعتراض Async أو decorator أوترجمة الرفض في الإطار.

تحد المهلة انتظار المستدعي ولا تلغي الأثر تلقائياً. المقاطعة تعاون: انشرها حيث يسمح أوأعد علامتها وأغلق المورد. قد تعيش مهمة تتجاهل الإلغاء بعد الطلب؛ ولا تستخدم قتلاً فجائياً للخيط أوتفترض أن إلغاء الطلب تراجع قاعدة مؤكداً.

java نموذج عمر مهمة مملوكة مستقل
public class Main {

 public static void main(String[] args) throws Exception {
var executor=new java.util.concurrent.ThreadPoolExecutor(1,1,0L,java.util.concurrent.TimeUnit.MILLISECONDS,
 new java.util.concurrent.ArrayBlockingQueue<Runnable>(1));
try {
 var result=executor.submit(()->7);
 System.out.println(result.get());
} finally {
 executor.shutdown();
 if(!executor.awaitTermination(2,java.util.concurrent.TimeUnit.SECONDS)) executor.shutdownNow();
}
 }
}
الناتج المتوقع
7

تُلاحظ نتيجة محلية وينتهي العامل دون ادعاء تنفيذ الإطار أوقياس الامتلاء.

لا تفترض انتقال السياق أوالدوام عبر الخيط

لا تنتقل المعاملة والهوية و MDC وكائنات الطلب صحيحة بمجرد Async. اختر سياقاً أدنى مراجعاً أوغلافاً مناسباً وأعده أوامسحه بعد العمل لمنع التسرب. وقد يحتاج العامل معاملة مستقلة مقصودة.

الأحداث المحلية تنسيق داخل العملية يتبع المستمع و async. تربط مراحل TransactionalEventListener التوقيت ولا تجعل after-commit صندوقاً دائماً؛ فقد تضيع مهمة بانهيار. وقد تشغل كل نسخة تطبيق الجدولة، فتحتاج إعادة البدء والتأخير والتكرار تصميماً موثوقاً آخر لا اسم annotation وحده.

اختبر العمر والفشل دون تخمين توقيت

اضبط المتعاون واستخدم مزلاجاً وانتظاراً محدوداً عند الحاجة لفحص التداخل والرفض والفشل والإلغاء والإغلاق. لا تسم نومَ انتظارٍ ينجح غالباً حتمياً. افحص غياب تسرب الحالة وقناة كل خطأ، وللدوام افحص سجل مهمة محفوظاً لا عبارة«أُرسلت».

لا يبدأ سياق أوجدولة هنا، وتبقى الأجزاء غير قابلة للتشغيل حتى اختبار مخول باعتمادها الحقيقي. يحدد الحد الموثوق معنى القبول وكيف يعرف الإكمال والسياق الموثوق ومالك المورد وكيف ينجو العمل من الانهيار أو لا ينجو بقصد معلن.

الأخطاء الشائعة وإصلاحها

TaskRejectedException

لماذا يحدث؟
تعذر قبول العمل بسعة المنفذ أوعمره.
كيف تصلحه؟
طبق الضغط أوالرفض الموثق.
كيف تتجنب تكراره؟
اختبر الامتلاء والإغلاق فيعزل.

اختفى فشل مؤجل.

لماذا يحدث؟
غابت قناة ملاحظة void أو future مهملة.
كيف تصلحه؟
حدد مسار النتيجة والخطأ.
كيف تتجنب تكراره؟
اختبر مهمة تفشل.

عملت الجدولة مرتين بين نسختين.

لماذا يحدث؟
عُدت محلية مفردةعالمياً.
كيف تصلحه؟
اختر تنسيقاً دائماً ومنع تكرار.
كيف تتجنب تكراره؟
اختبر الافتراض دون حمل خدمة.

اختبر فهمك

1. هل تثبت 202 أو Future اكتمال الملخص؟

اعرض الحل المشروح

لا؛ قد تعني القبول أو مقبضاً للإكمال. حدد الحالة والنتيجة والمهلة وفرق الإلغاء، فقد تفشل المهمة بعد عودة الطلب.

2. لماذا لا يضمن حدث after-commit التسليم الدائم؟

اعرض الحل المشروح

يضبط التوقيت ولا يحفظ عملاً قد يضيع بالانهيار. احفظ هوية وصندوقاً أو وظيفة وحدد منع التكرار إذا طلب الدوام.

أهم ما تعلمته

  • يختلف القبول والإكمال والدوام.
  • يحتاج المنفذ حدوداً وسياقاً ومالك عمر.
  • لا يعوض الحدث والجدولة تصميم الاسترداد.

المراجع الرسمية