Redis - Spring Boot Redis 使用 msgpack 作为序列化

Posted Lux_Sun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis - Spring Boot Redis 使用 msgpack 作为序列化相关的知识,希望对你有一定的参考价值。

首先引入 msgpack 所需要的包

<dependency>
    <groupId>org.msgpack</groupId>
    <artifactId>msgpack-core</artifactId>
    <version>0.8.13</version>
</dependency>

<dependency>
    <groupId>org.msgpack</groupId>
    <artifactId>jackson-dataformat-msgpack</artifactId>
    <version>0.8.13</version>
</dependency>
  • 版本一定要对齐,之前 jackson-dataformat-msgpack 版本太低导致无法使用。

 

RedisConfig.java(Spring Boot Redis 配置类)

import com.fasterxml.jackson.databind.ObjectMapper;
import org.msgpack.jackson.dataformat.MessagePackFactory;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.*;

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * 设置spring redis data 序列化模板
     * @param factory
     * @return
     */
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        ObjectMapper mapper = new ObjectMapper(new MessagePackFactory());
        Jackson2JsonRedisSerializer Jackson2Serializer = new Jackson2JsonRedisSerializer(Object.class);
        Jackson2Serializer.setObjectMapper(mapper);
        RedisSerializer redisSerializer= Jackson2Serializer;
        template.setValueSerializer(redisSerializer);
        template.setKeySerializer(new StringRedisSerializer());
        return template;
    }

    /**
     * 整合spring cache
     * 设置@cacheable 序列化方式
     * @return
     */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration() {
        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
        configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericMsgpackRedisSerializer()));
        return configuration;
    }
}

 

GenericMsgpackRedisSerializer.java(Spring Cache msgpack 序列化类)

import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import org.msgpack.core.annotations.Nullable;
import org.msgpack.jackson.dataformat.MessagePackFactory;
import org.springframework.cache.support.NullValue;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

public class GenericMsgpackRedisSerializer implements RedisSerializer<Object> {

    static final byte[] EMPTY_ARRAY = new byte[0];

    private final ObjectMapper mapper;

    public GenericMsgpackRedisSerializer() {
        this.mapper = new ObjectMapper(new MessagePackFactory());
        this.mapper.registerModule((new SimpleModule()).addSerializer(new GenericMsgpackRedisSerializer.NullValueSerializer(null)));
        this.mapper.enableDefaultTyping(DefaultTyping.NON_FINAL, As.PROPERTY);
    }

    @Override
    public byte[] serialize(@Nullable Object source) throws SerializationException {
        if (source == null) {
            return EMPTY_ARRAY;
        } else {
            try {
                return this.mapper.writeValueAsBytes(source);
            } catch (JsonProcessingException var3) {
                throw new SerializationException("Could not write JSON: " + var3.getMessage(), var3);
            }
        }
    }

    @Override
    public Object deserialize(@Nullable byte[] source) throws SerializationException {
        return this.deserialize(source, Object.class);
    }

    @Nullable
    public <T> T deserialize(@Nullable byte[] source, Class<T> type) throws SerializationException {
        Assert.notNull(type, "Deserialization type must not be null! Pleaes provide Object.class to make use of Jackson2 default typing.");
        if (source == null || source.length == 0) {
            return null;
        } else {
            try {
                return this.mapper.readValue(source, type);
            } catch (Exception var4) {
                throw new SerializationException("Could not read JSON: " + var4.getMessage(), var4);
            }
        }
    }

    private class NullValueSerializer extends StdSerializer<NullValue> {
        private static final long serialVersionUID = 2199052150128658111L;
        private final String classIdentifier;

        NullValueSerializer(@Nullable String classIdentifier) {
            super(NullValue.class);
            this.classIdentifier = StringUtils.hasText(classIdentifier) ? classIdentifier : "@class";
        }

        @Override
        public void serialize(NullValue value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
            jgen.writeStartObject();
            jgen.writeStringField(this.classIdentifier, NullValue.class.getName());
            jgen.writeEndObject();
        }
    }
}

 

RedisCacheRedisUtils.java(序列化工具类)

import java.util.Set;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.msgpack.jackson.dataformat.MessagePackFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;

/**
 * @description: redis缓存工具类
 * @version:1.0
 */
@Component
public class RedisCacheUtils {

    private static Logger logger = LoggerFactory.getLogger(RedisCacheUtils.class);

    private final static Boolean REDIS_ENABLE = true;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    RedisConnectionFactory redisConnectionFactory;

    public RedisTemplate getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public RedisCacheUtils(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key   缓存的键值
     * @param value 缓存的值
     * @return缓存的对象   
     */
    public boolean setCacheObject(String key, Object value) {
        if (!REDIS_ENABLE) {
            return false;
        }
        logger.debug("存入缓存 key:" + key);
        try {
            ValueOperations<String, Object> operation = redisTemplate.opsForValue();
            operation.set(key, value);
            return true;
        } catch (Exception ex) {
            logger.error(ex.getMessage());
            return false;
        }
    }

    /**
     * 根据pattern匹配清除缓存
     * @param pattern
     */
    public void clear(String pattern) {
        if (!REDIS_ENABLE) {
            return;
        }
        logger.debug("清除缓存 pattern:" + pattern);
        try {
            ValueOperations<String, Object> valueOper = redisTemplate.opsForValue();
            RedisOperations<String, Object> redisOperations = valueOper.getOperations();
            redisOperations.keys(pattern);
            Set<String> keys = redisOperations.keys(pattern);
            for (String key : keys) {
                redisOperations.delete(key);
            }
        } catch (Exception ex) {
            logger.error(ex.getMessage());
            return;
        }
    }

    /**
     * 根据key清除缓存
     * @param key
     */
    public void delete(String key) {
        if (!REDIS_ENABLE) {
            return;
        }
        logger.debug("删除缓存 key:" + key);
        try {
            ValueOperations<String, Object> valueOper = redisTemplate.opsForValue();
            RedisOperations<String, Object> redisOperations = valueOper.getOperations();
            redisOperations.delete(key);
        } catch (Exception ex) {
            logger.error(ex.getMessage());
            return;
        }
    }


    /**
     * 获得缓存的基本对象。
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     *   
     */
    public Object getCacheObject(String key) {
        if (!REDIS_ENABLE) {
            return null;
        }
        logger.debug("获取缓存 key:" + key);
        try {
            ValueOperations<String, Object> operation = redisTemplate.opsForValue();
            return operation.get(key);
        } catch (Exception ex) {
            logger.error(ex.getMessage());
            return null;
        }
    }

    /**
     * 获得缓存的基本对象。
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     *   
     */
    public <T> T getCacheObject(String key, Class<T> clazz) {
        if (!REDIS_ENABLE) {
            return null;
        }
        logger.debug("获取缓存 key:" + key);
        RedisTemplate template = new StringRedisTemplate(redisConnectionFactory);
        Jackson2JsonRedisSerializer Jackson2Serializer = new Jackson2JsonRedisSerializer(clazz);
        Jackson2Serializer.setObjectMapper(new ObjectMapper(new MessagePackFactory()));
        RedisSerializer redisSerializer = Jackson2Serializer;
        template.setValueSerializer(redisSerializer);
        try {
            ValueOperations<String, T> operation = template.opsForValue();
            return (T) operation.get(key);
        } catch (Exception ex) {
            logger.error(ex.getMessage());
            return null;
        }
    }
}

 

启动 Spring Boot 开始测试

@RestController
public class TestController {

    @Resource
    RedisCacheUtils redisCacheUtils;

    @GetMapping("/getCache")
    public Object getCache() {
        List<String> result = redisCacheUtils.getCacheObject("list_cache", new ArrayList<HashMap<String, String>>().getClass());
        return result;
    }

    @GetMapping("/setCache")
    public Object setCache() {
        List<Map> list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            Map map = new HashMap<String, String>();
            map.put("id", i);
            map.put("name", "index=" + i);
            list.add(map);
        }
        return redisCacheUtils.setCacheObject("list_cache", list);
    }
}
  • 缓存结果(msgpack:大小1893字节)

  • 缓存结果(fastjson:大小2781字节)

  • 速度比较(10000条数据测试,非专业测试结果,仅供参考)

排名结果:msgpack > fastJson > jackson

以上是关于Redis - Spring Boot Redis 使用 msgpack 作为序列化的主要内容,如果未能解决你的问题,请参考以下文章

spring boot:Spring Boot中Redis的使用

Spring boot在Spring boot中Redis的使用

spring boot 集成 Redis

Spring Boot 集成 Redis

spring-boot-data-redis,使用redisson作为redis客户端

Spring Boot如何整合Redis