Camunda المهام الخارجية - أداة قوية لبناء التطبيقات مع بنية مرنة وقابلة للتطوير

صورة

في Tinkoff ، نستخدم إطار Camunda + Spring لتطوير أنظمة أتمتة العمليات التجارية . نحن نصف العمليات التجارية باستخدام BPMN (تدوين إدارة العمليات التجارية) في شكل مخططات انسيابية.

العنصر الأكثر استخدامًا في الرسوم البيانية لدينا هو مهام الخدمة (مستطيل الترس). يدعم Camunda طريقتين لأداء مهام الخدمة :

  1. باستخدام مكالمة متزامنة لرمز جافا.
  2. إنشاء مهمة خارجية.

تسمح لك الطريقة الثانية بتنفيذ المهام باستخدام أنظمة خارجية - على سبيل المثال ، إذا كنت بحاجة إلى استدعاء تطبيق camunda واحد من آخر أو حتى تفويض العمل إلى أي نظام خارجي.

صورة
مثال مخطط BPMN

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

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

مهمة خارجية في BPMN


تتضمن المهمة الخارجية إنشاء مهمة يمكن تنفيذها بواسطة معالج خارجي. جوهر نمط المهمة الخارجية هو:

  1. , «» , «».
  2. camunda , , .
  3. camunda (/).

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

أريد أن يكون تطبيق camunda الخاص بي مسؤولاً فقط عن العمليات التجارية ، وأي تطبيق آخر يجب أن يكون مسؤولاً عن البريد الإلكتروني. في هذه الحالة ، يعمل نموذج المهمة الخارجي بشكل جيد بالنسبة لي . في عملي ، سأقوم ببساطة بإنشاء مهمة نشرة إخبارية بالبريد الإلكتروني وانتظر اكتمالها بواسطة معالج خارجي.

لإنشاء مهمة خارجية في الرسم البياني ، يجب:

  1. إنشاء مهمة عادية .
  2. قم بتغيير نوعه إلى مهمة خدمة .
  3. تعيين التنفيذ إلى خارجي .
  4. حدد قيمة حقل الموضوع .

صورة

الموضوع هو اسم قائمة الانتظار التي ستتم إضافة المهام من نوع واحد إليها والتي سيشترك فيها معالج خارجي.

الآن بعد أن كانت هناك مهمة خارجية في العملية ، يمكنك تشغيلها ، ولكن لن يتم تنفيذها ، حيث لا أحد يعالجها.

عامل المهام الخارجية


نمط المهمة الخارجية جيد لأنه يسمح لك بتنفيذ معالجة المهام بأي لغة ، باستخدام أي أدوات يمكنها تنفيذ طلبات HTTP .

فيما يلي مثال من مدونة camunda . يقوم المثال بتنفيذ معالج جافا سكريبت خارجي يطلب ، كل 20 ثانية ، قائمة بمهام المعالجة من camunda . إذا كانت هناك مهام ، فإنها ترسلها وتخطر camunda حول اكتمال المهمة.

const baseUrl = 'http://localhost:8080/my-app/rest';
const workerSettings = {
 workerId: 'worker01', // some unique name for the current worker instance
 maxTasks: 5,
 topics: [
   {
     topicName: 'sendEmail',
     lockDuration: 10000, // How much time the worker thinks he needs to process the task
     variables: ['video'] // Which variables should be returned in the response (to avoid additional REST calls to read data)
   }]};
const requestParams = {method: 'POST', headers: {contentType: 'application/json'}};

function pollExternalTasks() {
 return fetch(`${baseUrl}/external-task/fetchAndLock`, {
   ...requestParams,
   body: JSON.stringify(workerSettings)
 })
}

function processExternalTask(result = []) {
 return Promise.all(result.map(externalTask => {
   sendEmail(externalTask); // Here the actual work would be done

   return fetch(`${baseUrl}/external-task/${externalTask.id}/complete`, {
     ...requestParams,
     body: JSON.stringify({workerId: workerSettings.workerId}),
   })
 }));
}

setInterval(() => {
 pollExternalTasks().then(processExternalTask)
}, 20000);

وكما ترون من التعليمات البرمجية أعلاه، الطرق الرئيسية للتعامل مع المهام الخارجية هي fetchAndLock و كاملة . يطلب الأسلوب الأول قائمة بالمهام ويؤمن تنفيذها ، ويبلغ الثاني عن اكتمال المهمة. بالإضافة إلى هاتين الطريقتين ، هناك طرق أخرى ، يمكنك أن تقرأ عنها في الوثائق الرسمية .

عميل Camunda مهمة خارجية


صورة

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

مثال على تنفيذ معالج خارجي باستخدام ExternalTaskClient :

