Flowable事务引发的一些思考

博客围绕系统流程实例中的事务问题展开。先指出第三方接口通信不佳或内部业务异常时,事务回滚会导致流程无法继续。接着说明流程开启到结束是大事务,存在诸多麻烦,需拆分事务分段提交。但分段提交后又出现脏读问题,查询事务也需开启新事务读取。

前言

最近将公司业务迁移到工作流框架上,在使用Flowable时遇到一个关于事务的问题,困扰了两天,便记录下来。
另 : Flowable的具体使用本文不进行赘述,等忙完这阵子整理一下!

问题发现

假设系统有这么一个流程实例
在这里插入图片描述
实例中,由两个 服务任务 组成,内部业务可能是调用第三方接口,也有可能处理自己的业务逻辑。
假设第三方接口通信不佳,在订单发起节点出现异常 :
1.节点内部没有try-catch的情况下,节点内部操作均会回滚。
2.流程不会执行到下一个节点,即配送环节。

假设配送环节内部业务异常:
1.节点内部没有try-catch的情况下,自身节点事务操作回滚,前面的订单发起节点操作也会回滚。
2.流程同样不会往下执行。

由此可见,流程实例启动后是一个大事务
期望 :
事务回滚是良性效果,但是希望异常节点后的节点不运行,可以直接到达结束节点!
参考资料
值得参考的好文章
作者的两篇文章描述很清楚

大事务引发的问题

上面的问题只是一个引子,解决如何终止流程。事务的问题才刚刚开始!
上文我得出结论 流程启动是一个大事务,我们先从源码中验证这一结论!
从一个流程实例的启动开始
runtimeService.startProcessInstanceByKeyAndTenantId
其内部实现,调用的是 CommandExecutorImpl 类的

public <T> T execute(CommandConfig config, Command<T> command) {
        return this.first.execute(config, command);
    }

this.first 是一个 CommandInterceptor 实现类,可以理解为是命令设计模式,
两个重要的实现类
SpringTransactionInterceptor

public <T> T execute(CommandConfig config, Command<T> command) {
        LOGGER.debug("Running command with propagation {}", config.getTransactionPropagation());
        int transactionPropagation = this.getPropagation(config);
        if (transactionPropagation == 0 && TransactionSynchronizationManager.isActualTransactionActive()) {
            return this.next.execute(config, command);
        } else {
            TransactionTemplate transactionTemplate = new TransactionTemplate(this.transactionManager);
            transactionTemplate.setPropagationBehavior(transactionPropagation);
            return transactionTemplate.execute((status) -> {
                return this.next.execute(config, command);
            });
        }
    }

设置了事务的传播行为为REQUIRED
TransactionContextInterceptor

public <T> T execute(CommandConfig config, Command<T> command) {
        CommandContext commandContext = Context.getCommandContext();
        boolean openTransaction = !config.getTransactionPropagation().equals(TransactionPropagation.NOT_SUPPORTED) && this.transactionContextFactory != null && !commandContext.isReused();
        boolean isContextSet = false;

        Object var10;
        try {
            if (openTransaction) {
                TransactionContext transactionContext = this.transactionContextFactory.openTransactionContext(commandContext);
                Context.setTransactionContext(transactionContext);
                isContextSet = true;
                commandContext.addCloseListener(new TransactionCommandContextCloseListener(transactionContext));
            }

            var10 = this.next.execute(config, command);
        } finally {
            if (openTransaction && isContextSet) {
                Context.removeTransactionContext();
            }

        }

        return var10;
    }

设置了流程开启到结束的事务开启。
关于Flow的事务源码,请查看网上大佬的文章

由此可见,流程的开启到结束是一个大事务

因为服务调用是在当前事务里,数据的产生或改变,在服务任务执行完之前,还没有提交到数据库.所以API对于数据库数据的操作,意味着未提交的操作在服务任务的API调用中都是不可见的

不用我提,大伙也会知道,大事务有诸多麻烦。
只要这个大事务不提交,节点的操作对其他事务都是不可见

简单的,如果我 订单发起节点 将订单状态置为Running,页面显示的仍然是非Runnning状态。

那么,我一个配送流程的节点很多,执行时间很长,肯定的,我需要拆分事务,分段提交。

事务分段提交后的一些问题

上文中,我们知道,流程开启的默认的事务传播行为是 REQUIRED,即如果事务存在,方法会加入当前事务,否则新起一个事务运行。显示,如果方法需要自己提交,那么就不能使用 REQUIRED,于是,在所有update操作方法上加上PROPAGATION_REQUIRES_NEW,即启用一个新的事务执行方法。
一些问题 :
update操作都加上新事务,流程节点的查询还是沿用之前的事务,我们知道
mysql默认是 可重复读, 事务启动时,视图可以认为是静态,不受其他事务更新的影响
也就是说,如果需要配送两个节点,第一个节点配送完成,更改状态为Finishi,继续第二个节点,此时查询仍然是Finishi的这条数据,陷入无限循环,出现脏读。
因为查询的事务是在流程开启的时候就启动了,update事务不会影响到它,显然,我们要的结果是需要影响到它的读。
显然,查询事务也需要开启新事务去读取。

本课程是《Flowable流程入门课程》的后续高级课程。在学习本课程前,应先学习入门课程,以掌握相关基础知识。高级课程着重讲解Flowable工作流的高级概念、复杂理论和实战应用。课程内容包括流程管理思想、技术与标准、工作流的控制模式和资源模式;Flowable数据库表及变量;与Spring、Spring Boot的集成;BPMN 2.0主要类图;Flowable高级服务如JAVA服务任务、脚本任务、Web Service任务、外部工作者任务、多实例任务、补偿处理程序、子流程和调用活动等;Flowable事件侦听器、执行侦听器和任务侦听器;Flowable历史和REST API;Flowable事务、并发性、身份管理及LDAP集成;Flowable高级主题如流程实例迁移、异步执行器的设计与配置、用于高并发的UUID ID生成器、多租户、高级流程引擎配置、执行自定义SQL和实验性流程调试器等;Flowable Eclipse设计器特性及定制;Flowable 事件注册;Flowable相关标准和规范如ISO8601标准和cron等。本课程对Flowable官方文档进行了彻底梳理和融汇贯通,并结合实践,形象生动、系统全面、简单易懂地呈现给大家,让大家从开源软件文档冗长耗时、英文晦涩难懂、概念理解困难、知识点分散等困境中解脱出来,从而能快速地将Flowable具有的高级特性应用到项目的高级需求和复杂实践中去。课程特色:案例和代码驱动、基础概念与经典实战相结合、知识环节融会贯通、关联知识平滑拓展、概念和原理展示形象生动。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值