SpringBoot下用Kyro作为Redis序列化工具
Posted 程序员欣宸
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot下用Kyro作为Redis序列化工具相关的知识,希望对你有一定的参考价值。
欢迎访问我的GitHub
本篇概览
- 有时候我们需要将Java对象实例存入Redis,常用方法有两种:
- 将对象序列化成字符串后存入Redis;
- 将对象序列化成byte数组后存入Redis;
本章实战上述第二种方式,并且序列化工具选择了Kyro,为了便于开发和验证,将代码写在一个基于SpringBoot的web工程中;
源码下载
- 本章实战的源码可以在github下载,地址和链接信息如下表所示:
名称 | 链接 | 备注 |
---|---|---|
项目主页 | https://github.com/zq2599/blog_demos | 该项目在GitHub上的主页 |
git仓库地址(https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https协议 |
git仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |
- 这个git项目中有多个文件夹,本章源码在springboot-redis-kyro-demo文件夹下,如下图所示:
环境信息
- JDK:1.8.0_144;
- SpringBoot:1.4.1.RELEASE;
- Kyro:4.0.0;
- Redis:3.2.11;
开发步骤列表
-
动手前先将整体步骤梳理一下:
- 准备Reids环境;
- 创建SpringBoot工程,添加依赖;
- 在application.properties中配置Redis相关信息;
- 创建基于Kyro的序列化接口实现类;
- 创建Redis配置类;
- 将存、取、删除等针对对象的基本操作封装成一个服务类;
- 开发一个Controller,对应几个web接口,用来验证对Redis的存、取、删除操作;
- 启动工程,通过web请求操作,并在Redis后台检查数据;
- 步骤已经列清楚了,开始实战吧!
准备Redis环境
- 如何安装Reids就不在本章展开了,请自行准备,我这里为了省事是在Docker上装的,只需以下一行命令即可(前提是Docker可用):
docker run --name redis -p 6379:6379 -idt redis:3.2.11 redis-server --appendonly yes
创建SpringBoot工程,添加依赖
- spring-boot-starter-parent的版本是1.4.1.RELEASE;
- 基于maven创建SpringBoot工程,需要依赖:spring-boot-starter-web、spring-boot-starter-data-redis;
-
在pom.xml文件中添加kyro、fastjson依赖,如下:
<!-- kyro相关 --> <dependency> <groupId>com.esotericsoftware</groupId> <artifactId>kryo</artifactId> <version>4.0.0</version> </dependency> <dependency> <groupId>de.javakaffee</groupId> <artifactId>kryo-serializers</artifactId> <version>0.41</version> </dependency> <!-- fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency>
在application.properties中配置Redis相关信息
application.properties中的配置信息如下,请注意IP地址和端口:
spring.redis.database=0
spring.redis.host=192.168.31.104
spring.redis.port=6379
spring.redis.pool.max-active=2500
spring.redis.pool.max-wait=6000
spring.redis.pool.max-idle=500
spring.redis.pool.min-idle=100
spring.redis.pool.testOnBorrow=true
spring.redis.pool.blockWhenExhausted=true
spring.redis.pool.numTestsPerEvictionRun=3
spring.redis.pool.timeBetweenEvictionRunsMillis=-1
spring.redis.timeout=1000
创建基于Kyro的序列化接口实现类
-
创建RedisSerializer接口的实现类,后续的序列化和反序列化操作都由该类完成:
public class KryoRedisSerializer<T> implements RedisSerializer<T> private static final Logger logger = LoggerFactory.getLogger(KryoRedisSerializer.class); public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; private static final ThreadLocal<Kryo> kryos = ThreadLocal.withInitial(Kryo::new); private Class<T> clazz; public KryoRedisSerializer(Class<T> clazz) super(); this.clazz = clazz; @Override public byte[] serialize(T t) throws SerializationException if (t == null) return EMPTY_BYTE_ARRAY; Kryo kryo = kryos.get(); kryo.setReferences(false); kryo.register(clazz); try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); Output output = new Output(baos)) kryo.writeClassAndObject(output, t); output.flush(); return baos.toByteArray(); catch (Exception e) logger.error(e.getMessage(), e); return EMPTY_BYTE_ARRAY; @Override public T deserialize(byte[] bytes) throws SerializationException if (bytes == null || bytes.length <= 0) return null; Kryo kryo = kryos.get(); kryo.setReferences(false); kryo.register(clazz); try (Input input = new Input(bytes)) return (T) kryo.readClassAndObject(input); catch (Exception e) logger.error(e.getMessage(), e); return null;
- 上述代码有以下三点需要注意:
- kryos是ThreadLocal对象,由ThreadLocal.withInitial创建,如此一来,每次调用kryos.get方法,都能取出当前线程对应的Kryo实例,如果没有会调用new方法创建;
- serialize方法负责将对象实例序列化成byte数组;
- deserialize方法复制将byte数组反序列化成对象实例;
创建Redis配置类
-
Redis配置类如下,通过setValueSerializer方法将KryoRedisSerializer设置位value的序列化器:
@Configuration public class RedisConfig /** * redisTemplate 序列化使用的Serializeable, 存储二进制字节码, 所以自定义序列化类 * @param redisConnectionFactory * @return */ @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); // redis value使用的序列化器 template.setValueSerializer(new KryoRedisSerializer<>(Object.class)); // redis key使用的序列化器 template.setKeySerializer(new StringRedisSerializer()); template.afterPropertiesSet(); return template;
将存、取、删除等针对对象的基本操作封装成一个服务类
-
本次会用到存、取、删除等操作,所以做个简单的封装,实际使用中,您可以自己定制:
/** * @Description : 封装了redis的操作 * @Author : zq2599@gmail.com * @Date : 2018-06-10 22:52 */ @Service public class RedisClient private static final Logger logger = LoggerFactory.getLogger(RedisClient.class); @Autowired private RedisTemplate<Object, Object> redisTemplate; private RedisConnection getConnection() return redisTemplate.getConnectionFactory().getConnection(); /** * 释放连接 * @param redisConnection */ private void releaseConnection(RedisConnection redisConnection) if(null!=redisConnection && null!=redisTemplate) RedisConnectionFactory redisConnectionFactory = redisTemplate.getConnectionFactory(); if(null!=redisConnectionFactory) RedisConnectionUtils.releaseConnection(redisConnection, redisConnectionFactory); /** * 获取缓存的key * @param id * @return */ private <T> byte[] getKey(T id) RedisSerializer serializer = redisTemplate.getKeySerializer(); return serializer.serialize(id); /** * 更新缓存中的对象,也可以在redis缓存中存入新的对象 * * @param key * @param t * @param <T> */ public <T> void set(String key, T t) byte[] keyBytes = getKey(key); RedisSerializer serializer = redisTemplate.getValueSerializer(); byte[] val = serializer.serialize(t); RedisConnection redisConnection = getConnection(); if(null!=redisConnection) try redisConnection.set(keyBytes, val); finally releaseConnection(redisConnection); else logger.error("1. can not get valid connection"); /** * 删除指定对象 * @param key * @return */ public long del(String key) RedisConnection redisConnection = getConnection(); long rlt = 0L; if(null!=redisConnection) try rlt = redisConnection.del(getKey(key)); finally releaseConnection(redisConnection); else logger.error("1. can not get valid connection"); return rlt; /** * 从缓存中取对象 * * @param key * @param <T> * @return */ public <T> T getObject(String key) byte[] keyBytes = getKey(key); byte[] result = null; RedisConnection redisConnection = getConnection(); if(null!=redisConnection) try result = redisConnection.get(keyBytes); finally releaseConnection(redisConnection); else logger.error("2. can not get valid connection"); return null!=redisConnection ? (T) redisTemplate.getValueSerializer().deserialize(result) : null;
- 以上代码有一处需注意:Redis连接对象RedisConnection使用完毕后,一定要记得释放!
- 上面用到的Person是个简单对象,如下:
package com.bolingcavalry.springbootrediskyrodemo.bean;
import java.io.Serializable;
public class Person implements Serializable
private static final long serialVersionUID = 1L;
private int id;
private String name;
private int age;
public String getName()
return name;
public int getId()
return id;
public void setId(int id)
this.id = id;
public void setName(String name)
this.name = name;
public int getAge()
return age;
public void setAge(int age)
this.age = age;
### 启动工程,通过web请求操作,并在Redis后台检查数据
- 现在可以启动SpringBoot工程进行验证了:
1. 运行SpringBootApplication;
2. 在浏览器输入http://localhost:8080/add/1/Jerry/11即可新增一条记录(注意localhost:8080换成您的服务所在地址和端口),如下图:
![image.png](https://s4.51cto.com/images/blog/202206/03085933_62995cf5e067a61649.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
3. 用redis-cli命令登录redis,执行命令**get person_1**,看到内容如下:
```shell
root@00afb3eb191b:/data# redis-cli
127.0.0.1:6379> get person_1
"\\x01\\x00com.bolingcavalry.springbootrediskyrodemo.bean.Perso\\xee\\x02\\x02Jerr\\xf9"
-
在浏览器输入http://localhost:8080/get/1即可查询记录,如下图:
-
在浏览器输入http://localhost:8080/del/1即可删除记录,如下图:
-
在浏览器输入http://localhost:8080/get/1查询记录,会提示找不到记录,如下图:
- Redis后台也查不到了:
127.0.0.1:6379> get person_1 (nil)
- 至此,使用Kyro作为redis序列化工具的实战已经完成,希望能对您的开发提供一些参考;
欢迎关注51CTO博客:程序员欣宸
以上是关于SpringBoot下用Kyro作为Redis序列化工具的主要内容,如果未能解决你的问题,请参考以下文章
SpringBoot整合Redis解决LocalDateTime序列化问题
解决Springboot使用Redis反序列化遇到的类型转换异常
解决Springboot使用Redis反序列化遇到的类型转换异常