11、基于Springboot与Jedis实现客户端操作

0.引言

之前的几期中我们讲解的redis的众多工作原理,但是一直没有说明过如何在java项目中实际的操作redis,所以本期我们就来讲解redis的客户端——Jedis

1. 实操

1、 创建一个springboot项目;

2、 pom.xml中引入Jedis依赖;

<dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
</dependency>

3、 application.properties配置文件中添加jedis配置,注意这里的线程池连接数根据自己服务器的配置来设置;

# redis配置
# redis服务ip
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
# redis连接超时时间
spring.redis.timeout=2000
# 连接池最大连接数(负数表示没有限制),默认8
spring.redis.jedis.pool.max-active=200
# 连接池最大空闲连接数,默认8
spring.redis.jedis.pool.max-idle=50
# 连接池最小空闲连接数,默认0
spring.redis.jedis.pool.min-idle=10

4、 创建Jedis配置类,生成连接池;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @author benjamin_5
 * @Description
 * @date 2022/12/22
 */
@Configuration
public class JedisConfig {

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

    @Value("${spring.redis.port}")
    private Integer port;
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.jedis.pool.max-idle}")
    private Integer maxIdle;
    @Value("${spring.redis.jedis.pool.max-active}")
    private Integer maxActive;
    @Value("${spring.redis.jedis.pool.min-idle}")
    private Integer minIdle;
    @Value("${spring.redis.timeout}")
    private Integer timeout;

    @Bean
    public JedisPool jedisPool() {

        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 最大空闲连接数
        jedisPoolConfig.setMaxIdle(maxIdle);
        // 最大连接数
        jedisPoolConfig.setMaxTotal(maxActive);
        // 最小空闲连接数
        jedisPoolConfig.setMinIdle(minIdle);
        // 连接空闲多久后释放,当空闲时间大于该值且空闲连接大于最大空闲连接数时直接释放连接线程
        jedisPoolConfig.setSoftMinEvictableIdleTimeMillis(10000);
        // 连接最小空闲时间
        jedisPoolConfig.setMinEvictableIdleTimeMillis(1800000);
        // 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1
        jedisPoolConfig.setMaxWaitMillis(1500);
        // 在获取连接的时候检查有效性, 默认false
        jedisPoolConfig.setTestOnBorrow(true);
        // 在空闲时检查有效性, 默认false
        jedisPoolConfig.setTestWhileIdle(true);
        // 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
        jedisPoolConfig.setBlockWhenExhausted(false);
        if (StringUtils.isBlank(password)) {

            password = null;
        }
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);
        logger.info("redis连接成功-{}:{}", host, port);
        return jedisPool;
    }

}

5、 直接使用jedis的接口不是很好用,我们可以创建工具类,将常用操作二次包装,如下所示,这样我们后续就可以直接引用工具类来调用了;

package com.wu.springboot_study.util;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.exceptions.JedisConnectionException;

import java.io.*;

/**
 * @author benjamin_5
 * @Description Jedis工具类
 * @date 2022/12/22
 */
@AllArgsConstructor
@Component
public class JedisUtil {

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

    private final JedisPool jedisPool;

    /**
     * 关闭资源
     * @param jedis
     */
    private void close(Jedis jedis){

        if(jedis != null){

            jedis.close();
        }
    }

    private String getKey(String namespace, String key){

        if(StringUtils.isNotBlank(namespace)){

            key = String.format("%s:%s", namespace, key);
        }
        return key;
    }

    private byte[] getKeyBytes(String namespace, String key){

        return getKey(namespace, key).getBytes();
    }

    /**
     * 设置缓存
     * @param namespace 命名空间
     * @param key 键
     * @param value 值
     * @param expireSecond 过期时间,<=0为不过期,单位s
     * @param <T>
     */
    public <T extends Serializable> void set(String namespace, String key, T value, long expireSecond){

        Jedis jedis = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {

            jedis = jedisPool.getResource();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(value);
            oos.flush();
            if(expireSecond <= 0){

                jedis.set(getKeyBytes(namespace, key), bos.toByteArray());
            }else{

                jedis.setex(getKeyBytes(namespace, key), expireSecond ,bos.toByteArray());
            }
        }catch (JedisConnectionException e){

            logger.error("jedis connection fail: {}" , e.getMessage());
        } catch (IOException e) {

            logger.error("对象序列化失败: {}" , e.getMessage());
        } finally {

            try {

                bos.close();
            } catch (IOException e) {

                e.printStackTrace();
            }
            close(jedis);
        }
    }

    public <T extends Serializable> void set(String namespace, String key, T value){

        set(namespace,key,value,0);
    }

    public <T extends Serializable> void set(String key, T value){

        set(null,key,value,0);
    }

    /**
     * 获取缓存
     * @param namespace 命名空间
     * @param key 键
     * @param <T> 返回值类型
     * @return
     */
    public <T extends Serializable> T get(String namespace,String key){

        Jedis jedis = null;
        ByteArrayInputStream bis = null;
        try {

            jedis = jedisPool.getResource();
            byte[] values = jedis.get(getKeyBytes(namespace, key));
            if (values == null) {

                return null;
            }
            bis = new ByteArrayInputStream(values);
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (T) ois.readObject();
        }catch (ClassNotFoundException e){

            logger.error("对象类型映射失败:{}" , e.getMessage());
        }catch (JedisConnectionException e){

            logger.error("jedis connection fail: {}" , e.getMessage());
        } catch (IOException e) {

            logger.error("对象反序列化失败: {}" , e.getMessage());
        } finally {

            try {

                if(bis != null){

                    bis.close();
                }
            } catch (IOException e) {

                e.printStackTrace();
            }
            close(jedis);
        }
        return null;
    }

    public <T extends Serializable> T get(String key){

        return get(null, key);
    }

    /**
     * 设置过期时间
     * @param namespace 命名空间
     * @param key 键
     * @param expireSecond 过期时间 单位s
     */
    public void expire(String namespace, String key, long expireSecond){

        Jedis jedis = null;
        try {

            jedis = jedisPool.getResource();
            jedis.expire(getKeyBytes(namespace, key), expireSecond);
        }catch (JedisConnectionException e){

            logger.error("jedis connection fail: {}" , e.getMessage());
        } finally {

            close(jedis);
        }
    }

    public void expire(String key, long expireSecond){

        expire(null, key, expireSecond);
    }

    /**
     * key值是否存在
     * @param namespace 命名空间
     * @param key 键
     * @return
     */
    public boolean exists(String namespace, String key){

        Jedis jedis = null;
        try {

            jedis = jedisPool.getResource();
            return jedis.exists(getKeyBytes(namespace, key));
        }catch (JedisConnectionException e){

            logger.error("jedis connection fail: {}" , e.getMessage());
        } finally {

            close(jedis);
        }
        return false;
    }

    public boolean exists(String key){

        return exists(null, key);
    }

    /**
     * 删除缓存
     * @param namespace 命名空间
     * @param key 键
     * @return
     */
    public boolean remove(String namespace, String key){

        Jedis jedis = null;
        try {

            jedis = jedisPool.getResource();
            jedis.del(getKeyBytes(namespace, key));
            return true;
        }catch (JedisConnectionException e){

            logger.error("jedis connection fail: {}" , e.getMessage());
        } finally {

            close(jedis);
        }
        return false;
    }

    public boolean remove(String key){

        return remove(null, key);
    }

    /**
     * 键值加1
     * @param namespace 命名空间
     * @param key 键
     * @return
     */
    public Long incr(String namespace, String key){

        Jedis jedis = null;
        try {

            jedis = jedisPool.getResource();
            return jedis.incr(getKeyBytes(namespace, key));
        }catch (JedisConnectionException e){

            logger.error("jedis connection fail: {}" , e.getMessage());
        } finally {

            close(jedis);
        }
        return null;
    }

    public Long incr(String key){

        return incr(null, key);
    }

    /**
     * 键值减1
     * @param namespace
     * @param key
     * @return
     */
    public Long decr(String namespace, String key){

        Jedis jedis = null;
        try {

            jedis = jedisPool.getResource();
            return jedis.decr(getKeyBytes(namespace, key));
        }catch (JedisConnectionException e){

            logger.error("jedis connection fail: {}" , e.getMessage());
        } finally {

            close(jedis);
        }
        return null;
    }

    public Long decr(String key){

        return decr(null, key);
    }

}

6、 在controller中调用测试;

@RestController
@RequestMapping("user")
@AllArgsConstructor
public class UserRestController {

    private final JedisUtil jedisUtil;

    @GetMapping("setCache")
    public String setCache(String key, String value){

        jedisUtil.set(key,value);
        return key + ":" + jedisUtil.get(key);
    }
}

7、 调用测试接口,数据显示正常,说明redis连接并调用成功;

&nbsp;

8、 如启动出现如下报错,则是因为jedis版本高导致,可以降低jedis的版本;

Description:

An attempt was made to call a method that does not exist. The attempt was made from the following location:

    redis.clients.jedis.JedisPoolConfig.<init>(JedisPoolConfig.java:11)

The following method did not exist:

    redis.clients.jedis.JedisPoolConfig.setMinEvictableIdleTime(Ljava/time/Duration;)V

总结

如上,我们关于redis客户端jedis的操作演示就结束了,更多关于jedis接口的使用需要我们自己去摸索,将其封装为工具类,下一期,我们继续讲解其他redis客户端