برنامه نویسی

نحوه حل مشکل اصلی ناشی از تحمل خطای خود DolphinScheduler که منجر به خرابی مداوم سرور می شود

توضیحات تصویر

بازتولید مشکل

در DolphinScheduler، وظیفه Shell زیر وجود دارد:

current_timestamp() {  
    date +"%Y-%m-%d %H:%M:%S"  
}

TIMESTAMP=$(current_timestamp)
echo $TIMESTAMP
sleep 60
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

استراتژی اجرای گردش کار در DolphinScheduler به صورت موازی تنظیم شده است.

فاصله زمانبندی 10 ثانیه تنظیم شده است.

پس از استقرار وظیفه برنامه ریزی شده، همانطور که انتظار می رود اجرا می شود.

در این مرحله، گره Master را برای شبیه سازی یک تصادف بکشید:

$ jps
1979710 AlertServer
1979626 WorkerServer
1979546 MasterServer
1979794 ApiApplicationServer
1980483 Jps
$ kill -9 1979546
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با بررسی DolphinScheduler، متوجه می شویم که گره Master دیگر در دسترس نیست.

در این مرحله، مشاهده می‌کنیم که گردش کار در DolphinScheduler زمان‌بندی وظایف را متوقف می‌کند و همه کارها به طور نامحدود اجرا می‌شوند تا زمانی که خطایی رخ دهد.

پس از مدتی (شبیه سازی کشف مشکل سقوط)، DolphinScheduler را مجددا راه اندازی کنید:

sh bin/stop-all.sh
sh bin/start-all.sh
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پس از راه‌اندازی مجدد، تمام کارهایی که قبلاً ناموفق بوده‌اند، از جمله کارهای برنامه‌ریزی نشده، اجرا می‌شوند.

این یک مسئله بحرانی ایجاد می کند: اگر اینها وظایف با کارایی بالا باشد، می تواند منجر به استفاده بیش از حد از CPU و حافظه شود و باعث از کار افتادن کل سرور شود!!!

توضیحات تصویر
استفاده از CPU و استفاده از RAM

تست چند سناریویی

  • خرابی اصلی، کل DS را ریستارت کنید: این موضوع باعث بروز مشکل فوق می شود.
  • Master Crash، Master مربوطه را دوباره راه اندازی کنید: این باعث مشکل بالا می شود – این یک نقص است، زیرا اسکریپت رسمی یک فرآیند پس زمینه جداگانه برای Master ندارد، فقط یک اسکریپت شروع پیش زمینه دارد. با این حال، می توانید دوباره اجرا کنید start-all.sh.
  • سقوط کارگر، کل DS را راه اندازی مجدد کنید: این باعث مشکل بالا نمی شود – زیرا Master به برنامه ریزی وظایف ادامه می دهد و وظایف مستقیماً پس از خرابی Worker با شکست مواجه می شوند.
  • خرابی کارگر، Worker مربوطه را مجددا راه اندازی کنید: این مشکل بالا را ایجاد نمی کند – شبیه به سناریوی تصادف Worker است.
  • کل DS خراب می شود، کل DS را راه اندازی مجدد کنید: این موضوع باعث بروز مشکل فوق می شود.
  • DS استفاده را متوقف کرد stop-all.sh، سپس راه اندازی مجدد کنید: این موضوع باعث بروز مشکل فوق می شود.

مسئله اصلی مربوط به استاد. تا زمانی که وظایف دوره ای پیکربندی شوند، چه Master خراب شود یا از طریق یک اسکریپت متوقف شود، مشکل فوق رخ می دهد.

تحلیل اصولی

اجزای اصلی DolphinScheduler:

  • مستر سرور: مسئول بخش بندی وظایف DAG، نظارت بر ارسال وظایف، و نظارت بر وضعیت سلامت سایر سرورهای Master و Worker است. هنگام شروع، گره های موقت را در ZooKeeper ثبت می کند و تحمل خطا را از طریق تغییرات گره ZooKeeper کنترل می کند.
  • Worker Server: مسئول اجرای وظایف و سرویس گزارش. این یک گره موقت را در ZooKeeper ثبت می کند و ضربان قلب را حفظ می کند.
  • APISserver: به درخواست‌های رابط کاربری جلویی رسیدگی می‌کند.

جریان اجرای کار را می توان به صورت زیر خلاصه کرد:

  1. ایجاد کار: یک کار در API-Server ایجاد می شود و در پایگاه داده باقی می ماند.
  2. تولید فرمان: یک کاربر به صورت دستی یک کار را راه اندازی یا زمان بندی می کند، که دستوری را برای اجرای گردش کار در پایگاه داده می نویسد.
  3. استاد مصرف دستورات: Master دستورات پایگاه داده را مصرف می کند، گردش کار را شروع می کند و وظایفی را برای اجرا به Worker اختصاص می دهد.
  4. تکمیل: پس از تکمیل گردش کار، Master اجرای گردش کار را به پایان می رساند.

توضیحات تصویر

بر اساس وب سایت رسمی، روند اجرای وظایف اصلی DolphinScheduler به شرح زیر است:

توضیحات تصویر

