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分布式問題。

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM