Activiti7修改内部缓存为redis

实现参照博客Activiti7将默认缓存替换为Redis_coderYXF的专栏-CSDN博客在其基础上进行调整

目录

1. 前言

2. Activiti6及以上版本的区别

3. 编写Kyro工具类

3.1 自定义默认的序列化策略类

3.2 序列化工具类

4. 自定义缓存实现

5. 切换Activiti默认缓存实现类


1. 前言

因为项目中要求需要将服务分布式部署,导致Activiti默认实现的缓存方法(DefaultDeploymentCache是将缓存存到本地的Map中)这就导致,多台部署的话,可能发生缓存数据的不同步,导致审批出现错误。使用redis进行管理,则能够解决同步问题。

可能发生的错误:点击流程发布或者临时修改Process类的节点连接,无法同步至其它服务

2. Activiti6及以上版本的区别

Activiti5缓存的对象实现了Serializable类,可以直接将对象存储进redis中,不用再做特殊处理。

Activiti6及以上版本缓存的对象中BpmnModel与Process均为实现实例化接口,则需要做进一步的处理。我是使用了Kyro框架进行了序列化处理

3. 编写Kyro工具类

3.1 自定义默认的序列化策略类

Activiti6、7中存在以transient修饰的属性。Kyro默认的序列化类是FieldSerializer,默认不会去序列化被transient修饰的属性,这就导致数据在存取的期间,出现数据的丢失。

FieldSerializer.FieldSerializerConfig.serializeTransient的属性默认为false,将其设置为true则会去序列化被transient修饰的属性。

创建com.esotericsoftware.kryo.serializers.CustomSerializer类

集成自FieldSerializer类,只是在其调用构造方法后,手动在设置一下serializeTransient值为true

package com.esotericsoftware.kryo.serializers;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Output;

public class CustomSerializer<T> extends FieldSerializer<T> {

    public CustomSerializer(Kryo kryo, Class type) {
        super(kryo, type, (Class[])null);
        setSerializeTransient(true);
    }

    public CustomSerializer(Kryo kryo, Class type, Class[] generics) {
        super(kryo, type, generics, kryo.getFieldSerializerConfig().clone());
        setSerializeTransient(true);
    }

    protected CustomSerializer(Kryo kryo, Class type, Class[] generics, FieldSerializerConfig config) {
        super(kryo, type, generics, config);
        setSerializeTransient(true);
    }
}

ps:FieldSerializer中的序列化方法write

    public void write(Kryo kryo, Output output, T object) {
        if (Log.TRACE) {
            Log.trace("kryo", "FieldSerializer.write fields of class: " + object.getClass().getName());
        }

        if (this.config.isOptimizedGenerics()) {
            if (this.typeParameters != null && this.generics != null) {
                this.rebuildCachedFields();
            }

            if (this.genericsScope != null) {
                kryo.getGenericsResolver().pushScope(this.type, this.genericsScope);
            }
        }

        FieldSerializer.CachedField[] fields = this.fields;
        int i = 0;

        int n;
        for(n = fields.length; i < n; ++i) {
            fields[i].write(output, object);
        }
        // 判断了是否序列化transient修饰的方法
        if (this.config.isSerializeTransient()) {
            i = 0;

            for(n = this.transientFields.length; i < n; ++i) {
                this.transientFields[i].write(output, object);
            }
        }

        if (this.config.isOptimizedGenerics() && this.genericsScope != null) {
            kryo.getGenericsResolver().popScope();
        }

    }

3.2 序列化工具类

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.serializers.CustomSerializer;
import com.esotericsoftware.kryo.serializers.FieldSerializer;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.impl.persistence.deploy.ProcessDefinitionCacheEntry;
import org.activiti.engine.repository.ProcessDefinition;
import org.apache.poi.ss.formula.functions.T;
import org.objenesis.strategy.StdInstantiatorStrategy;
import org.springframework.stereotype.Component;
import sun.reflect.ReflectionFactory;

import java.io.*;
import java.lang.reflect.Constructor;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 序列化工具类
 *
 * @author 36020
 */
@Slf4j
@Component
public class SerializeUtils {

    private final ThreadLocal<Kryo> kryoLocal = ThreadLocal.withInitial(() -> {
        // kyro是线程不安全的,需要为每个线程创建一个独立的对象
        Kryox kryo = new Kryox();
        kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(
                new StdInstantiatorStrategy()));
        kryo.setDefaultSerializer(CustomSerializer.class);
        return kryo;
    });

    public byte[] serialize(Object obj) {
        if (obj == null) {
            return null;
        }

        Kryo kryo = kryoLocal.get();
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Output output = new Output(byteArrayOutputStream);
        kryo.writeClassAndObject(output, obj);
        output.close();
        return byteArrayOutputStream.toByteArray();
    }


    public Object deSerialize(byte[] bytes) {
        if (bytes == null) {
            return null;
        }

        Kryo kryo = kryoLocal.get();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        Input input = new Input(byteArrayInputStream);
        input.close();
        return kryo.readClassAndObject(input);
    }


    /**
     * Kyro序列化没有无参构造方法的类时会报错,所以进行改造
     */
    class Kryox extends Kryo {

        private final ReflectionFactory REFLECTION_FACTORY = ReflectionFactory.getReflectionFactory();

        private final ConcurrentHashMap<Class<?>, Constructor<?>> _constructors = new ConcurrentHashMap<Class<?>, Constructor<?>>();

        @Override
        public <T> T newInstance(Class<T> type) {
            try {
                return super.newInstance(type);
            } catch (Exception e) {
                return (T) newInstanceFromReflectionFactory(type);
            }
        }

        private Object newInstanceFrom(Constructor<?> constructor) {
            try {
                return constructor.newInstance();
            } catch (final Exception e) {
                throw new RuntimeException(e);
            }
        }

        @SuppressWarnings("unchecked")
        public <T> T newInstanceFromReflectionFactory(Class<T> type) {
            Constructor<?> constructor = _constructors.get(type);
            if (constructor == null) {
                constructor = newConstructorForSerialization(type);
                Constructor<?> saved = _constructors.putIfAbsent(type, constructor);
                if (saved != null)
                    constructor = saved;
            }
            return (T) newInstanceFrom(constructor);
        }

        private <T> Constructor<?> newConstructorForSerialization(Class<T> type) {
            try {
                Constructor<?> constructor = REFLECTION_FACTORY.newConstructorForSerialization(type,
                        Object.class.getDeclaredConstructor());
                constructor.setAccessible(true);
                return constructor;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

    }
}

4. 自定义缓存实现

实现DeploymentCache

版本5中泛型为ProcessDefinitionEntity

版本6和7泛型为ProcessDefinitionCacheEntry

@Component
public class CustomDeploymentCache implements DeploymentCache<ProcessDefinitionCacheEntry> {

    private final static RedisService redisService;

    private final static String ACTIVITI_CACHE = "activiti_cache:";
    private final static String BPMN_CACHE = "bpmn_cache:";
    private final static String PROCESS_CACHE = "process_cache:";
    @Resource
    private SerializeUtils serializeUtils;

    static {
        redisService = SpringUtils.getBean(RedisService.class);
        // 启动时删除全部缓存
        clearAll();
    }

    @Override
    public ProcessDefinitionCacheEntry get(String id) {
        byte[] entry = redisService.getCacheMapValue(ACTIVITI_CACHE, id);
        byte[] bpmn = redisService.getCacheMapValue(BPMN_CACHE, id);
        byte[] process = redisService.getCacheMapValue(PROCESS_CACHE, id);
        if (entry == null) {
            return null;
        }
        return new ProcessDefinitionCacheEntry((ProcessDefinition)serializeUtils.deSerialize(entry)
                , (BpmnModel) serializeUtils.deSerialize(bpmn)
                , (Process) serializeUtils.deSerialize(process));

    }

    @Override
    public void add(String id, ProcessDefinitionCacheEntry entry) {
        redisService.setCacheMapValue(ACTIVITI_CACHE, id, serializeUtils.serialize(entry.getProcessDefinition()));
        redisService.setCacheMapValue(BPMN_CACHE, id, serializeUtils.serialize(entry.getBpmnModel()));
        redisService.setCacheMapValue(PROCESS_CACHE, id, serializeUtils.serialize(entry.getProcess()));
    }

    @Override
    public void remove(String id) {
        redisService.delCacheMapValue(ACTIVITI_CACHE, id);
        redisService.delCacheMapValue(BPMN_CACHE, id);
        redisService.delCacheMapValue(PROCESS_CACHE, id);
    }

    @Override
    public void clear() {
        clearAll();
    }

    public static void clearAll() {
        redisService.deleteObject(ACTIVITI_CACHE);
        redisService.deleteObject(BPMN_CACHE);
        redisService.deleteObject(PROCESS_CACHE);
    }

    @Override
    public boolean contains(String id) {
        return redisService.getCacheMapValue(ACTIVITI_CACHE, id) != null;
    }
}

5. 切换Activiti默认缓存实现类

    @Bean
    public DeploymentCache<ProcessDefinitionCacheEntry> deploymentCache() {
        return new CustomDeploymentCache();
    }
    /**
     * 初始化配置
     *
     * @return
     */
    @Bean
    public SpringProcessEngineConfiguration processEngineConfiguration() {
        SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration();

        transactionManager.setDataSource(dataSource);
        configuration.setTransactionManager(transactionManager);
        // 执行工作流对应的数据源
        configuration.setDataSource(dataSource);
        // 是否自动创建流程引擎表
        configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
        configuration.setAsyncExecutorActivate(false);
        // 修改缓存
        configuration.setProcessDefinitionCache(deploymentCache());
        // 流程历史等级
        configuration.setHistoryLevel(HistoryLevel.AUDIT);
        return configuration;
    }

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值