Dieses Blog durchsuchen

Montag, 11. April 2022

Solution for long running tasks in Camunda: External tasks

One of my recent jobs was to design a bpmn-process with Camunda for one of my clients.

The requirements were clear and not too complicated. I had to struggle with some technical challenges regarding Camunda though. One of these challenges was that a task within the process can take some time. Standard synchronous Camunda tasks are not designed to do long lasting work by design.

At my client the Camunda engine runs within a Wildfly application server and therefore uses the transaction handling of Wildfly. The default transaction timeout for a Wildfly managed database transaction is 10 minutes. This means that your task's transaction will also timeout after 10 minutes which might not be enough in particular cases. Of course you could increase the transaction timeout, but this will affect all applications deployed within wildfly. And what if your transaction has to run for several days or weeks? Consulting the Camunda forum (Camunda has a great community) and the documentation I found a solution in using "external tasks".

External tasks enable you to provide a work load to an external worker. This way the task's transaction can finish right away and let some other service deal with the complexity of the long running job. This service can use its own transaction handling completely independent of the Camunda process.

The following steps are necessary to implement an external task:

  • Activate the Camunda REST-API (it is probably also possible to use external tasks with the Camunda Java API but I have found only examples with the REST-API). The REST-API is activated by extending the Application class and overriding the getClasses method: (only applicable for a Java EE application)

@ApplicationPath("/camunda-rest")
public class CamundaRestApi extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        // setting up Camunda REST service
        Set<Class<?>> classes = new HashSet<Class<?>>();
        // add camunda engine rest resources
        classes.addAll(CamundaRestResources.getResourceClasses());
        // add mandatory configuration classes
        classes.addAll(CamundaRestResources.getConfigurationClasses());
        return classes;
    }
}

Besides the file org.camunda.bpm.engine.rest.spi.ProcessEngineProvider with the content org.camunda.bpm.engine.rest.spi.ProcessEngineProvider has to be placed under the application's META-INF/services path: my-application\src\main\webapp\META-INF\services

  • Define your service task in your bpmn-file by setting the caunda:type attribute to external und providing a topic name. You can choose any topic name you want:
<bpmn:serviceTask id="lotsOfWorkId" name="do a long running task" camunda:type="external" camunda:topic="lotsOfWorkTopic">
  • Create and an external task client and subscripe to the topic:
public static ExternalTaskClient getExternalTaskClient() {
    return externalTaskClientBuilder()
        .baseUrl("https://myHost/MyWebapp/camunda-rest")
        .workerId("lotsOfWorkWorkerId")
        .build();
}

getExternalTaskClient()
    .subscribe("lotsOfWorkTopic")
    .handler(lotsOfWorkHandler)
    .open();
  • The external task handler is the place where the actual business logic is defined that will be executed when a task is written to the topic. This handler is not bound to any transactions and can define on its own how the work should be executed. The handler implements the ExternalTaskHandler interface.
public class LotsOfWorkHandler implements ExternalTaskHandler {
    @Override
    public void execute(final ExternalTask externaltask, final ExternalTaskService service) {
    //do some long lasting work...
    //...
    //complete the task
    externalTaskService.complete(externaltask);
}

By using external tasks I was able to let a task run for a very long time and leave the default transaction timeout to 10 minutes. One disadvantage of this approach is, that all variables that are passed to and from the external task handler have to be serializable because they are transferred over http through the REST API. I was not able to use JSON directly (Camunda uses the Spin API for this) but had to pass a List<String> and convert it to JSON within the task itself.


Keine Kommentare:

Kommentar veröffentlichen