با توجه به پیچیدگی زمان‌بندی کار، یک فرآیند بزرگ را می‌توان به فرآیندهای کوچک‌تر تقسیم کرد که فرآیندهای کمکی خارج از جریان اصلی اضافه می‌شوند. در زیر تجزیه و تحلیل تجزیه و تحلیل فرآیند برنامه ریزی اجرا است که درک آن را آسان تر می کند:

توضیحات تصویر

من ابتدا، سرور API سازنده، درخواست HTTP اجرای گردش کار کاربر را در داده‌های فرمان کپسوله می‌کند و آن را در دستور قرار می‌دهد. t_ds_command جدول در زیر نمونه ای از دستور برای شروع یک نمونه گردش کار (نسخه قدیمی) آمده است:


{
    "commandType": "START_PROCESS",
    "processDefinitionCode": 14285512555584,
    "executorId": 1,
    "commandParam": "{}",
    "taskDependType": "TASK_POST",
    "failureStrategy": "CONTINUE",
    "warningType": "NONE",
    "startTime": 1723444881372,
    "processInstancePriority": "MEDIUM",
    "updateTime": 1723444881372,
    "workerGroup": "default",
    "tenantCode": "default",
    "environmentCode": -1,
    "dryRun": 0,
    "processInstanceId": 0,
    "processDefinitionVersion": 1,
    "testFlag": 0
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ii بعد، مصرف کننده، MasterSchedulerBootstrap برنامه حلقه در سرور اصلی، از Zookeeper (ZK) برای تخصیص اسلات به خود استفاده می کند. MasterSchedulerBootstrap لیستی از دستورات متعلق به اسلات خود را از میان انتخاب می کند t_ds_command جدول برای پردازش بیانیه پرس و جو به شرح زیر است:



وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

III. این MasterSchedulerBootstrap نظرسنجی های حلقه ای برای پردازش دستورات. الف را ایجاد می کند ProcessInstance از دستور وظیفه و میزبان اصلی، درج می کند ProcessInstance شی به t_ds_process_instance جدول، و یک کار اجرایی ایجاد می کند workflowExecuteRunnable که حاوی اطلاعات زمینه زمان اجرا لازم است. این workflowExecuteRunnable به صورت محلی در حافظه پنهان ذخیره می شود processInstanceExecCacheManager. در همان زمان، WorkflowEventType.START_WORKFLOW از ProcessInstance تولید می شود در workflowEventQueue صف

مراحل ذکر شده در بالا پس از کلیک کاربر روی دکمه شروع کار در صفحه وب رخ می دهد. با این حال، موضوعی که در اینجا به آن پرداخته می شود مربوط به زمان بندی دوره ای در استاد است. پس از بررسی مستندات، وظیفه زمان بندی دوره ای توسط MasterServer به عنوان داده فرمان کپسوله می شود و در آن درج می شود. t_ds_process_instance جدول مراحل بعدی به شرح زیر است و روند کلی به شرح زیر است:

  1. ارسال فرمان: توسط درخواست گردش کار ارسالی کاربر، MasterServer آن را در داده های فرمان کپسوله می کند و آن را در پایگاه داده قرار می دهد.

  2. تکلیف: MasterServer به طور مداوم دستورات معلق را جستجو می کند و وظایف مربوطه را به آن اختصاص می دهد. ProcessInstance بر اساس بار

  3. اجرای وظیفه: بر اساس وابستگی های DAG (Directed Acyclic Graph)، WorkerServer وظایفی را که وابستگی ندارند اولویت بندی می کند و به تدریج سایر وظایف را بر اساس اولویت آنها اجرا می کند.

  4. بازخورد وضعیت: در حین اجرای وظیفه، WorkerServer به صورت دوره ای با MasterServer تماس می گیرد تا پیشرفت و وضعیت اجرای وظایف را مطلع کند.

موضوع زمانی پیش می آید که استاد راه اندازی مجدد – باعث می شود تعداد زیادی از کارها در صف قرار گیرند t_ds_command جدول، به ویژه وظایف دوره ای.

در DolphinScheduler 3.2.1، نمونه داده برای جدول t_ds_command به شرح زیر است:


id  |command_type|process_definition_code|process_definition_version|process_instance_id|command_param                        |task_depend_type|failure_strategy|warning_type|warning_group_id|schedule_time      |start_time         |executor_id|update_time        |process_instance_priority|worker_group|tenant_code|environment_code|dry_run|test_flag|
----+------------+-----------------------+--------------------------+-------------------+-------------------------------------+----------------+----------------+------------+----------------+-------------------+-------------------+-----------+-------------------+-------------------------+------------+-----------+----------------+-------+---------+
1988|           6|         15921642898976|                         4|                  0|{"schedule_timezone":"Asia/Shanghai"}|               2|               1|           0|               0|2024-12-11 00:36:40|2024-12-11 00:39:01|          2|2024-12-11 00:39:01|                        2|default     |default    |              -1|      0|        0|
1989|           6|         15921642898976|                         4|                  0|{"schedule_timezone":"Asia/Shanghai"}|               2|               1|           0|               0|2024-12-11 00:36:50|2024-12-11 00:39:01|          2|2024-12-11 00:39:01|                        2|default     |default    |              -1|      0|        0|
1990|           6|         15921642898976|                         4|                  0|{"schedule_timezone":"Asia/Shanghai"}|               2|               1|           0|               0|2024-12-11 00:37:00|2024-12-11 00:39:01|          2|2024-12-11 00:39:01|                        2|default     |default    |              -1|      0|        0|
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

enum command_type در کد منبع تحت CommandType تعریف شده است و محتوای آن به شرح زیر است:


/**
 * command types
 * 0 start a new process
 * 1 start a new process from current nodes
 * 2 recover tolerance fault process
 * 3 recover suspended process
 * 4 start process from failure task nodes
 * 5 complement data
 * 6 start a new process from scheduler
 * 7 repeat running a process
 * 8 pause a process
 * 9 stop a process
 * 10 recover waiting thread
 * 11 recover serial wait
 * 12 start a task node in a process instance
 */
START_PROCESS(0, "start a new process"),
START_CURRENT_TASK_PROCESS(1, "start a new process from current nodes"),
RECOVER_TOLERANCE_FAULT_PROCESS(2, "recover tolerance fault process"),
RECOVER_SUSPENDED_PROCESS(3, "recover suspended process"),
START_FAILURE_TASK_PROCESS(4, "start process from failure task nodes"),
COMPLEMENT_DATA(5, "complement data"),
SCHEDULER(6, "start a new process from scheduler"),
REPEAT_RUNNING(7, "repeat running a process"),
PAUSE(8, "pause a process"),
STOP(9, "stop a process"),
RECOVER_WAITING_THREAD(10, "recover waiting thread"),
RECOVER_SERIAL_WAIT(11, "recover serial wait"),
EXECUTE_TASK(12, "start a task node in a process instance"),
DYNAMIC_GENERATION(13, "dynamic generation"),
;
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

دلیل این رفتار در مکانیزم تحمل خطا خود استاد نهفته است. این مکانیسم تحمل خطا را می توان به چندین ماژول به شرح زیر تقسیم کرد:

  1. تحمل خطای استاد: در یک راه‌اندازی چند استاد، یک گره Active Master مسئول رسیدگی به درخواست‌های زمان‌بندی کار است، در حالی که بقیه به‌عنوان Standby Master عمل می‌کنند. اگر Active Master از کار بیفتد، یک Standby Master به طور خودکار وظایف خود را بر عهده می گیرد تا از عملکرد عادی سیستم اطمینان حاصل کند. این فرآیند از طریق ZooKeeper مدیریت می شود که مسئولیت انتخاب گره Active Master و نظارت بر وضعیت گره را بر عهده دارد.

  2. همگام سازی حالت: چندین گره Master حالت های خود را همگام می کنند تا اطمینان حاصل کنند که در صورت خرابی Active Master، Standby Master می تواند برنامه ریزی کار را به طور یکپارچه به عهده بگیرد.

  3. بازیابی عیب: هنگامی که یک گره اصلی از کار می افتد، سایر گره های اصلی از مکانیسم ZooKeeper's Watcher برای شناسایی خرابی و راه اندازی فرآیندهای بازیابی خطا استفاده می کنند.

  4. تحمل خطا برای وظایف در حال اجرا: پس از خرابی یک گره Master، یک Master جدید با دسترسی به آدرس Master شکست خورده و آرایه وضعیت گردش کار در حال انجام، فهرستی از ProcessInstance هایی را که نیاز به تحمل خطا دارند، بازیابی می کند. این لیست سپس در درج می شود t_ds_command جدول، و فرآیند برنامه ریزی منظم (استاد وظایف را بازیابی و زمان بندی می کند و به دنبال آن اجرای Worker) از سر گرفته می شود.

  5. قفل های توزیع شده: در طول فرآیند تحمل خطا، گره‌های Master از مکانیسم قفل توزیع شده ZooKeeper، همراه با تخصیص شناسه‌های خاص به command جدول، برای اطمینان از اینکه تنها یک گره اصلی در یک زمان عملیات تحمل خطا را انجام می دهد. این مانع از انجام همزمان چندین گره Master می شود.

  6. رشته تحمل خطا دوره ای: جدا از تحمل خطا که توسط رویدادهای ZooKeeper ایجاد می شود، DolphinScheduler همچنین یک رشته دوره ای به نام پیاده سازی می کند. FailoverExecuteThread. این رشته مسئول بازیابی نمونه های گردش کار پس از راه اندازی مجدد Master است.

  7. دوباره کار کنید: DolphinScheduler همچنین از مکانیزم تکرار کار پس از شکست کار پشتیبانی می کند. این تلورانس خطای خرابی سرویس را تکمیل می کند و تضمین می کند که وظایف در نهایت با موفقیت اجرا می شوند.

بنابراین، بر اساس اصل و بازتولید، در ابتدا می توان استنباط کرد که تحمل خطا توسط یک نخ خاص در هنگام راه اندازی Master انجام می شود. در مرحله بعد، ما باید کد منبع را برای تأیید این موضوع بررسی کنیم.

تجزیه و تحلیل کد منبع

در org.apache.dolphinscheduler.server.master.MasterServer کلاس، در طول راه اندازی Master، یک نقطه ورودی برای آن وجود دارد run() روش:

/**
 * run master server
 */
@PostConstruct
public void run() throws SchedulerException {
    // init rpc server
    this.masterRPCServer.start();
    // install task plugin
    this.taskPluginManager.loadPlugin();
    this.masterSlotManager.start();
    // self tolerant
    this.masterRegistryClient.start();
    this.masterRegistryClient.setRegistryStoppable(this);
    this.masterSchedulerBootstrap.start();
    this.eventExecuteService.start();
    this.failoverExecuteThread.start();
    this.schedulerApi.start();
    this.taskGroupCoordinator.start();
    MasterServerMetrics.registerMasterCpuUsageGauge(() -> {
        SystemMetrics systemMetrics = metricsProvider.getSystemMetrics();
        return systemMetrics.getTotalCpuUsedPercentage();
    });
    MasterServerMetrics.registerMasterMemoryAvailableGauge(() -> {
        SystemMetrics systemMetrics = metricsProvider.getSystemMetrics();
        return (systemMetrics.getSystemMemoryMax() - systemMetrics.getSystemMemoryUsed()) / 1024.0 / 1024 / 1024;
    });
    MasterServerMetrics.registerMasterMemoryUsageGauge(() -> {
        SystemMetrics systemMetrics = metricsProvider.getSystemMetrics();
        return systemMetrics.getJvmMemoryUsedPercentage();
    });
    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
        if (!ServerLifeCycleManager.isStopped()) {
            close("MasterServer shutdownHook");
        }
    }));
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