public class App {
   public static void main(String... args) {
       // bootstrap the client
       ExternalTaskClient client = ExternalTaskClient.create()
           .baseUrl("http://localhost:8080/engine-rest")
           .asyncResponseTimeout(1000)
           .build();

       // subscribe to the topic
       client.subscribe("sendEmail").handler((externalTask, externalTaskService) -> {
           try {
               String result = sendEmail(externalTask)
               Map<String, Object> variables = new HashMap<>();

               variables.put("result", result);
               externalTaskService.complete(externalTask, variables);
               System.out.println("The External Task " + externalTask.getId() + " has been completed!");
           } catch (e: Exception) {
               externalTaskService.handleFailure(externalTask, e.message, e.stackTrace.toString())
           }
       }).open();
   }
}

إذا كانت مهمتك لا تتطلب فقط القيام ببعض الإجراءات المتزامنة ، ولكن بدء العملية بأكملها ، فيمكنك القيام بذلك ، على سبيل المثال ، عن طريق بدء العملية من خلال RuntimeService :

@Service
class EmailWorker(
   private val runtimeService: RuntimeService
) {
   val builder = ExternalTaskClientBuilderImpl().baseUrl("http://localhost:8080").workerId("myWorker")
   val taskClient = builder.build()
   val engineClient = (builder as ExternalTaskClientBuilderImpl).engineClient

   @PostConstruct
   fun init() {
       taskClient
           .subscribe("sendEmail")
           .lockDuration(10000)
           .handler { externalTask, externalService ->
               runtimeService.startProcessInstanceByKey(
                   "SendEmailProcess",
                   externalTask.getVariable("emailId"),
                   mapOf(
                       "text" to externalTask.getVariable("text"),
                       "email" to externalTask.getVariable("email")
                   )
               )
           }
           .open()
   }


   @PreDestroy
   fun destroy() {
       taskClient.stop()
   }
}

// Delegate from SendEmailProcess process
@Component
class EmailResultDelegate(private val emailWorker: EmailWorker) {
   fun doExecute(execution: DelegateExecution) {
       emailWorker.engineClient.complete(
           execution.readVar(EXTERNAL_TASK_ID),
           mapOf("result" to "Success")
       )
   }
}

في هذا المثال ، يبدأ معالج المهام الخارجية ( EmailWorker ) ، عند تلقي المهمة ، عملية SendEmailProcess .

تخيل أن هذه العملية تنفذ بعض الإجراءات اللازمة لإرسال رسالة إخبارية ، وفي النهاية تستدعي EmailResultDelegate ، والتي بدورها تكمل المهمة الخارجية .

الفوائد المعمارية للمهمة الخارجية


من الجدير بالذكر أن هناك طريقة لبدء العملية في تطبيق camunda آخر بطريقة أبسط: POST: / rest / process-definition / key / $ {id} / start

عند استخدام REST ، ليس لديك أي ضمانات للمعاملات. ولكن بعد كل شيء ، نحن نعمل أيضًا مع مهمة خارجية من خلال REST ، فما الفرق إذن؟

الفرق هو أننا لا نتصل بالخدمة الخارجية مباشرة ، بل ننشر فقط المهام التي يمكن معالجتها. خذ بعين الاعتبار مثال:

صورة

بعض المعالج الخارجي يأخذ مهمة تم تعيينها إليه الآن ، ولكن عند تلقي استجابة ، يتم قطع الاتصال. الآن على جانب camundaتم حظر مهمة لن تتم معالجتها لأن المعالج الخارجي لم يتلق استجابة. ولكن هذه ليست مخيفة: في camunda لل مهام الخارجية هناك مهلة التي مهمة سيعود إلى قائمة الانتظار مرة أخرى وشخص آخر يمكن التعامل معها.

الآن دعونا نلقي نظرة على الحالة عندما تلقى معالج خارجي مهمة ، أكملها ، ودعا الأسلوب الكامل ، الذي فشل بسبب مشاكل في الشبكة. الآن لن تكون قادرًا على فهم ما إذا كانت المهمة قد اكتملت بنجاح في camunda أم لا. يمكنك المحاولة مرة أخرى ، ولكن هناك فرصة لاستمرار مشاكل الشبكة.

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

يمكن أن تحدث مشكلة مشابهة عند بدء عملية جديدة ، لذلك يجب عليك التحقق من المثيلات الموجودة بنفس البيانات ، على سبيل المثال ، مفتاح الأعمال .

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

المزيد عن المهمة الخارجية :
https://docs.camunda.org/manual/latest/user-guide/process-engine/external-tasks/
https://docs.camunda.org/manual/latest/reference/rest/external -task /
https://docs.camunda.org/manual/latest/user-guide/ext-client/

All Articles