Минуты исследования Activiti6.0

Java

предисловие

В начале года для создания платформы автоматизации эксплуатации и обслуживания понадобился движок процессов, изначально проект планировалось написать на golang, но функция движка процессов golang слишком проста для использования. конец, я выбрал java + activiti. Я зашел на официальный сайт activiti, чтобы увидеть, что 7.0 вышла.Результирующий документ еще не завершен.Наша java все еще 8, а 7.0 соответствует java11.В конце концов, слишком много проблем, и я должен сдаться. с помощью активности 6.0.

пересечь реку, ощупывая камни

Несмотря на то, что существует множество онлайн-руководств, запустить его действительно непросто.Некоторое содержание, такое как значение параметров, можно понять, только прочитав руководство 5.0.

Установить

Разрешите зависимости и запустите первым

Среда сборки: java8 + springboot2.1.3 + activiti6.0 + mysql

пом зависимости:

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter-basic</artifactId>
	<version>${activiti.version}</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.15</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>

Настройте базу данных:

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/activiti?nullCatalogMeansCurrent=true&serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root

Тогда запускайте, поздравляю, приложение не запускается, появится следующая ошибка:

Caused by: java.io.FileNotFoundException: class path resource [org/springframework/security/config/annotation/authentication/configurers/GlobalAuthenticationConfigurerAdapter.class] cannot be opened because it does not exist:

Причина этого в том, что на момент разработки activiti 6.0 еще не вышел springboot 2.0, поэтому он давно в негодности, а путь к файлу неверный.Решение:

Исключить SecurityAutoConfiguration перед началом занятия

@SpringBootApplication(exclude = SecurityAutoConfiguration.class)

Начните снова, если вы столкнулись с проблемой создания таблицы базы данных, но не можете найти таблицу, есть два решения:

  • Вручную создайте таблицу, путь к таблице указан вactiviti-engine-6.0.0.jar/org/activiti/db/create
  • Добавить в конфигурацию базы данныхnullCatalogMeansCurrent=true

Это потому, что вorg/activiti/engine/impl/db/DbSqlSessionиспользовать в деятельностиdatabaseMetaData.getTablesчтобы узнать, существует ли библиотечная таблица, аdbSqlSessionFactoryполученныйcatalogпуст, потому что mysql используетschemaИдентифицирует имя библиотеки вместоcatalog, что заставляет mysql сканировать все библиотеки, чтобы найти таблицу.Как только в других библиотеках есть таблица с таким же именем, activiti думает, что она была найдена, но таблица не существует.nullCatalogMeansCurrentСмысл в том, чтобы позволить mysql по умолчанию использовать текущую библиотеку, вmysql-connector-java 5.xЭтот параметр по умолчанию имеет значение true, но по умолчанию имеет значение false в 6.x и более поздних версиях.

Начать снова, все еще не вверх:

Caused by: java.io.FileNotFoundException: class path resource [processes/] cannot be resolved to URL because it does not exist

Это связано с тем, что activiti будет искать файлы процессов в папке resources/processes и создавать этот каталог; добавьте в application.propertiesspring.activiti.check-process-definitions=false

Настройте плагин для рисования

Activiti предоставляет множество дизайнеров для рисования блок-схем.Я установил подключаемый модуль IDEA и подключаемый модуль eclipse.По сравнению с eclipse он прост в использовании, поэтому я представляю только процесс установки подключаемого модуля eclipse.

  1. следующее затмение
  2. Загрузите справку плагина --> Установить новое программное обеспечение --> Добавить:
Names : Activiti BPMN 2.0 designer
Location : https://www.activiti.org/designer/update/
  1. Завершена загрузка Новая диаграмма

4. Нарисуйте блок-схему.Стандартная блок-схема имеет начальное и конечное событие.После рисования сохраните ее как файл .bpmn или .bpmn20.xml и поместите в каталог ресурсов/процессов.Пример файла xml выглядит следующим образом: следует:

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test" id="m1551347612734" name="">
  <process id="machine_expansion" name="Machine Expansion" isExecutable="true" isClosed="false" processType="None">
    <startEvent id="start" name="开始"></startEvent>
    ...

бегать

