
Redis 可以说是服务端开发技术的标配,任何一个在线并有一些访问量的系统,例如电商系统、内容网站等,都需要用到 Redis 。Redis 的作用不仅仅是我们熟知的缓存,由于其实内存型数据库并且拥有丰富的数据类型,它还可以用于其他场景,例如消息队列、Pub/Sub 模式的实时聊天、计数器、数据重拍等。所以说,你不懂 Redis 都不好意说是自己是一个服务端开发攻城狮。
Redis 有多种语言版本的客户端,Java 版本中,最流行的就是 Jedis。而 Spring 在 Jedis 的基础上,封装了 spring-data-redis,使用 spring-data-redis 只需要配置连接池、配置连接工厂、配置 RedisTemplate 然后直接使用即可。这里简单说下配置:
1.在 propertites 配置文件中生命 redis 参数
redis.maxIdle=300redis.maxWait=3000redis.testOnBorrow=falseredis.timeout=3000redis.host=127.0.0.1redis.port=32768redis.password=yourpassword
2.新建一个 redis.xml 配置文件,配置连接池、连接工厂bean、RedisTemplate 等
首先配置 JedisPoolConfig ,表示使用连接池;
接着配置 JedisConnectionFactory,这是连接工厂类配置;
最后配置 RedisTemplate,这里指定使用的连接工厂和各种类型的序列化方式(这是本文的中点,稍后说到)
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"><property name="maxIdle" value="${redis.maxIdle}"/><property name="maxWaitMillis" value="${redis.maxWait}"/><property name="testOnBorrow" value="${redis.testOnBorrow}"/></bean><bean id="jedisConnFactory"class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"p:use-pool="true"p:poolConfig-ref="poolConfig"p:port="${redis.port}"p:password="${redis.password}"p:hostName="${redis.host}"p:database="1"p:timeout="${redis.timeout}"/><bean id="redisTemplate"class="org.springframework.data.redis.core.RedisTemplate"p:connection-factory-ref="jedisConnFactory"p:keySerializer-ref="stringRedisSerializer"p:hashKeySerializer-ref="stringRedisSerializer"p:valueSerializer-ref="jdkRedisSerializer"p:hashValueSerializer-ref="jdkRedisSerializer"/><bean id="jdkRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
3.在 spring mvc 配置文件中引用 redis.xml。
<import resource="redis.xml"></import>
配置完成,接下来就是使用,由于 RedisTemplate 就是普通的 bean,所以直接自动注入就可以了。新建 RedisCache.java 的类,内容如下:
@Repositorypublic class RedisCache{@Resource(name = "redisTemplate")private RedisTemplate<String,Object> redisTemplate;public <T> void put(String key, T value, long seconds){redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);}public <T> T get(String key, Class<T> type){Object obj = redisTemplate.opsForValue().get(key);return obj != null ? (T)obj : null;}public void remove(String key){if (exists(key)) {redisTemplate.delete(key);}}public boolean exists(final String key){return redisTemplate.hasKey(key);}}
以上整个配置和使用很简单,此处只是考虑单实例 redis 的情况,不包括集群,集群的使用方式可以查看我之前的一篇文章(http://www.fengzheng.pub/archives/120.html)
接下来,说到本文的重点了。回想我们用到 redis 的场景,大多数情况下并不是存储基本数据类型,而是存储一些自定义实体对象。为了压缩数据的大小,会用到序列化。 spring-data-redis 主要提供了 JdkSerializationRedisSerializer、GenericJackson2JsonRedisSerializer 两种序列化的方式,但是性能并不是最好的,尤其 JdkSerializationRedisSerializer,无论是序列化后的大小还是序列化速度,都比较差。基于这种情况,我们扩展 Kryo 这种性能比较好的序列化方式进来。
通过查看上面提到的两个序列化类,扩展第三方序列化只需要做很简单的实现就可以。
1.实现 RedisSerializer 接口
2.实现序列化方法 byte[] serialize(T var1) throws SerializationException
3.实现反序列化方法 T deserialize(byte[] var1) throws SerializationException
基于以上分析,我们最终实现的序列化类 KryoSerizlizationRedisSerialize.java 内容如下:
package com.kite.redis;import com.esotericsoftware.kryo.Kryo;import com.esotericsoftware.kryo.io.ByteBufferOutput;import com.esotericsoftware.kryo.io.Input;import org.springframework.data.redis.serializer.RedisSerializer;import org.springframework.data.redis.serializer.SerializationException;/*** KryoSerizlizationRedisSerializer** @author fengzheng*/public class KryoSerizlizationRedisSerializer<T> implements RedisSerializer<T> {private Kryo serializer = new Kryo();private Kryo deserializer = serializer;private final byte[] EMPTY_BYTE = new byte[0];public T deserialize(byte[] bytes) {if (bytes == null || bytes.length == 0) {return null;} else {try {Input input = new Input(bytes);return (T) this.deserializer.readClassAndObject(input);} catch (Exception var3) {throw new SerializationException("Cannot deserialize", var3);}}}public byte[] serialize(Object object) {if (object == null) {return EMPTY_BYTE;} else {try {ByteBufferOutput bufferOutput = new ByteBufferOutput(1,4096);System.out.println(this.serializer == null);this.serializer.writeClassAndObject(bufferOutput, object);byte[] result = bufferOutput.toBytes();bufferOutput.flush();return result;} catch (Exception var3) {throw new SerializationException("Cannot serialize", var3);}}}}
回过头来在 redis.xml 中修改序列化方式,看一下上面配置的 RedisTemplate 。
<bean id="redisTemplate"class="org.springframework.data.redis.core.RedisTemplate"p:connection-factory-ref="jedisConnFactory"p:keySerializer-ref="stringRedisSerializer"p:hashKeySerializer-ref="stringRedisSerializer"p:valueSerializer-ref="jdkRedisSerializer"p:hashValueSerializer-ref="jdkRedisSerializer"/><bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/><bean id="jdkRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
其中 keySerializer-ref 和 hashKeySerializer-ref 都是用的 StringRedisSerializer,而 valueSerializer-ref 和 hashValueSerializer-ref 用的是 JdkSerializationRedisSerializer。
我们将 Kyro 序列化类引入
<bean id="kryoRedisSerializer" class="com.ebanghu.redis.KryoSerizlizationRedisSerializer"/>
然后修改 valueSerializer-ref 和 hashValueSerializer-ref 的引用
<bean id="jacksonRedisTemplate"class="org.springframework.data.redis.core.RedisTemplate"p:connection-factory-ref="jedisConnFactory"p:keySerializer-ref="stringRedisSerializer"p:hashKeySerializer-ref="stringRedisSerializer"p:valueSerializer-ref="kryoRedisSerializer"p:hashValueSerializer-ref="kryoRedisSerializer"/>
之后的调用方式不变。
Redis 自动序列化原理
关键就在 RedisTemplate 类,在调用 RedisTemplate 提供的各种数据类型的 set 方法时,会先看有没有指定序列化器(例如 hashValueSerializer、hashKeySerializer ),如果有指定就用这个序列化方式,否则用默认序列化方式,默认的序列化就是 JdkSerializationRedisSerializer。
同理,get 方法也是同样,如果有提供就用指定的方式做反序列化,否则用默认的 JdkSerializationRedisSerializer。
古时的风筝 【微信号】gushidefengzheng





