1. 程式人生 > >SpringBoot下用Kyro作為Redis序列化工具

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

有時候我們需要將Java物件例項存入Redis,常用方法有兩種:
1. 將物件序列化成字串後存入Redis;
2. 將物件序列化成byte陣列後存入Redis;

本章實戰上述第二種方式,並且序列化工具選擇了Kyro,為了便於開發和驗證,將程式碼寫在一個基於SpringBoot的web工程中;

原始碼下載

本章實戰的原始碼可以在github下載,地址和連結資訊如下表所示:

名稱 連結 備註
git倉庫地址(ssh) [email protected]:zq2599/blog_demos.git 該專案原始碼的倉庫地址,ssh協議

這個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工程,新增依賴

  1. spring-boot-starter-parent的版本是1.4.1.RELEASE;
  2. 基於maven建立SpringBoot工程,需要依賴:spring-boot-starter-web、spring-boot-starter-data-redis;
  3. 在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;
    }
}

上述程式碼有以下三點需要注意:
1. kryos是ThreadLocal物件,由ThreadLocal.withInitial建立,如此一來,每次呼叫kryos.get方法,都能取出當前執行緒對應的Kryo例項,如果沒有會呼叫new方法建立;
2. serialize方法負責將物件例項序列化成byte陣列;
3. 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 : [email protected]
 * @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換成您的服務所在地址和埠),如下圖:
這裡寫圖片描述
3. 用redis-cli命令登入redis,執行命令get person_1,看到內容如下:

[email protected]:/data# redis-cli
127.0.0.1:6379> get person_1
"\x01\x00com.bolingcavalry.springbootrediskyrodemo.bean.Perso\xee\x02\x02Jerr\xf9"
127.0.0.1:6379> get person_1
(nil)

至此,使用Kyro作為redis序列化工具的實戰已經完成,希望能對您的開發提供一些參考;

相關推薦

SpringBootKyro作為Redis序列工具

有時候我們需要將Java物件例項存入Redis,常用方法有兩種: 1. 將物件序列化成字串後存入Redis; 2. 將物件序列化成byte陣列後存入Redis; 本章實戰上述第二種方式,並且序列化工具選擇了Kyro,為了便於開發和驗證,將程式碼寫在一個基於

pytorch可采visidom作為可視工具

設置 運行 opts data 不能 文件鏈接 沒有 上網 bsp 2018/9/18更新 感覺tensorboardX插件更好用,已轉用https://github.com/lanpa/tensorboardX 更新:新版visdom0.1.7安裝方式為:conda

自定義redis序列工具

我們 utils 字節數 pac keys ted ive onu 問題 redis一個優點就是可以將數據寫入到磁盤中。 我們知道寫入磁盤的數據實際上都是以字節(0101這樣的二進制數據)的形式寫入的。 這意味著如果我們要將一個對象寫入磁盤,就必須將這個對象序列化。 jav

Springboot+Redis序列

今天在測試springboot整合redis的時候遇到下面這個坑,百度來百度去發現提示都是ajax的問題,真的是醉了,錯誤提示如下所示,不信大家可以直接複製百度一下答案是什麼(流淚中。。。。),錯誤如下: org.springframework.data.redis.serializer.Seria

SpringBoot Redis序列配置

Redis配置 #Redis spring.redis.host= spring.redis.port=6379 spring.redis.database=0 # Redis伺服器連線密碼(預設為空) spring.redis.password= # 連線池最大連線數(使用負值表示沒有限制) sp

redis 序列存入對象

rac ioe tin pan input trace cnblogs bject oid redis 序列化存入對象 //序列化 public static byte [] serialize(Object obj){ ObjectOutpu

redis序列對象操作

com sql iss stat nts 字符 project esc sage 在項目開發過程中,一些經常用到但又基本不變的對象信息我們可以把它緩存起來,這樣可以減少我們操作msql等數據庫的時間 緩存對象信息最常用的有兩種,一種是將對象信息轉成json形式的字符串緩存起

Redis 序列方式StringRedisSerializer、FastJsonRedisSerializer和KryoRedisSerializer

out public ack 我們 must instance key) strings initial 當我們的數據存儲到Redis的時候,我們的鍵(key)和值(value)都是通過Spring提供的Serializer序列化到數據庫的。RedisTemplate默認使

Redis-序列和存儲模式

min 無法 dump hang 兩種模式 行操作 請求 開啟 日誌 Redis中數據存儲模式有2種:cache-only,persistence; ? cache-only即只做為“緩存”服務,不持久數據,數據在服務終止後將消失,此模式下也將不存在“數據恢復”的手段,是一

改變可識別redis序列方式

bind creat void fast 定義 fault 字體 object autoconf 原來系統所看到的是jdk的默認序列化方式,需要更改設置才可以變為可識別的字體 package com.redisSeri.data.redis; import com.

Spring Data Redis 序列

mar ext 速度 亦或 access 序列化機制 png 現在 圖片 在Spring中使用的Redis緩存數據,可以通過RedisTemplate直接操作,也可以通過@Cacheable註解實現緩存(可參照另一篇文章的介紹:Spring Cachable Key的定義及

C JSON字串的反序列

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

redis序列java8 LocalDateTime

org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Can not construct instance of java.time.LocalDateTime: no su

linuxdocker啟動redis叢集

問題起源:我在專案中連線redis叢集連線不上,在伺服器上檢視redis叢集是否啟動。----伺服器虛擬機器用的是docker; 首先用命令:docker ps -a檢視開啟/關閉狀態; 可以看到reids-d1、reids-d2已經redis-s0、redis-s1、redis-s2

mac安裝redisredis視覺工具rdm並且連線redis

一、安裝redis 最最最最簡單和推薦的方法就是使用brew命令安裝,前提是你的mac要安裝brew brew install redis 然後就等安裝完畢就好了 二、安裝rdm 直接安裝rdm dmg檔案 https://pan.baidu.com/s/10vpdhw7YfDD7G4y

spring-redis序列

  (一)spring data redis 提供了多種可選擇策略(RedisSerializer) JdkSerializationRedisSerializer:POJO物件的存取場景,使用JDK本身序列化機制,將pojo類通過ObjectInputStream

springboot學習——使用HttpMessageConverter進行http序列和反序列

本文轉發自:https://segmentfault.com/a/1190000012658289 物件的序列化/反序列化大家應該都比較熟悉:序列化就是將object轉化為可以傳輸的二進位制,反序列化就是將二進位制轉化為程式內部的物件。序列化/反序列化主要體現在程式I/O這個過程中,包括網路I/

SpringBootjava操作MongoDB資料庫的增刪改查

首先我們需要建立一個SpringBoot工程,在IDEA中有快捷的建立方式。new -> project -> Spring Initializr 根據需要一路next下去即可第二步,給application.properties新增MongoDB配置#Mongo

關於:“無法序列化會話狀態。在“StateServer”或“SQLServer”模式,ASP.NET 將序列化會話狀態物件,因此不允許使用無法序列化的物件或 MarshalByRef 物件。如果自定義會話狀態儲存在“Custom”模式執行了類似的序列化

錯誤描述: 無法序列化會話狀態。在“StateServer”或“SQLServer”模式下,ASP.NET 將序列化會話狀態物件,因此不允許使用無法序列化的物件或 MarshalByRef 物件。如果

spring整合redis,序列物件,以及websocket依賴注入

      最近業餘在使用websocket開發一個聊天系統,打算使用redis儲存聊天記錄。       首先匯入spring整合redis的包spring-data-redis-1.6.2.RELEASE.jar, redis java驅動包jedis-2.9.0.ja