Используйте runtimeservice для создания createProcessInstanceBuilder, задайте для processDefinitionKey идентификатор процесса в XML, а затем запустите.

    @Autowired
    private RuntimeService runtimeService;
    @Override
    public String start(BaseParamDTO baseParamDTO) {
        String currentUserName = CurrentUserUtil.getCurrentUserName();
        identityService.setAuthenticatedUserId(currentUserName);
        ExpansionParamDTO expansionParamDTO = (ExpansionParamDTO) baseParamDTO.getParams();
        ProcessInstance machineExpansion = runtimeService.createProcessInstanceBuilder().processDefinitionKey(WorksheetTypeEnum.EXPANSION.getActivitiDefineName())
                .businessKey(expansionParamDTO.getAppName()).name(WorksheetTypeEnum.EXPANSION.name()).start();
        baseParamDTO.setId(machineExpansion.getProcessInstanceId());
        submit(baseParamDTO);
        return machineExpansion.getProcessInstanceId();
    }

Чтобы установить начального пользователя процесса, необходимо использовать настройку identityService.setAuthenticatedUserId, поскольку процесс получает пользователя текущего потока.

Таблица базы данных

Самый простой способ познакомиться с проектом — посмотреть, как устроены его таблицы базы данных и что в них хранится.Всего в базе данных activiti 28 таблиц.

  • ACT_GE_*: Общая таблица, как правило, не нужно обращать внимание, если идентификатор двоичных данных появляется в других таблицах, например, выдает ошибку, перейдите к act_ge_bytearray для запроса
  • ACT_HI_*: историческая информация, историческая информация об активности хранится на 4 уровнях, которые можно использоватьspring.activiti.historyLevelКонфигурация, соответствующая HistoryService
    • none: ничего не сохранять
    • активность: сохраняет все экземпляры процесса, задачи и информацию о деятельности;
    • Аудит: уровень по умолчанию, на котором сохраняются все экземпляры процессов, задачи, действия и атрибуты форм;
    • полный: все сохраняется в процессе, с большим количеством деталей и переменных, чем аудит;
  • ACT_ID_*: информация аутентификации пользователя, соответствующая IdentityService;
  • ACT_RE_*: развертывание и определение в процессе, соответствующем RepositoryService;
  • ACT_RU_*: Информация о времени выполнения.Таблица, относящаяся к уровню процесса, соответствует RuntimeService, таблица уровня задачи соответствует TaskService, а таблица уровня задания соответствует ManagementService.
Имя таблицы описывать
act_evt_log Таблица журнала событий
act_ge_bytearray Таблица двоичных данных, например, ошибка броска
act_ge_property лист свойств активности
act_hi_actinst Исторические экземпляры операций процесса
act_hi_attachment Исторические вложения процесса
act_hi_comment историческая описательная информация
act_hi_detail Детали исторических запусков процессов
act_hi_identitylink Историческая информация о взаимоотношениях пользователей
act_hi_procinst Исторические экземпляры процесса
act_hi_taskinst исторический экземпляр пользовательской задачи
act_hi_varinst Исторические параметры процесса
act_id_group группа пользователей
act_id_info Данные пользователя
act_id_membership Ассоциации пользователей и групп пользователей
act_id_user Информация о пользователе
act_procdef_info Таблица расширения определения процесса
act_re_deployment Информация о развертывании процесса
act_re_model Модель процесса
act_re_procdef Развернутый процесс
act_ru_deadletter_job Вакансии, которые умерли во время выполнения
act_ru_event_subscr Слушатель событий во время выполнения
act_ru_execution Экземпляр выполнения процесса во время выполнения
act_ru_identitylink Информация о взаимоотношениях пользователей во время выполнения
act_ru_job работа во время выполнения
act_ru_suspended_job Задание, которое приостановлено во время выполнения
act_ru_task экземпляр usertask во время выполнения
act_ru_timer_job Работа таймера времени выполнения
act_ru_variable параметры времени выполнения

описывать вещи с примерами

основное определение

В деятельности есть несколько основных определений:

  • процесс: экземпляр процесса, например, процесс утверждения bpmn определен, и теперь он запущен, это экземпляр процесса.
  • выполнение: Экземпляр выполнения процесса.В процессе много шагов, и каждый шаг запускает экземпляр выполнения процесса.
  • задача: относится конкретно к пользовательской задаче. Когда создается пользовательская задача, activiti создает задачу в процессе выполнения.
  • job: При выполнении шага каждое выполнение будет генерировать задание для выполнения jobExecutor.Если это общее задание, оно находится в act_ru_job, а запланированное задание — в act_ru_timer_job.Когда задание приостановлено, оно войдет в act_ru_suspended_job, а если не получится, то пойдет на act_ru_deadletter_job.
  • активность: это слово появляется в таблице истории. Оно похоже на действие в понимании. Разница в том, что действие является экземпляром выполнения. Экземпляр выполнения может выполнять несколько действий, а действие относится к шагу в процессе, который соответствует шаг в bpmn.

Разница между несколькими часто используемыми задачами

В конструкторе затмений всего 8 задач, на данный момент я использую только 4 из них.

Задача пользователя

Пользовательские задачи используются для установки задач, требующих действий человека, таких как утверждение.При выполнении пользовательской задачи процесс зависает, и процесс будет продолжаться только в том случае, если пользователь вручную запустит процесс. Пример следующий.Здесь пользователь не записывается насмерть при отрисовке bpmn,а через taskService в процессе ставится assignee.Usertask может выполняться только полным,а последующие процессы могут управляться параметрами и эксклюзивными шлюзами.

    @Override
    public void submit(BaseParamDTO baseParamDTO) {
        String currentUserName = CurrentUserUtil.getCurrentUserName();
        ExpansionParamDTO expansionParamDTO = (ExpansionParamDTO) baseParamDTO.getParams();
        Task submit = taskService.createTaskQuery().processInstanceId(baseParamDTO.getId()).taskDefinitionKey(ExpansionDisplayEnum.SUBMIT.getFlow()).singleResult();
        taskService.setAssignee(submit.getId(), currentUserName);
        taskService.setVariable(submit.getId(), INPUT_PARAM, expansionParamDTO);
        taskService.setVariable(submit.getId(), INPUT_PRIORITY, baseParamDTO.getPriority());
        taskService.setPriority(submit.getId(), baseParamDTO.getPriority());
        if (StringUtils.isNotBlank(baseParamDTO.getComment())){
            taskService.addComment(submit.getId(), null, baseParamDTO.getComment());
        }
        taskService.complete(submit.getId());
    }

Пользовательские задачи включают в себя несколько понятий:

  • цессионарий: исполнитель задачи, кто ее выполнит, напишет кому угодно
  • Кандидаты-пользователи: кандидаты на эту задачу, кого нужно выполнить.
  • Кандидаты в группы: Группы кандидатов для выполнения задачи, напишите, какие группы необходимы для выполнения группой людей.
  • комментарий: обратите внимание, эта информация будет записана в таблицу act_hi_comment, которая может быть связана только с пользовательской задачей или процессом.

Задача сценария

Напишите сценарий, который автоматически выполняет сценарий при выполнении шага. (Не используется)

Сервисная задача

Наиболее часто используемая задача, при выполнении шага автоматически выполняется соответствующий класс Java.

@Component
public class ApplyMachineTask implements JavaDelegate {

    public static final Logger LOGGER = LoggerFactory.getLogger(ApplyMachineTask.class);

    public static final String MACHINE_APPLY_ID = "applyId";
    public static final String MACHINE_APPLY_ID_LOCAL = "localApplyId";
    public static final String MACHINE_APPLY_CHECK = "applyCheckCount";

    @Autowired
    private MachineService machineService;

    @Override
    public void execute(DelegateExecution delegateExecution){
        ExpansionParamDTO param = delegateExecution.getVariable(INPUT_PARAM, ExpansionParamDTO.class);
        LOGGER.debug("start apply machine, param {}", param);
        param.setWorksheetId(delegateExecution.getProcessInstanceId());
        ApiResponseDTO<String> stringApiResponseDTO = machineService.applyMachine(param);
        if (stringApiResponseDTO.isSuccess()){
            delegateExecution.setVariable(MACHINE_APPLY_ID, stringApiResponseDTO.getBody());
            delegateExecution.setVariableLocal(MACHINE_APPLY_ID_LOCAL, stringApiResponseDTO.getBody());
            delegateExecution.setVariable(MACHINE_APPLY_CHECK, 0);
        } else {
            delegateExecution.setVariableLocal(ERROR_LOCAL, stringApiResponseDTO.getError());
            throw new BpmnError(ERROR_RETRY, stringApiResponseDTO.getError());
        }
    }
}

Вот настройки параметров,setVariableБудет установлена ​​глобальная переменная процесса, то есть параметры, которые могут быть получены на любом шаге процесса, используемые для передачи параметров, иsetVariableLocalПараметр представляет собой локальную переменную, параметр, который можно получить только на текущем шаге. Целью локальной переменной является запись состояния процесса, когда процесс выполняется на шаге. Параметр связан с проблемой покрытия, поэтому не имеют феномена, что глобальные и локальные параметры имеют одно и то же имя (за исключением подпроцесса).

Задача бизнес-правила (задача BusinessRule)

Пользователь бизнес-правил используется для синхронного выполнения одного или нескольких правил. Activiti использует механизм правил drools для выполнения бизнес-правил. (Не используется)

Почтовая задача

Автоматические почтовые задачи, которые могут отправлять почту одному или нескольким участникам. (Не используется)

Ручная задача

Задачи, выполняемые вручную, определяют задачи, внешние по отношению к механизму BPM. Используется для обозначения того, что работу должен выполнять кто-то, и движку не нужно знать, и нет соответствующего интерфейса системы и пользовательского интерфейса. Для движка ручная задача — это прямое сквозное действие, и когда процесс достигает ее, он автоматически выполняется вниз. (Не используется)

Получить задание

Задача получения — это простая задача, ожидающая поступления соответствующего сообщения. После того, как процесс достигнет этого шага, он будет ждать, пока сообщение не будет запущено. Как показано на рисунке ниже, на принимающей задаче зависает событие границы времени.Событие границы времени запускает задачу службы для проверки состояния через регулярные промежутки времени. задача завершена, и шлюз временной границы останавливается.

Конфигурация временного пограничного шлюза
триггер сообщения:

 Execution receiveTask = runtimeService.createExecutionQuery().processInstanceId(execution.getProcessInstanceId())
    .activityId(ExpansionDisplayEnum.WAIT_INIT.getFlow()).singleResult();
runtimeService.trigger(receiveTask.getId());

Подпроцесс вызова (CallActivity)

Есть два подпроцесса активности, один CallActivity, а другой подпроцесс. Разница в том, что CallActivity — это процесс вне основного процесса. Во время выполнения будет сгенерирован новый идентификатор процесса, а область действия полностью независима. Обычно это используется для повторного использования других процессов или потребности Случай независимого идентификатора процесса; хотя подпроцесс является вложенным подпроцессом, он не создает новый идентификатор процесса, а определяет независимый домен событий, который обычно используется для захвата событий того же типа.

  1. Нарисуйте подпроцесс. Вы не можете выбрать здесь асинхронный. Если вы выберете его, вы не сможете запустить. Насколько я понимаю, вызов CallActivity и выполнение нового подпроцесса выполняются разными потоками. Выполняется ли процесс асинхронно настраивается на странице с несколькими экземплярами.
  2. На главной странице конфигурации настраивается идентификатор процесса вызываемого подпроцесса и параметры, которые необходимо передать.Параметр здесь означает, что привилегия имени параметра основного процесса передается и называется параметром имени параметра param.
  3. многоэкземплярная многоэкземплярная конфигурация страницы, последовательное — это место, где конфигурация является синхронной или асинхронной, цикл может напрямую указывать количество экземпляров, здесь решается путем передачи параметров, определение коллекции означает, что будет список глобальных параметров с именем privetList , каждый параметр в списке. Имя экземпляра — привилегия, а условие завершения задает условие завершения. Когда создается несколько экземпляров, activiti автоматически создает несколько параметров:
  • nrOfInstances: общее количество экземпляров
  • nrOfActiveInstances: текущие активные экземпляры
  • nrOfCompletedInstances: Завершенные экземпляры

Установленное здесь условие состоит в том, что основной процесс продолжается, когда все завершено.

Мама, я хочу использовать try catch

В Activiti есть два типа событий: одно — событие процесса, которое помещается в процесс, а другое — граничное событие, которое косвенно инициируется процессом, выполняющимся на определенном этапе, и эти два вида событий делятся на две категории, одна заключается в том, чтобы захватить одну, чтобы вызвать.

  • Перехват: когда процесс выполняется по событию, он ожидает запуска.
  • Бросок: Когда процесс выполняется для события, событие будет запущено.

Здесь мы хотим поговорить о событии захвата граничных ошибок.

Как показано на рисунке, все шаги запуска приложения помещаются во встроенный подпроцесс подпроцесса, а затем в подпроцессе монтируется событие захвата границы ошибки. кнопка срабатывает, если ее перезапустить, то весь подпроцесс будет выполняться заново, а если этот процесс пропустить, то процесс продолжится.

