Activiti工做流--分佈式實現方案

1、運行環境redis

如下全部的描述都是基於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 }

 

2、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部署在多臺服務器上就會有兩個問題ide

  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 }

 

3、解決方案this

要想解決Activiti分佈式的問題,就須要解決id生成的問題,也就是要本身實現IdGenerator接口,所以要有一個地方來生成一個全局惟一的id才行。spa

我在實際工做中是經過redis來實現的,redis也能夠作集羣,所以不須要考慮redis單點的問題,具體方案以下:code

 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的前綴,orm

重點是後面的this.redisService.incrby(MainRK.ACTIVITI_ENGINE_DISTRIBUTED_ID_KEY, 1L)

該方法是在redis中獲取key (也就是代碼中Constants.ACTIVITI_ENGINE_DISTRIBUTED_ID_KEY)對應的值,並自增1

 

 在實際工做中經過該方案能夠解決Activiti分佈式問題。

若是其餘同窗有更好的方案,也但願能夠一塊兒分享,謝謝!

相關文章
相關標籤/搜索