از کد بالا می توان دریافت که Master با اجرای:

  • masterRPCServer.start(): سرور RPC را برای ارتباط گره راه اندازی و راه اندازی می کند.
  • taskPluginManager.loadPlugin(): پلاگین های وظیفه را بارگیری می کند که انواع وظایف DolphinScheduler را گسترش می دهد.
  • masterSlotManager.start(): مدیر اسلات Master را راه‌اندازی می‌کند که مسئول مدیریت جایگاه‌های منابع Master برای زمان‌بندی وظایف است.
  • masterRegistryClient.start(): مشتری رجیستری Master را راه اندازی می کند که مسئول ثبت گره Master در سرویس هماهنگی توزیع شده (مانند ZooKeeper) است.
  • masterRegistryClient.setRegistryStoppable(این): شیء قابل توقف را برای سرویس گیرنده رجیستری تنظیم می کند و به آن اجازه می دهد وقتی Master متوقف می شود پاک شود.
  • masterSchedulerBootstrap.start(): سرویس بوت استرپ زمان‌بندی Master را راه‌اندازی می‌کند که سرویس‌های مربوط به زمان‌بندی را راه‌اندازی می‌کند.
  • eventExecuteService.start(): سرویس اجرای رویداد را راه اندازی می کند، که رویدادها را در جریان کار مدیریت می کند، مانند تغییرات وضعیت وظیفه.
  • failoverExecuteThread.start(): رشته اجرای failover را شروع می کند که وظیفه بازیابی اجرای کار را پس از از کار افتادن Master بر عهده دارد.
  • SchedulerApi.start(): سرویس API زمان‌بندی را شروع می‌کند و رابط‌های مربوط به زمان‌بندی را برای تماس‌های خارجی ارائه می‌کند.
  • taskGroupCoordinator.start(): هماهنگ کننده گروه وظیفه را شروع می کند که اجرای وظایف را در یک گروه کاری هماهنگ می کند.

از طریق بازرسی کد منبع، مشخص می شود که مهم ترین بخش، failoverExecuteThread، در مورد اجرای مجدد وظایف دوره ای برنامه ریزی نشده نیست، بلکه در مورد تحمل خطا برای کارهایی است که تکمیل نشده اند. علاوه بر این، هیچ محتوای مرتبطی در کد منبع در مورد بازیابی زمان‌بندی کار دوره‌ای وجود ندارد.

اکنون، یک رویکرد جدید مورد نیاز است، که حرکت از پایین به بالا است:

ابتدا، مشخص شد که پس از راه اندازی مجدد و بازیابی، “نوع اجرا” در صفحه وب “اجرای برنامه ریزی شده” است، در حالی که “نوع_فرمان” در پایگاه داده “6” است. این بدان معناست که باید سرویسی وجود داشته باشد که دستوری با command_type = 6 در پایگاه داده وارد کند و نمونه های زمان بندی کار را از t_ds_schedules جدول

با توجه به کد منبع، با ردیابی پروژه dolphinscheduler-dao، که شامل تمام DAO های عملیات پایگاه داده است، می توانیم ScheduleMapper کلاس، که کلاس DAO مربوط به t_ds_schedules جدول سپس با ردیابی t_ds_command، ما پیدا کردیم createCommand روش در CommandServiceImpl کلاس با ارجاع متقابل این دو، با command_type با 6 سالگی، ما آن را پیدا کردیم executeInternal روش در ProcessScheduleTask کلاس

این executeInternal روش در ProcessScheduleTask کلاس سه شرط را برآورده می کند: وظیفه زمان بندی را واکشی می کند، داده های فرمان را درج می کند و نوع آن 6 است.

با بررسی کد منبع از executeInternal، قسمت اول زمان برنامه ریزی شده و زمان اجرای واقعی را از زمینه کوارتز بازیابی می کند. بخش دوم تأیید می کند که آیا Cron برای این زمان بندی وجود دارد و آنلاین است یا خیر.

