zoukankan      html  css  js  c++  java
  • SpringBoot修改Redis序列化方式

    前言

    由于Springboot默认提供了序列化方式并不是非常理想,对于高要求的情况下,序列化的速度和序列化之后大小有要求的情况下,不能满足,所以可能需要更换序列化的方式。
    这里主要记录更换序列化的方式以及其中一些出现问题。
    坑坑坑坑坑坑!!!
    这次踩的坑坑。

    序列化方式更换

    第一步,加入依赖

    //protostuff序列化依赖
    compile group: 'io.protostuff', name: 'protostuff-runtime', version: '1.6.0'
    compile group: 'io.protostuff', name: 'protostuff-core', version: '1.6.0'

    第二步,加入序列化工具

    import io.protostuff.LinkedBuffer;
    import io.protostuff.ProtostuffIOUtil;
    import io.protostuff.Schema;
    import io.protostuff.runtime.RuntimeSchema;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.serializer.SerializationException;
    
    /**
    * ProtoStuff序列化工具
    * @author LinkinStar
    */
    public class ProtostuffSerializer implements RedisSerializer {
    
    private boolean isEmpty(byte[] data) {
        return (data == null || data.length == 0);
    }
    
    private final Schema<ProtoWrapper> schema;
    
    private final ProtoWrapper wrapper;
    
    private final LinkedBuffer buffer;
    
    public ProtostuffSerializer() {
        this.wrapper = new ProtoWrapper();
        this.schema = RuntimeSchema.getSchema(ProtoWrapper.class);
        this.buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
    }
    
    @Override
    public byte[] serialize(Object t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        wrapper.data = t;
        try {
            return ProtostuffIOUtil.toByteArray(wrapper, schema, buffer);
        } finally {
            buffer.clear();
        }
    }
    
    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (isEmpty(bytes)) {
            return null;
        }
        ProtoWrapper newMessage = schema.newMessage();
        ProtostuffIOUtil.mergeFrom(bytes, newMessage, schema);
        return (T) newMessage.data;
    }
    
    private static class ProtoWrapper {
        private Object data;
    }
    }

    第三步,对RedisTemplate进行封装

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Component;
    
    import java.util.concurrent.TimeUnit;
    
    /**
    * Redis操作工具类
    * @author LinkinStar
    */
    @Component
    public class RedisUtil {
    
    @Autowired
    @Qualifier("protoStuffTemplate")
    private RedisTemplate protoStuffTemplate;
    
    /**
     * 设置过期时间,单位秒
     * @param key 键的名称
     * @param timeout 过期时间
     * @return 成功:true,失败:false
     */
    public boolean setExpireTime(String key, long timeout) {
        return protoStuffTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }
    
    /**
     * 通过键删除一个值
     * @param key 键的名称
     */
    public void delete(String key) {
        protoStuffTemplate.delete(key);
    }
    
    /**
     * 判断key是否存在
     * @param key 键的名称
     * @return 存在:true,不存在:false
     */
    public boolean hasKey(String key) {
        return protoStuffTemplate.hasKey(key);
    }
    
    /**
     * 数据存储
     * @param key 键
     * @param value 值
     */
    public void set(String key, Object value) {
        protoStuffTemplate.boundValueOps(key).set(value);
    }
    
    /**
     * 数据存储的同时设置过期时间
     * @param key 键
     * @param value 值
     * @param expireTime 过期时间
     */
    public void set(String key, Object value, Long expireTime) {
        protoStuffTemplate.boundValueOps(key).set(value, expireTime, TimeUnit.SECONDS);
    }
    
    /**
     * 数据取值
     * @param key 键
     * @return 查询成功:值,查询失败,null
     */
    public Object get(String key) {
        return protoStuffTemplate.boundValueOps(key).get();
    }
    }

    第四步,修改默认序列化方式

    import com.linkinstars.springBootTemplate.util.ProtostuffSerializer;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
    
    /**
    * redisTemplate初始化,开启spring-session redis存储支持
    * @author LinkinStar
    */
    @Configuration
    @EnableRedisHttpSession
    public class RedisConfig {
    
    /**
     * redisTemplate 序列化使用的Serializeable, 存储二进制字节码, 所以自定义序列化类
     * @Rparam redisConnectionFactory
     * @return redisTemplate
     */
    @Bean
    public RedisTemplate<Object, Object> protoStuffTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
    
        // redis value使用的序列化器
        template.setValueSerializer(new ProtostuffSerializer());
        // redis key使用的序列化器
        template.setKeySerializer(new StringRedisSerializer());
    
        template.afterPropertiesSet();
        return template;
    }
    }

    第五步,相应测试

    //测试redis
    UserEntity user = new UserEntity();
    user.setId(1);
    user.setVal("xxx");
    
    redisUtil.set("xxx", user);
    Object object = redisUtil.get("xxx");
    UserEntity userTemp = (UserEntity) object;
    
    System.out.println("redis数据获取为: " + userTemp);
    redisUtil.delete("xxx");
    System.out.println("redis删除数据之后获取为: " + redisUtil.get("xxx"));

    遇到问题

    问题描述:序列化之后反序列化没有问题,但是强制转换成对应的类出现问题,抛出无法强制转换的异常。
    问题分析:无法强制转换说明可能两个类的类加载器不一样,所以打印两者类加载器发现确实不一样。
    发现其中一个类的类加载器出现了devtool的字样
    所以联想到可能是热部署机制导致这样问题的产生。
    然后查询网络资料,并在多台机器上进行测试,发现有的机器不会出现这样的问题,而有的机器就会出现。
    如果使用Kryo进行序列化的话,第一次就会出现上述问题,而使用protostuff热部署之后才会出现上述问题。
    问题解决:最后为了免除后续可能出现的问题,注释了热部署的devtool的相应依赖和相应的配置得以解决。

    完整代码

    github:https://github.com/LinkinStars/springBootTemplate

    参考博客:
    https://www.spldeolin.com/posts/redis-template-protostuff/
    https://blog.csdn.net/wsywb111/article/details/79612081
    https://github.com/protostuff/protostuff

  • 相关阅读:
    041_form表单重置数据reset()
    040_下拉列表的显示与提交数值时,需要用到转义字符
    039_如何选取checkbox的id值?
    011_表单数据非空验证
    010_@ResposBody错误
    010_页面单击按钮失灵
    使用Maven创建 web项目
    java设计模式(八) 适配器模式
    设计模式 6大设计原则
    Java设计模式(七) 模板模式-使用钩子
  • 原文地址:https://www.cnblogs.com/linkstar/p/9488733.html
Copyright © 2011-2022 走看看