В событии границы ошибки есть параметр errorCode, errorCode используется для сопоставления захваченной ошибки:

  • Если errorRef не задан, граничные события ошибок будут перехватывать все события ошибок, независимо от errorCode ошибки.
  • Если errorRef установлен и ссылается на существующую ошибку, граничное событие будет перехватывать только ошибки с тем же кодом ошибки.
  • Если errorRef установлен, но ошибка не определена в BPMN 2.0, errorRef будет использоваться как errorCode (аналогично использованию события окончания ошибки).

Настройте эксклюзивный шлюз и запишите условия для каждого пути

Попробуйте поймать ошибки на каждом шаге подпроцесса и сбросить

delegateExecution.setVariableLocal(ERROR_LOCAL,stringApiResponseDTO.getError());
throw new BpmnError(ERROR_RETRY, stringApiResponseDTO.getError());

Следует отметить, что текст ошибки в броске ошибки, похоже, недоступен, поэтому его все же необходимо сохранить в локальной переменной.

Как одобрить и отклонить

Поскольку я хочу записать все этапы операции, я разделил каждую операцию, чтобы появился процесс утверждения, показанный на рисунке ниже.

Первая задача отправки выполняется непосредственно в начале процесса, а затем прослушиватель используется для установки кандидата на утверждение перед началом этапа утверждения.Если текущий пользователь является кандидатом на утверждение, процесс утверждения пропускается напрямую. передается или не добавляется через эксклюзивный шлюз, контролируемый указанными выше параметрами.

Установите прослушиватель в пользовательской задаче утверждения, чтобы установить кандидатов на утверждение для задачи.

@Component
public class ApprovalTaskListener implements TaskListener {

    private static final Logger LOGGER = LoggerFactory.getLogger(ApprovalTaskListener.class);

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private ApprovalTask approvalTask;

    @Autowired
    private ApplicationService applicationService;

    @Autowired
    private TaskService taskService;

    @Override
    public void notify(DelegateTask delegateTask) {
        String processInstanceId = delegateTask.getProcessInstanceId();
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        String appName = processInstance.getBusinessKey();
        String owner = processInstance.getStartUserId();
        Integer priority = delegateTask.getVariable(INPUT_PRIORITY, Integer.class);
        taskService.setPriority(delegateTask.getId(), priority);
        ApiResponseDTO<ApplicationDTO> application = applicationService.getApplication(appName);
        if (application.isSuccess()){
            List<String> bizMaintainer = application.getBody().getBizMaintainer();
            delegateTask.addCandidateUsers(bizMaintainer);
            //申请人是审批人时直接处理
            if (bizMaintainer.contains(owner)){
                taskService.setAssignee(delegateTask.getId(), owner);
            }
        } else {
            LOGGER.error("get application info error, pId: {}, app: {}", processInstanceId, application);
            throw new RuntimeException(application.getError());
        }
    }
}

Установите прослушиватель ApprovalSkipListener выполнения на внешнем уровне пользовательской задачи утверждения. Как упоминалось ранее, выполнение выполняется перед пользовательской задачей. Каждая пользовательская задача имеет выполнение на внешнем уровне, поэтому этот прослушиватель может контролировать поведение пользовательской задачи.

@Component
public class ApprovalSkipListener implements ExecutionListener {

    private static final Logger LOGGER = LoggerFactory.getLogger(ApprovalSkipListener.class);

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private ApplicationService applicationService;

    @Override
    public void notify(DelegateExecution execution) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(execution.getProcessInstanceId()).singleResult();
        String appName = processInstance.getBusinessKey();
        String owner = processInstance.getStartUserId();
        ApiResponseDTO<ApplicationDTO> application = applicationService.getApplication(appName);
        if (application.isSuccess()){
            List<String> bizMaintainer = application.getBody().getBizMaintainer();
            if (bizMaintainer.contains(owner)){
                execution.setVariable(SKIP_ENABLE,true);
                execution.setVariable(APPROVAL_PASS, true);
            }
        } else {
            LOGGER.error("get application info error, pId: {}, app: {}", processInstance.getId(), application);
            throw new RuntimeException(application.getError());
        }
    }
}

Установить условие пропуска

Если она будет отклонена, процесс снова вернется к отправке задачи, требуя от пользователя повторной отправки, и в базе данных появится новая запись.

откатиться назад

Очень интересной функцией activiti является компенсация, а откат выполняется с помощью событий триггера компенсации и событий фиксации граничной компенсации. Когда компенсация срабатывает, activiti запускает события захвата компенсации в последовательности от конца к началу.

  1. Нарисуйте триггерное событие компенсации
  2. Нарисуйте событие захвата граничной компенсации
    Приведенный выше рисунок означает, что когда процесс достигает периода молчания, принимающая задача зависает и запускает событие граничного времени.Если время истекло, машина отключится, и процесс завершится.Если время не достигнуто, но срабатывает сообщение о периоде молчания, это будет «Принять триггерное событие компенсации», запустить приложение, и откат будет успешным.

Многие проблемы решаются медленно

Почему миссия не записывается?

Говоря об этой проблеме, необходимо упомянуть асинхронные задачи:

  • async: определяет, выполняется ли задача асинхронно.Если это правда, это означает, что задача не выполняется непосредственно jobExecutor, а генерирует дополнительное задание и помещает его в пул рабочих потоков для выполнения рабочим потоком.

Заимствуйте официальное изображение, чтобы кратко объяснить. Activiti выполняет процесс транзакционным образом.Когда процесс запускается, activit будет продолжать продвигать процесс, пока не встретит состояние ожидания, затем сохранит текущее состояние в базе данных и будет ждать следующего триггера. Как показано на рисунке ниже, пользовательская задача — это первое состояние ожидания, таймер — второе состояние ожидания, а завершение пользовательской задачи и выполнение служебных задач находятся в одной и той же рабочей единице, а успех и неудача в одной и той же рабочей единице являются атомарными. Если сервисная задача выполнена успешно, то, что вы видите в базе данных, является завершенной пользовательской задачей и завершенной сервисной задачей. Если сервисная задача завершится неудачно, это заставит activti откатить транзакцию. В это время она вернется в исходное состояние. ● Запись выполненных пользовательских задач, но не запись служебных задач.

Если я хочу увидеть статус выполнения сервисной задачи, решение состоит в том, чтобы установить для async значение true и передать сервисную задачу в фоновый режим для выполнения.JobExecutor в фоновом режиме будет периодически сканировать задание базы данных и отправлять его рабочему пул потоков. Как показано на рисунке ниже, сервисная задача устанавливает для async значение true.В это время существует три состояния ожидания: первое — пользовательская задача, второе — сервисная задача и третье — таймер. Теперь процесс начинается.После завершения пользовательской задачи будет создано задание задачи обслуживания в состоянии ожидания и сохранено в базе данных, а затем выполнено пулом рабочих потоков.Если выполнение задания задачи обслуживания не удается, задание еще в базе. Если почтовая задача завершается сбоем, она откатывается к исходному состоянию служебной задачи, как и раньше.

почему задание не выполняется

Это тоже часто встречающаяся яма, и она еще не решена, что предполагает эксклюзивные задания.

  • эксклюзивный: указывает, является ли задача эксклюзивной задачей.Если это правда, рабочий поток выберет все эксклюзивные задачи одного и того же экземпляра процесса и выполнит их последовательно.

Или, заимствуя официальную диаграмму, предполагая следующий сценарий, три служебных задачи являются асинхронными, и jobExecutor назначит три задания для выполнения в пул рабочих потоков.Если они одновременно достигнут шлюза параллельной агрегации, будет проблема согласованности.Поскольку каждая ветвь будет проверять, прибыла ли другая ветвь, возникает ситуация ожидания.

Чтобы решить эту проблему, Activiti использует оптимистическую блокировку. Когда все ветви сойдутся, они обновят номер версии экземпляра процесса. Если какая-либо ветвь не обновится, она будет заблокирована на определенный период времени, а затем повторит попытку. Но проблема с оптимистической блокировкой заключается в том, что невыполненное задание по умолчанию повторяется только три раза, а задание не является транзакционным, что может привести к дублированию данных. Следовательно, существуют эксклюзивные задачи. Когда эксклюзивный имеет значение true, он гарантирует, что activiti не будет выполнять две эксклюзивные задачи для одного и того же экземпляра процесса одновременно. То есть, когда асинхронный и эксклюзивный оба являются истинными, это не настоящий параллелизм. , а для эксклюзивных версий 6.0 по умолчанию установлено значение true.

Логически, только когда асинхронность истинна, а эксклюзивность ложна, задание будет заблокировано и не будет запущено, но это не так, поэтому я подозреваю, что эта проблема вызвана тем, что несколько машин получают задания одновременно, и это остается исследованы позже.

постскриптум

YangyangsaSa наконец-то закончил писать сводку по активити, но это пока что, осталось еще много функций, которые не опробованы, и баги не обнаружены. Революция еще не удалась, и товарищам еще нужно потрудиться.