در executeInternal، عناصر کلیدی در واقع هستند scheduledFireTime و fireTime.

با این اطلاعات می‌توانیم اصول زمان‌بندی را خلاصه کنیم Dolphin Scheduler + Quartz:

  • صفحه وب برنامه زمانی را تنظیم می کند که از طریق آن انجام می شود SchedulerController.createSchedule() برای ایجاد برنامه و درج یک رکورد در t_ds_schedules.
  • هنگامی که برنامه آنلاین است، با استفاده از کوارتز یک ماشه ایجاد می کند QuartzScheduler.insertOrUpdateScheduleTask() و یک رکورد را وارد می کند QRTZ_CRON_TRIGGERS جدول
  • به صورت دوره ای، ProcessScheduleTask.executeInternal() برای درج داده در آن فراخوانی می شود t_ds_command.
  • سپس، با جریان اجرای Master-Worker پیش می رود.

پس از درک جریان برنامه ریزی کلی، همراه با scheduledFireTime و fireTime، می توانیم استنباط کنیم که زمان برنامه ریزی توسط DolphinScheduler تنظیم نمی شود، بلکه توسط Quartz تنظیم می شود.

در مرحله بعد، مواد مرتبط با کوارتز را بررسی می کنیم و متوجه می شویم که کوارتز دارای یک است شلیک اشتباه مکانیسم: یک کار دوره ای الف باید در زمان مشخصی اجرا شود، اما بنا به دلایلی، وظیفه الف اجرا نشد که نامیده می شود MisFire.

Quartz یک آیتم پیکربندی برای تعیین اینکه آیا یک کار MisFire است یا خیر دارد: org.quartz.jobStore.misfireThreshold، که به طور پیش فرض 60000 میلی ثانیه (یعنی 60 ثانیه) است.

دو شرط برای وقوع Misfire لازم است:

  1. وقتی به زمان ماشه رسید، کار اجرا نشد.
  2. تاخیر در اجرای کار بیش از misfireThreshold در کوارتز پیکربندی شده است.

اگر تاخیر در اجرای کار کمتر از آستانه باشد، کوارتز آن را Misfire نمی داند و بلافاصله کار را اجرا می کند. اگر تاخیر بیشتر یا مساوی آستانه باشد، Misfire در نظر گرفته می شود و کوارتز آن را طبق استراتژی مشخص شده اجرا می کند.

دلایل رایج Misfire عبارتند از:

  • وقتی کار به زمان ماشه می رسد، همه رشته ها توسط مشاغل دیگر اشغال می شوند و هیچ رشته ای موجود نیست.
  • زمانبندی (احتمالاً به طور غیرمنتظره) در زمان شروع متوقف شد. [— This is the type of problem here.]
  • کار با حاشیه نویسی شده است @DisallowConcurrentExecution، یعنی نمی توان آن را همزمان اجرا کرد و زمانی که نقطه اجرای بعدی می آید، کار قبلی تکمیل نشده است.
  • کار زمان شروع گذشته را مشخص کرده است. به عنوان مثال، اگر زمان فعلی 8:00 صبح است، و زمان شروع روی 7:00:00 صبح تنظیم شده است.

هنگامی که کوارتز تشخیص داد که یک کار یک اشتباه است، یک مکانیسم جبران را راه اندازی می کند، اما مکانیسم جبران تنها پس از تایید کار به عنوان Misfire اجرا می شود. مکانیسم جبران در کوارتز پیکربندی شده است Trigger کد منبع:

public interface Trigger extends Serializable, Cloneable, Comparable {
    long serialVersionUID = -3904243490805975570L;
    int MISFIRE_INSTRUCTION_SMART_POLICY = 0;
    int MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1;
    int DEFAULT_PRIORITY = 5;
    ......
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اما این مکانیسم جبران باید بر اساس Trigger تعیین شود. در زیر انواع مختلفی از محرک ها وجود دارد:

توضیحات تصویر

در DolphinScheduler، انواع مختلفی از محرک ها شامل موارد زیر است:

توضیحات تصویر

انواع ماشه:

  • SimpleTrigger یک ماشه ساده است که برای اجرای کارهای تکراری استفاده می شود. می‌تواند زمان شروع را مشخص کند و سپس کار را در فواصل زمانی ثابت تکرار کند تا به تعداد تکرار مشخص‌شده برسد. ویژگی های SimpleTrigger شامل بازه تکرار (repeatInterval) و تکرار شمارش (repeatCount) با تعداد واقعی اعدام ها repeatCount + 1، زیرا کار یک بار در زمان شروع اجرا می شود (startTime).

  • CronTrigger: CronTrigger از عبارت Cron برای تعریف یک برنامه زمان بندی پیچیده استفاده می کند. یک عبارت Cron از 6 یا 7 فیلد زمانی جدا شده با فضا تشکیل شده است که نشان دهنده ثانیه، دقیقه، ساعت، روز ماه، ماه، روز هفته و یک سال اختیاری است. CronTrigger اجازه می دهد تا برنامه های راه اندازی بسیار پیچیده ای را تنظیم کنید، که اکثر قابلیت های سایر محرک ها را پوشش می دهد.

