• Activiti工作流--分布式实现方案


    一、运行环境

    以下所有的描述都是基于Activiti的5.20.0.1版本

     1 public interface ProcessEngine extends EngineServices {
     2 
     3   /** the version of the activiti library */
     4   public static String VERSION = "5.20.0.1";
     5 
     6   /** The name as specified in 'process-engine-name' in 
     7    * the activiti.cfg.xml configuration file.
     8    * The default name for a process engine is 'default */
     9   String getName();
    10 
    11   void close();
    12 }

    二、Activiti不支持分布的原因分析

    1. next.dbid
    2. schema.history
    3. schema.version

    其中next.dbid对应的值为数据库中当前最近一次增长后的最大记录id,每次增长的步长为2500,

     1 protected int idBlockSize = 2500; (在ProcessEngineConfiguration类中)

    • Activiti中所有的id(如:Task的id,Execution的id,ProcessInstance的id等)都是通过IdGenerator来生成的
     1 /**
     2  * generates {@link IdBlock}s that are used to assign ids to new objects.
     3  * 
     4  * The scope of an instance of this class is process engine,
     5  * which means that there is only one instance in one process engine instance.
     6  * 
     7  * @author Tom Baeyens
     8  * @author Joram Barrez
     9  */
    10 public interface IdGenerator {
    11 
    12   String getNextId();
    13 
    14 }
    • IdGenerator的默认实现是
     1 /**
     2  * @author Tom Baeyens
     3  */
     4 public class DbIdGenerator implements IdGenerator {
     5 
     6   protected int idBlockSize;
     7   protected long nextId = 0;
     8   protected long lastId = -1;
     9   
    10   protected CommandExecutor commandExecutor;
    11   protected CommandConfig commandConfig;
    12   
    13   public synchronized String getNextId() {
    14     if (lastId<nextId) {
    15       getNewBlock();
    16     }
    17     long _nextId = nextId++;
    18     return Long.toString(_nextId);
    19   }
    20 
    21   protected synchronized void getNewBlock() {
    22     IdBlock idBlock = commandExecutor.execute(commandConfig, new GetNextIdBlockCmd(idBlockSize));
    23     this.nextId = idBlock.getNextId();
    24     this.lastId = idBlock.getLastId();
    25   }

    从上面的代码可以看出,获取下一个id的方法是加锁的,也就是在一台服务器上id的增长是没有问题的,但是如果将Activiti部署在多台服务器上就会有两个问题

    1. 从代码的第17,18行可以看出id是本地自增,如果有多台服务器就会出现id相同的情况(由并发写造成的);
    2. 获取lastId的方法是操作同一个数据库的,会有问题,代码22中通过执行GetNextIdBlockCmd来获取数据库中的next.dbid的值,如果在多台服务器上由于一台服务器修改后,其他服务器无法知道
     1 /**
     2  * @author Tom Baeyens
     3  */
     4 public class GetNextIdBlockCmd implements Command<IdBlock> {
     5   
     6   private static final long serialVersionUID = 1L;
     7   protected int idBlockSize;
     8   
     9   public GetNextIdBlockCmd(int idBlockSize) {
    10     this.idBlockSize = idBlockSize;
    11   }
    12 
    13   public IdBlock execute(CommandContext commandContext) {
    14     PropertyEntity property = (PropertyEntity) commandContext
    15       .getPropertyEntityManager()
    16       .findPropertyById("next.dbid");
    17     long oldValue = Long.parseLong(property.getValue());
    18     long newValue = oldValue+idBlockSize;
    19     property.setValue(Long.toString(newValue));
    20     return new IdBlock(oldValue, newValue-1);
    21   }
    22 }

    三、解决方案

    要想解决Activiti分布式的问题,就需要解决id生成的问题,也就是要自己实现IdGenerator接口,因此要有一个地方来生成一个全局唯一的id才行。

    我在实际工作中是通过redis来实现的,redis也可以做集群,因此不需要考虑redis单点的问题,具体方案如下:

     1 /**
     2  * 分布式id生成器
     3  * 
     4  * @version 1.0
     5  * @author Pin Xiong
     6  * @date 创建时间:2016年8月12日 下午3:22:09
     7  */
     8 public class DistributedIdGenerator implements IdGenerator {
     9 
    10     public DistributedIdGenerator(RedisService redisService) {
    11         this.redisService = redisService;
    12     }
    13 
    14     private RedisService redisService;
    15 
    16     @Override
    17     public String getNextId() {
    18         return String.format("%sX%s", D.formatDate(Constants.ACTIVITI_ENGINE_DISTRIBUTED_ID_PREFIX),this.redisService.incrby(Constants.ACTIVITI_ENGINE_DISTRIBUTED_ID_KEY, 1L));
    20     }
    21 }

    其中,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分布式问题。

    如果其他同学有更好的方案,也希望可以一起分享,谢谢!

  • 相关阅读:
    Java实现第十届蓝桥杯组队
    Java实现第十届蓝桥杯组队
    Java实现第十届蓝桥杯组队
    Java实现第十届蓝桥杯组队
    Java实现第十届蓝桥杯组队
    程序员加班奇遇
    Spring Boot导出jar包发布
    Eclipse工具栏太多,自定义工具栏,去掉调试
    ${pageContext.request.contextPath}的解释以及和request.contextPath的区别
    request 获取各种路径
  • 原文地址:https://www.cnblogs.com/pinxiong/p/5826967.html
Copyright © 2020-2023  润新知