写点什么

SpringBoot 下用 Kyro 作为 Redis 序列化工具

作者:程序员欣宸
  • 2022 年 6 月 03 日
  • 本文字数:4934 字

    阅读完需:约 16 分钟

SpringBoot下用Kyro作为Redis序列化工具

欢迎访问我的 GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

本篇概览

  • 有时候我们需要将 Java 对象实例存入 Redis,常用方法有两种:


  1. 将对象序列化成字符串后存入 Redis;

  2. 将对象序列化成 byte 数组后存入 Redis;


本章实战上述第二种方式,并且序列化工具选择了 Kyro,为了便于开发和验证,将代码写在一个基于 SpringBoot 的 web 工程中;

源码下载

  • 本章实战的源码可以在 github 下载,地址和链接信息如下表所示:



  • 这个 git 项目中有多个文件夹,本章源码在 springboot-redis-kyro-demo 文件夹下,如下图所示:


环境信息

  1. JDK:1.8.0_144;

  2. SpringBoot:1.4.1.RELEASE;

  3. Kyro:4.0.0;

  4. Redis:3.2.11;

开发步骤列表

  • 动手前先将整体步骤梳理一下:


  1. 准备 Reids 环境;

  2. 创建 SpringBoot 工程,添加依赖;

  3. 在 application.properties 中配置 Redis 相关信息;

  4. 创建基于 Kyro 的序列化接口实现类;

  5. 创建 Redis 配置类;

  6. 将存、取、删除等针对对象的基本操作封装成一个服务类;

  7. 开发一个 Controller,对应几个 web 接口,用来验证对 Redis 的存、取、删除操作;

  8. 启动工程,通过 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=0spring.redis.host=192.168.31.104spring.redis.port=6379spring.redis.pool.max-active=2500spring.redis.pool.max-wait=6000spring.redis.pool.max-idle=500spring.redis.pool.min-idle=100spring.redis.pool.testOnBorrow=truespring.redis.pool.blockWhenExhausted=truespring.redis.pool.numTestsPerEvictionRun=3spring.redis.pool.timeBetweenEvictionRunsMillis=-1spring.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; }}
复制代码
  • 上述代码有以下三点需要注意:


  1. kryos 是 ThreadLocal 对象,由 ThreadLocal.withInitial 创建,如此一来,每次调用 kryos.get 方法,都能取出当前线程对应的 Kryo 实例,如果没有会调用 new 方法创建;

  2. serialize 方法负责将对象实例序列化成 byte 数组;

  3. deserialize 方法复制将 byte 数组反序列化成对象实例;

创建 Redis 配置类

  • Redis 配置类如下,通过 setValueSerializer 方法将 KryoRedisSerializer 设置位 value 的序列化器:


@Configurationpublic 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 */@Servicepublic 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 换成您的服务所在地址和端口),如下图:


  1. 用 redis-cli 命令登录 redis,执行命令 get person_1,看到内容如下:


root@00afb3eb191b:/data# redis-cli127.0.0.1:6379> get person_1"\x01\x00com.bolingcavalry.springbootrediskyrodemo.bean.Perso\xee\x02\x02Jerr\xf9"
复制代码


  1. 在浏览器输入 http://localhost:8080/get/1 即可查询记录,如下图:


  1. 在浏览器输入 http://localhost:8080/del/1 即可删除记录,如下图:

  2. 在浏览器输入 http://localhost:8080/get/1 查询记录,会提示找不到记录,如下图:


  1. Redis 后台也查不到了:


127.0.0.1:6379> get person_1(nil)
复制代码
  • 至此,使用 Kyro 作为 redis 序列化工具的实战已经完成,希望能对您的开发提供一些参考;

欢迎关注 InfoQ:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...


发布于: 2022 年 06 月 03 日阅读数: 44
用户头像

搜索"程序员欣宸",一起畅游Java宇宙 2018.04.19 加入

前腾讯、前阿里员工,从事Java后台工作,对Docker和Kubernetes充满热爱,所有文章均为作者原创,个人Github:https://github.com/zq2599/blog_demos

评论

发布
暂无评论
SpringBoot下用Kyro作为Redis序列化工具_Java_程序员欣宸_InfoQ写作社区