  • CalendarIntervalTrigger: CalendarIntervalTrigger وظایفی را مشخص می کند که باید در بازه های زمانی معینی از زمان خاصی شروع شوند. برخلاف SimpleTrigger که فقط فواصل زمانی میلی‌ثانیه‌ای را پشتیبانی می‌کند، CalendarIntervalTrigger از واحدهای بازه‌ای مانند ثانیه، دقیقه، ساعت، روز، ماه و سال پشتیبانی می‌کند. برای کارهایی مانند اجرای یک بار در هفته مناسب است.

  • DailyTimeIntervalTrigger: DailyTimeIntervalTrigger وظایفی را مشخص می کند که در فواصل زمانی معینی در یک زمان خاص هر روز اجرا شوند. همچنین از تعیین روزهای هفته پشتیبانی می کند. برای کارهایی مانند اجرای هر 70 ثانیه بین ساعت 9:00 تا 18:00 و فقط در روزهای هفته مناسب است.

بنابراین، از آنجایی که انواع مختلف Trigger ها پارامترهای متفاوتی دارند، زمانی که Trigger مکانیسم Misfire را فعال می کند، استراتژی نیز بر اساس Trigger متفاوت خواهد بود:

/**
 * Common Misfire mechanism in the Trigger class
 **/
 // This is an intelligent strategy, and Quartz will automatically select an appropriate misfire strategy based on the type of Trigger. For CronTrigger, the default is MISFIRE_INSTRUCTION_FIRE_ONCE_NOW.
int MISFIRE_INSTRUCTION_SMART_POLICY = 0;
// This strategy immediately executes all missed trigger events and compensates for all missed actions. Even if the scheduled task's time has ended, it will execute all the tasks that should have been executed all at once.
int MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1;
/**
 * Misfire mechanism for SimpleTrigger, in the SimpleTrigger class
 **/
 // If the trigger misses the scheduled time, this strategy will immediately execute one task and then continue executing subsequent tasks according to the original plan.
int MISFIRE_INSTRUCTION_FIRE_NOW = 1;
// This strategy will set the start time of the trigger to the current time and immediately execute the missed task, including any missed repeat counts.
int MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT = 2;
// Similar to MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT, but it will ignore the missed trigger counts and only execute the remaining repeat counts.
int MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT = 3;
// This strategy will ignore the missed trigger counts and execute the task at the next scheduled time, executing the remaining repeat counts.
int MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT = 4;
// Similar to MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT, but it will include all the missed repeat counts.
int MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT = 5;
/** 
 * Misfire mechanism for CronTrigger, in the CronTrigger class
 **/
 // If the trigger misses the scheduled time, this strategy will immediately execute one task and then continue executing subsequent tasks according to the original plan.
int MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1;
// For CronTrigger, this strategy will ignore all missed trigger events and wait directly for the next scheduled trigger time.
int MISFIRE_INSTRUCTION_DO_NOTHING = 2;
......
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در QuartzScheduler.insertOrUpdateScheduleTask()، فقط CronTrigger استفاده می شود و کد منبع به شرح زیر است:

CronTrigger cronTrigger = newTrigger()
        .withIdentity(triggerKey)
        .startAt(startDate)
        .endAt(endDate)
        .withSchedule(
                cronSchedule(cronExpression)
                        .withMisfireHandlingInstructionIgnoreMisfires()
                        .inTimeZone(DateUtils.getTimezone(timezoneId)))
        .forJob(jobDetail).build();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پایین تر:

public CronScheduleBuilder withMisfireHandlingInstructionIgnoreMisfires() {
    this.misfireInstruction = -1;
    return this;
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مکانیسم جبران آن با استفاده از -1 رمزگذاری، به این معنی که تمام رویدادهای ماشه از دست رفته بلافاصله اجرا می شوند و تمام اقدامات جبرانی انجام می شود. خب حالا می تونیم توضیح بدیم که چرا بعد از یک ریستارت Master، تمام کارهای دوره ای برنامه ریزی نشده یکبار اجرا میشن!!!

این تنظیم، بسته به ماشه، می‌تواند پارامترهای مختلفی را نیز تنظیم کند:

/**
 * For CronTrigger, refer to settings in CronScheduleBuilder
 **/
public CronScheduleBuilder withMisfireHandlingInstructionIgnoreMisfires() {
    this.misfireInstruction = -1;
    return this;
}
public CronScheduleBuilder withMisfireHandlingInstructionDoNothing() {
    this.misfireInstruction = 2;
    return this;
}
public CronScheduleBuilder withMisfireHandlingInstructionFireAndProceed() {
    this.misfireInstruction = 1;
    return this;
}
/**
 * For SimpleTrigger, refer to settings in SimpleScheduleBuilder
**/
public SimpleScheduleBuilder withMisfireHandlingInstructionIgnoreMisfires() {
    this.misfireInstruction = -1;
    return this;
}
public SimpleScheduleBuilder withMisfireHandlingInstructionFireNow() {
    this.misfireInstruction = 1;
    return this;
}
public SimpleScheduleBuilder withMisfireHandlingInstructionNextWithExistingCount() {
    this.misfireInstruction = 5;
    return this;
}
public SimpleScheduleBuilder withMisfireHandlingInstructionNextWithRemainingCount() {
    this.misfireInstruction = 4;
    return this;
}
public SimpleScheduleBuilder withMisfireHandlingInstructionNowWithExistingCount() {
    this.misfireInstruction = 2;
    return this;
}
public SimpleScheduleBuilder withMisfireHandlingInstructionNowWithRemainingCount() {
    this.misfireInstruction = 3;
    return this;
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

توضیح مکانیسم آتش سوزی در کوارتز

وظیفه را روی “Serial Wait” تنظیم کنید – این امکان پذیر است، اما نمی تواند به طور کامل از مزایای موازی سازی خوشه های کلان داده استفاده کند. همچنین یک نقص مهلک وجود دارد: وظایف تنظیم شده روی انتظار سریال نمی توانند به صورت دستی از طریق صفحه متوقف شوند، و وضعیت باید اصلاح شود یا داده ها در صفحه حذف شوند. t_ds_process_instance جدول

استاد HA: برای یک استاد، یک دیمون برای Master تنظیم کنید تا به طور خودکار پس از خرابی راه اندازی مجدد شود (اگرچه گاهی اوقات نمی تواند راه اندازی مجدد شود). برای Multi-Master، چندین Master را برای فعال کردن HA مستقر کنید.

هشدارهای مانیتورینگ DolphinScheduler: وضعیت اجرای DolphinScheduler را به طور مداوم نظارت کنید. هنگامی که یک نقش با شکست مواجه می شود، یک هشدار به موقع ارسال می شود (ممکن است مواردی وجود داشته باشد که در نیمه شب خرابی اتفاق بیفتد و تیم عملیات متوجه هشدار نشود).

CPU و آستانه استفاده از حافظه را برای DolphinScheduler تنظیم کنید: در فایل پیکربندی، آستانه پیش‌فرض CPU و حافظه روی 70% تنظیم شده است، به این معنی که وقتی میزان استفاده از CPU و حافظه سرور به 70% برسد، DolphinScheduler دیگر کارها را در این سرور برنامه‌ریزی نمی‌کند. مزیت این رویکرد این است که تضمین می کند که منابع سرور به طور کامل استفاده نمی شود. نکته منفی این است که اگر وظایف قدیمی مستحکم در برابر خطا، منابع را اشغال کند، هنگام اجرای عادی بر وظایف جدید در DolphinScheduler تأثیر می گذارد. علاوه بر این، برخی از وظایف بسیار حیاتی هستند و باید با موفقیت اجرا شوند.

تعداد وظایف را برای DolphinScheduler تنظیم کنید: در فایل پیکربندی، تعداد وظایف پیش فرض DolphinScheduler برای هر Worker 100 و برای هر Master 1000 است. در محیط زنده امکان کنترل دقیق تعداد کارها وجود ندارد و DolphinScheduler نمی تواند به طور خودکار تخصیص وظایف را تنظیم کند.

قبل از راه اندازی مجدد پس از شکست، داده ها را در جدول t_ds_command حذف کنید: پس از تأیید، وقتی Master از کار بیفتد، داده‌ها را در آن نمی‌نویسد t_ds_command. داده ها را در آن خواهد نوشت t_ds_command پس از راه اندازی مجدد، اما زمان این کار حدود 1 تا 2 ثانیه است و نمی توان آن را به صورت دستی حذف کرد.

داده ها را در جدول t_ds_process_instance تغییر دهید: بر اساس فواصل زمانی، وضعیت همه گردش‌های کاری را تغییر دهید t_ds_process_instance جدول در آن محدوده زمانی برای پایان دستی آنها (اما اگر DolphinScheduler و پایگاه داده ابرداده روی یک سرور باشند، می تواند به راحتی منابع سرور را پس از راه اندازی مجدد DolphinScheduler پر کند، که ممکن است پایگاه داده ابرداده را غیر قابل دسترس کند).

راه حل های فوق را می توان به طور عمده به موارد زیر تقسیم کرد:

  1. از زمان خرابی Master اجتناب کنید یا آن را کاهش دهید.
  2. وظایف MisFire را پس از توقف Master اجرا نکنید.

اول، “Avoid or Reduce Master Downtime”: دستیابی به این امر در محیط تولید دشوار است. فرض در برنامه های کامپیوتری این است که برخی از مسائل با اطمینان 100٪ در یک نقطه زمانی خاص رخ می دهد، به همین دلیل است که معماری های میکروسرویس مختلف، در دسترس بودن بالا (HA)، چند فعال بودن و مکانیسم های بازیابی فاجعه وجود دارد.

دوم، “وظایف MisFire را اجرا نکنید”: با توجه به راه حل های قبلی هیچ راه حلی نمی تواند این مشکل را به طور کامل حل کند. بنابراین، بر اساس آخرین تجزیه و تحلیل منبع کد، لازم است اصلاح کد منبع و کامپایل مجدد بسته برای حل مشکل در نظر گرفته شود.

کد منبع را اصلاح کنید

کد منبع کلید را به این صورت تغییر دهید:

CronTrigger cronTrigger = newTrigger()
        .withIdentity(triggerKey)
        .startAt(startDate)
        .endAt(endDate)
        .withSchedule(
                cronSchedule(cronExpression)
                        .withMisfireHandlingInstructionDoNothing()
//                        .withMisfireHandlingInstructionIgnoreMisfires()
                        .inTimeZone(DateUtils.getTimezone(timezoneId)))
        .forJob(jobDetail).build();
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تایید محیط توسعه

از جاوا 8 برای تایید استفاده کنید.

تغییر اطلاعات اتصال MySQL در application.yaml فایل های Master، Worker و API:

spring:
  config:
    activate:
      on-profile: mysql
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://IP_ADDRESS:3306/dolphinscheduler?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: USERNAME
    password: PASSWORD
  quartz:
    properties:
      org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تغییر اطلاعات Zookeeper در application.yaml فایل های Master، Worker و API:

registry:
  type: zookeeper
  zookeeper:
    namespace: dolphinscheduler_dev
    connect-string: IP_ADDRESS:2181
    retry-policy:
      base-sleep-time: 60ms
      max-sleep: 300ms
      max-retries: 5
    session-timeout: 30s
    connection-timeout: 9s
    block-until-connected: 600ms
    digest: ~
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

را اصلاح کنید pom.xml تحت BOM:


    mysql
    mysql-connector-java
    ${mysql-connector.version}


وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

را اصلاح کنید logback-spring.xml تحت API، Master و Worker برای فعال کردن گزارش زمان اجرا:

 level="INFO">





     ref="STDOUT"/>
     ref="APILOGFILE"/>


 level="INFO">





     ref="STDOUT"/>
     ref="TASKLOGFILE"/>
     ref="MASTERLOGFILE"/>


 level="INFO">





     ref="STDOUT"/>
     ref="TASKLOGFILE"/>
     ref="WORKERLOGFILE"/>

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

Master، Worker و API را شروع کنید:

Master Options VM: -Dlogging.config=classpath:logback-spring.xml -Ddruid.mysql.usePingMethod=false -Dspring.profiles.active=mysql

گزینه های Worker VM: -Dlogging.config=classpath:logback-spring.xml -Ddruid.mysql.usePingMethod=false -Dspring.profiles.active=mysql

گزینه های API VM: -Dlogging.config=classpath:logback-spring.xml -Dspring.profiles.active=api,mysql

توضیحات تصویر

اگر خطا رخ دهد:

Error running 'ApiApplicationServer'
Error running ApiApplicationServer.
The command line is too long. Shorten the command line via JAR manifest or via a classpath file and rerun.
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس اضافه کنید:

توضیحات تصویر

اگر به دلیل وجود درایور MySQL JDBC، خطا همچنان ادامه داشت، موارد زیر را در قسمت اضافه کنید pom.xml تحت Master، Worker و API:


    mysql
    mysql-connector-java
    8.0.33

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

گردآوری و بسته بندی کامل

توجه به این نکته مهم است که بسته ایجاد شده در این مرحله باید بسته ای باشد که مطابق با آن اصلاح شده است “تغییر کد منبع” محیط، نه محیطی که مطابق با آن اصلاح شده است “تأیید محیط توسعه” قدم!

از جاوا 8 برای بسته بندی استفاده کنید.

دستور زیر را در دایرکتوری ریشه پروژه اجرا کنید (بسته بندی ممکن است کمی طول بکشد):

mvn spotless:apply clean package -Dmaven.test.skip=true -Prelease
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

فایل های باینری بسته بندی شده در آن قرار خواهند گرفت dolphinscheduler-dist/target با .tar.gz پسوند.

سپس، می‌توانید دوباره استقرار کنید تا بررسی کنید که آیا مشکلات قبلی حل شده‌اند یا خیر.

فقط یک ماژول را کامپایل کنید

حرکت به dolphinscheduler-scheduler-quartz دایرکتوری و اجرا:

mvn spotless:apply clean package -Dmaven.test.skip=true -Prelease
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

فایل بسته بندی شده در dolphinscheduler-scheduler-quartz/target دایرکتوری:

توضیحات تصویر

آن را در سرور جایگزین کنید:

su dolphinscheduler -
mv /opt/module/dolphinscheduler-3.2.1/master-server/libs/dolphinscheduler-scheduler-quartz-3.2.1.jar /opt/module/dolphinscheduler-3.2.1/master-server/libs/dolphinscheduler-scheduler-quartz-3.2.1.jar.bak
mv /opt/module/dolphinscheduler-3.2.1/api-server/libs/dolphinscheduler-scheduler-quartz-3.2.1.jar /opt/module/dolphinscheduler-3.2.1/api-server/libs/dolphinscheduler-scheduler-quartz-3.2.1.jar.bak

cp dolphinscheduler-scheduler-quartz-3.2.1.jar /opt/module/dolphinscheduler-3.2.1/master-server/libs/dolphinscheduler-scheduler-quartz-3.2.1.jar
cp dolphinscheduler-scheduler-quartz-3.2.1.jar /opt/module/dolphinscheduler-3.2.1/api-server/libs/dolphinscheduler-scheduler-quartz-3.2.1.jar

chown -R dolphinscheduler:dolphinscheduler /opt/module/dolphinscheduler-3.2.1/
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سپس، می توانید DolphinScheduler را مجددا راه اندازی کنید و بررسی کنید که آیا مشکل قبلی حل شده است یا خیر.

مشکل حل شد

پس از بازتولید مجدد موضوع، مشخص شد که مشکل حل شده است.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا