Activiti工作流--分布式实现方案
一、运行环境
以下所有的描述都是基于Activiti的5.20.0.1版本
public interface ProcessEngine extends EngineServices { /** the version of the activiti library */
public static String VERSION = "5.20.0.1"; /** The name as specified in 'process-engine-name' in
* the activiti.cfg.xml configuration file.
* The default name for a process engine is 'default */
String getName(); void close();
}
二、Activiti不支持分布的原因分析
- 在Activiti工作流的act_ge_property表中通常情况下有3条记录:
- next.dbid
- schema.history
- schema.version
其中next.dbid对应的值为数据库中当前最近一次增长后的最大记录id,每次增长的步长为2500,
protected int idBlockSize = 2500; (在ProcessEngineConfiguration类中)
- Activiti中所有的id(如:Task的id,Execution的id,ProcessInstance的id等)都是通过IdGenerator来生成的
/**
* generates {@link IdBlock}s that are used to assign ids to new objects.
*
* The scope of an instance of this class is process engine,
* which means that there is only one instance in one process engine instance.
*
* @author Tom Baeyens
* @author Joram Barrez
*/
public interface IdGenerator { String getNextId(); }
- IdGenerator的默认实现是
/**
* @author Tom Baeyens
*/
public class DbIdGenerator implements IdGenerator { protected int idBlockSize;
protected long nextId = 0;
protected long lastId = -1; protected CommandExecutor commandExecutor;
protected CommandConfig commandConfig; public synchronized String getNextId() {
if (lastId<nextId) {
getNewBlock();
}
long _nextId = nextId++;
return Long.toString(_nextId);
} protected synchronized void getNewBlock() {
IdBlock idBlock = commandExecutor.execute(commandConfig, new GetNextIdBlockCmd(idBlockSize));
this.nextId = idBlock.getNextId();
this.lastId = idBlock.getLastId();
}
从上面的代码可以看出,获取下一个id的方法是加锁的,也就是在一台服务器上id的增长是没有问题的,但是如果将Activiti部署在多台服务器上就会有两个问题
- 从代码的第17,18行可以看出id是本地自增,如果有多台服务器就会出现id相同的情况(由并发写造成的);
- 获取lastId的方法是操作同一个数据库的,会有问题,代码22中通过执行GetNextIdBlockCmd来获取数据库中的next.dbid的值,如果在多台服务器上由于一台服务器修改后,其他服务器无法知道
/**
* @author Tom Baeyens
*/
public class GetNextIdBlockCmd implements Command<IdBlock> { private static final long serialVersionUID = 1L;
protected int idBlockSize; public GetNextIdBlockCmd(int idBlockSize) {
this.idBlockSize = idBlockSize;
} public IdBlock execute(CommandContext commandContext) {
PropertyEntity property = (PropertyEntity) commandContext
.getPropertyEntityManager()
.findPropertyById("next.dbid");
long oldValue = Long.parseLong(property.getValue());
long newValue = oldValue+idBlockSize;
property.setValue(Long.toString(newValue));
return new IdBlock(oldValue, newValue-1);
}
}
三、解决方案
要想解决Activiti分布式的问题,就需要解决id生成的问题,也就是要自己实现IdGenerator接口,因此要有一个地方来生成一个全局唯一的id才行。
我在实际工作中是通过redis来实现的,redis也可以做集群,因此不需要考虑redis单点的问题,具体方案如下:
/**
* 分布式id生成器
*
* @version 1.0
* @author Pin Xiong
* @date 创建时间:2016年8月12日 下午3:22:09
*/
public class DistributedIdGenerator implements IdGenerator { public DistributedIdGenerator(RedisService redisService) {
this.redisService = redisService;
} private RedisService redisService; @Override
public String getNextId() {
return String.format("%sX%s", D.formatDate(Constants.ACTIVITI_ENGINE_DISTRIBUTED_ID_PREFIX),this.redisService.incrby(Constants.ACTIVITI_ENGINE_DISTRIBUTED_ID_KEY, 1L));
}
}
其中,D.formatDate(Constants.ACTIVITI_ENGINE_DISTRIBUTED_ID_PREFIX)是通过服务器时间来生成id的前缀,
重点是后面的this.redisService.incrby(MainRK.ACTIVITI_ENGINE_DISTRIBUTED_ID_KEY, 1L)
该方法是在redis中获取key (也就是代码中Constants.ACTIVITI_ENGINE_DISTRIBUTED_ID_KEY)对应的值,并自增1
在实际工作中通过该方案可以解决Activiti分布式问题。
如果其他同学有更好的方案,也希望可以一起分享,谢谢!
Activiti工作流--分布式实现方案的更多相关文章
- Activiti工作流学习-----基于5.19.0版本(1)
该版本的Activiti运行须知: 1.JDK 6+,Eclipse最好是Kepler以上版本. 2.试验功能都有EXPERIMENTAL标注,被标注的部分不应该视为稳定的. 有兴趣的同学可以去了解下 ...
- activiti工作流的web流程设计器整合视频教程 SSM和独立部署
本视频为activiti工作流的web流程设计器整合视频教程 整合Acitiviti在线流程设计器(Activiti-Modeler 5.21.0 官方流程设计器) 本视频共讲了两种整合方式 1. 流 ...
- activiti工作流的web流程设计器整合视频教程 SSM 和 独立部署
本视频为activiti工作流的web流程设计器整合视频教程 整合Acitiviti在线流程设计器(Activiti-Modeler 5.21.0 官方流程设计器) 本视频共讲了两种整合方式 1. 流 ...
- Activiti工作流学习(三)Activiti工作流与spring集成
一.前言 前面Activiti工作流的学习,说明了Activiti的基本应用,在我们开发中可以根据实际的业务参考Activiti的API去更好的理解以及巩固.我们实际的开发中我们基本上都使用sprin ...
- activiti工作流的web流程设计器整合视频教程
本视频为activiti工作流的web流程设计器整合视频教程 整合Acitiviti在线流程设计器(Activiti-Modeler 5.21.0 官方流程设计器) 本视频共讲了两种整合方式 1. 流 ...
- Activiti工作流学习(一)部署对象和流程定义
一.前言 前一段时间在工作中,使用了流程审批,对api的调用非常不熟悉,都是调用别人写好的接口在界面上进行显示,基本了解了流程审批的主要步骤,现对流程审批进行学习,主要是调用api进行CRUD操作,感 ...
- Activiti工作流引擎参考资料
Activiti工作流引擎使用 工作流-Activiti核心API介绍 传智播客Activiti工作流视频教程(企业开发实例讲解) 工作流引擎Activiti演示项目 http://www.kafei ...
- Memcached常规应用与分布式部署方案
1.Memcached常规应用 $mc = new Memcache(); $mc->conncet('127.0.0.1', 11211); $sql = sprintf("SELE ...
- Window Redis分布式部署方案 java
Redis分布式部署方案 Window 1. 基本介绍 首先redis官方是没有提供window下的版本, 是window配合发布的.因现阶段项目需求,所以研究部署的是window版本的,其实都 ...
随机推荐
- nodejs如何从异步回调函数返回想要的值
const fs = require('fs') let read=()=>{ fs.readFile("./contents/test.json",(err,data)=& ...
- python实现批量文件重命名
本文实例为大家分享了python批量文件重命名的具体代码,供大家参考,具体内容如下 问题描述 最近遇到朋友求助,如何将大量文件名前面的某些字符删除. 即将图中文件前的编号删除. Python实现 用到 ...
- delete语句的基本用法
DELETE FROM tb_courses WHERE course_id=;
- vc6.0代码转vs2017相关问题
vc6.0代码转vs2017相关问题 命令行 error D8016: “/ZI”和“/Gy-”命令行选项不兼容fatal error C1083: 无法打开包括文件: “WinSock2.h”: N ...
- 使用addEventListener绑定事件是关于this和event记录
DOM元素使用addEventListener绑定事件的时候经常会碰到想把当前作用域传到函数内部,可以使用以下两种放下: var bindAsEventListener=function (objec ...
- redis概要学习
redis 概要学习 redis简介 Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库. Redis 与其他 key - value 缓存产品有以下三个特点: Re ...
- Java并发编程:Callable、Future和FutureTask 实现龟兔赛跑
1.不清楚的看博客http://www.cnblogs.com/dolphin0520/p/3949310.html 我们使用上面的代码来实现一个龟兔赛跑 package com.weiyuan.te ...
- ImageLoader在Gridview中的使用
原理和ImageLoader在Listview中的使用一样,只有下面的几点变化 主页面的布局 <?xml version="1.0" encoding="utf-8 ...
- c++数字转化为字符串、字符串转换为数字
char ch[20]; int i =1; int j = 2; char *p = "34567"; 数字装换为字符串 sprintf(ch , "%d,%d&quo ...
- Maven中央仓库正式成为Oracle官方JDBC驱动程序组件分发中心
1. 前言 相信参与使用Oracle数据库进行项目开发.运维的同学常常被Oracle JDBC驱动的Maven依赖折磨.现在这一情况在今年二月份得到了改变,甲骨文这个老顽固终于开窍了. 一位甲骨文的工 ...