Redis-KV数据库Java连接以及Jedis包的使用

2022-09-29 16:47:06 浏览数 (1)

0x00 前言

描述:Redis是一个开源的Key-Value数据缓存,和Memcached类似。现在基本上主流的语言都有客户端支持,比如java、C、C#、C 、php、Node.js、Go等。

Redis多种类型的value,包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型)。

Jedis 是 Redis 官方首选的 Java 客户端开发包,其他我们有shardjedis可以进行备选;

环境准备: 开始在 Java 中使用 Redis 前, 我们需要确保已经安装了 redis 服务及 Java redis 驱动,且你的机器上能正常使用 Java。

Java redis 驱动:https://mvnrepository.com/artifact/redis.clients/jedis

组件依赖:

代码语言:javascript复制
jedis-2.9.3.jar
org.apache.commons » commons-pool2	2.6.2	2.8.0
org.slf4j » slf4j-simple 1.7.30
0x01 基础语法

描述:主要列举了Jedis中常用的方法

代码语言:javascript复制
#(1) redis的默认端口是6379
Jedis jedis = new Jedis ("localhost",6379);


#(2) 密码验证
jedis.auth("password"); 


#(3) 连接验证
jedis.connect();
jedis.ping();


#(4) 断开连接
jedis.disconnect();


#(5) 数据库号选择
jedis.select(1);


#(6) 字符串键值设置与获取(增删改查)
jedis.set("Key","Value");
jedis.setex("foo", 5, "tor"); #将值value关联到key,并将key的生存时间设为seconds(秒)。 
jedis.get("Key");
Set<String> keys = jedis.keys("*");   #列出所有的key并存入集合中
Set<String> keys = jedis.keys("key"); #查找特定的key并存入集合中
jedis.exists("key1");    #检查给定key是否存在
jedis.expire("key1", 5); #设置key生存时间,当key过期时,它会被自动删除。 
jedis.persist("key1");   #移除给定key的生存时间(设置这个key永不过期)
jedis.type("key1");      #返回key所储存的值的类型。 
#none(key不存在),string(字符串),list(列表),set(集合),zset(有序集),hash(哈希表) 
jedis.rename("key1", "key2"); #将key改名为newkey,当key和newkey相同或者key不存在时,返回一个错误
jedis.del("key1","key2","key3","key4","key5"); #移除给定的一个或多个key不存在则不执行;
jedis.dbSize();   #返回key的个数 
jedis.flushAll(); #清空所有的key


#(7) List集合增删改查
jedis.lpush("ListKey1","Value0");  #将值value插入到列表key的表头。 
jedis.lpush("ListKey1","Value1");
jedis.llen("key1")   #返回列表key的长度。 
#//返回列表key中指定区间内的元素,区间以偏移量start和stop指定.
#//下标(index)参数start和stop从0开始;
#//负数下标代表从后开始(-1表示列表的最后一个元素,-2表示列表的倒数第二个元素,以此类推)
List<String> list = jedis.lrange("ListKey1", 0 ,2); #lrange("key1", 0, -1);  stop下标也在取值范围内(闭区间)
for(int i=0;i<list.size();i  ){ 
   System.out.println(list.get(i)); 
} 


#(8) Hash集合增删改查
jedis.hset("hashkey1", "field1", "field1-value"); #设置单个哈希表key中的域field的值设为value
jedis.hset("hashkey1", "field2", "field2-value"); #可以不断向哈希表key中添加键值对

Map<String,String> map = new HashMap<String,String>(); 
map.put("field1", "field1-value"); 
map.put("field2", "field2-value"); 
jedis.hmset("hkey1", map);    #哈希表key中的不同域field的值设为value

jedis.hkeys("key1");          #返回哈希表key中的所有域
jedis.hvals("key1");          #返回哈希表key中的所有值

jedis.hget("hkey1", "field1"); #返回哈希表key中给定域field的值 
Map<String,String> map = jedis.hgetAll("hashkey1");  #返回哈希表key中所有域和值
for(Map.Entry entry: map.entrySet()) { 
   System.out.print(entry.getKey()   ":"   entry.getValue()   "t"); 
} 

jedis.hexists("key1", "field1");      #查看哈希表key中,给定域field是否存在
jedis.hdel("key1", "field1","field2");#删除哈希表key中的一个或多个指定域


#(9) set集合增删改查
jedis.sadd("key1", "value0");  #//将member元素加入到集合key当中。 
jedis.sadd("key1", "value1");
jedis.sismember("key1", "value2"));  #//判断元素是否是集合key的成员

jedis.scard("key1");                 #//返回集合key的元素的数量
Set set = jedis.smembers("key1");    #//返回集合key中的所有成员。 
for (Object val : set) {
  System.out.println(val);
}

#交、并、补
jedis.sinter("key1","key2")    #//返回一个集合的全部成员,该集合是所有给定集合的交集
jedis.sunion("key1","key2")    #//返回一个集合的全部成员,该集合是所有给定集合的并集
jedis.sdiff("key1","key2");    #//返回一个集合的全部成员,该集合是所有给定集合的差集

jedis.srem("key1", "value1");  #//移除集合中的member元素。 


#(10)额外补充
jedis.set("count","1")
System.out.println("incr key = "   jedis.incr("count"));
System.out.println("incrby key 5 = "   jedis.incrBy("count", 5));

0x02 实际案例

描述: 采用Jedis连接Redis数据库的基础使用案例

Redis连接认证配置文件:config.properties

代码语言:javascript复制
# Connection Redis Configure
RedisUrl=10.20.10.248:6379
RedisAuth=weiyigeek.top

示例1.基础语法使用

代码语言:javascript复制
package top.weiyigeek.connredis;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import redis.clients.jedis.Jedis;

/**
 * Redis 连接测试
 * @author WeiyiGeek
 */
public class RedisDemo1 {
    public static void main(String[] args) {
        String[] RedisUrl=null;
        String RedisAuth=null;
        try {
            //1.创建一个属性配置对象并打开配置文件
            Properties prop = new Properties();
            InputStream ins = RedisDemo1.class.getClassLoader().getResourceAsStream("config.properties");
            prop.load(ins);
            RedisUrl=prop.getProperty("RedisUrl").split(":");
            RedisAuth=prop.getProperty("RedisAuth");
            System.out.println("RedisServer:"  prop.getProperty("RedisUrl")   "nPassword:"   RedisAuth );
            
            //2.连接到 redis 服务
            Jedis jedis = new Jedis(RedisUrl[0],Integer.parseInt(RedisUrl[1]));
            System.out.println("正在Redis认证连接...");
            jedis.auth(RedisAuth);
   
            //3.查看链接是否成功并且服务是否运行
            System.out.println("服务正在运行: " jedis.ping() "n");
            
            //4.选择一号库
            jedis.select(1);
             
            //5.设置 redis 字符串数据与 获取存储的数据并输出
            String key = "WeiyiGeek";
            jedis.set(key, "www.weiyigeek.top");
            jedis.set("count", "1");
            jedis.setex("Key", 60, "60 Sec");
            System.out.println("当前数据库总键数:" jedis.dbSize());
            if(jedis.exists(key)) {
                System.out.println("Redis中WeyiGeek键存储的字符串为:"  jedis.get(key));
                System.out.println("其类型为 : "   jedis.type(key));
            }
            //value   1 并返回其其值
            System.out.println("incr key = "   jedis.incr("count"));
            System.out.println("incrby key 5 = "   jedis.incrBy("count", 5));
            
            //6.采用迭代器进行遍历所有键值
            Set<String> keys = jedis.keys("*"); 
            for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
                String k = (String) iterator.next();
                if (jedis.type(k).equalsIgnoreCase("string")) {
                    System.out.println(k   " - "   jedis.get(k));
                }
                    
            }
            System.out.println("");
            
            //List集合
            RedisListTest(jedis);
            
            //Hash集合
            RedisHashTest(jedis);
            
            //Set集合
            RedisSetTest(jedis);
            
            //Zset集合
            RedisZsetTest(jedis);
            
            //清空所有的key
            jedis.flushAll();
            
            //关闭释放jedis连接资源
            jedis.disconnect();
            
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    //7.列表集合存储K-V数据到列表
    public static void RedisListTest(Jedis jedis) {
        jedis.lpush("site-list", "WeiyiGeek");
        jedis.lpush("site-list", "Google");
        jedis.lpush("site-list", "Taobao");
        System.out.println("列表中元素数量:  "   jedis.llen("site-list"));
        // 获取存储的数据并输出
        List<String> list = jedis.lrange("site-list", 0 , 2);
        for(int i=0; i<list.size(); i  ) {
            System.out.println("列表项为: " list.get(i));
        }
    }

    //8.Hash值
    public static void RedisHashTest(Jedis jedis) {
        //hash值存入
        Map<String, String> map = new HashMap<String, String>();
        jedis.hset("hashkey1", "field1", "1024");  //
        jedis.hset("hashkey1", "field2", "whoami");
        map.put("field3","This");
        map.put("field4","10.24");
        jedis.hmset("hashkey1", map);
        
        if (jedis.hexists("hashkey1", "field1")) {
            System.out.println("n返回哈希表中给定field的值:"   jedis.hget("hashkey1", "field1"));
            System.out.println("哈希表所有中field域名称:  "   jedis.hkeys("hashkey1"));
            System.out.println("哈希表所有中field域的值:  "  jedis.hvals("hashkey1"));
        }
        //循环遍历读取添加的域与值
        Map <String,String> map1 = jedis.hgetAll("hashkey1");
        for (Map.Entry<String, String> entry: map1.entrySet()) {
            System.out.println("域:"   entry.getKey()   ", 值:  "   entry.getValue());
        }
    }
    
    //9.Set集合
    public static void RedisSetTest(Jedis jedis) {
        jedis.sadd("setkey1", "Java");
        jedis.sadd("setkey1", "Python");
        jedis.sadd("setkey2", "Redis");
        jedis.sadd("setkey2", "Java");
        jedis.sadd("setkey2", "Java"); //自动剔除重复的元素
        System.out.println("nset 集合中setkey2键中元素数量 : "   jedis.scard("setkey2"));
        
        if(!jedis.sismember("setkey1", "setkey2")) {
            Set set = jedis.smembers("setkey2");   //返回集合key中的所有成员。 
            for (Object val : set) {
                System.out.println(val);
            }
        }
        //#交、并、补
        System.out.println("交集:"   jedis.sinter("setkey1","setkey2"));  //返回一个集合的全部成员,该集合是所有给定集合的交集
        System.out.println("并集:"   jedis.sunion("setkey1","setkey2"));  //返回一个集合的全部成员,该集合是所有给定集合的并集
        System.out.println("差集:"   jedis.sdiff("setkey1","setkey2"));   //返回一个集合的全部成员,该集合是所有给定集合的差集
        
    }
    
    //10.Zset集合
    public static void RedisZsetTest(Jedis jedis) {
       jedis.zadd("Zsetkey", 0, "redis");
       jedis.zadd("Zsetkey", 0, "java");
       jedis.zadd("Zsetkey", 1, "eclipse");
       Map<String, Double> map = new HashMap<String, Double>();
       map.put("WeiyiGeek", 1.0);
       map.put("Weiyi", 3.0);
       map.put("极客", 4.0);
       jedis.zadd("Zsetkey", map);
       System.out.println("n有序集合元素个数: "   jedis.zcard("Zsetkey"));
       System.out.println("指定集合范围内的元素个数: "   jedis.zcount("Zsetkey", 0, 1));  //把1.0 等价于 1
       //循环遍历有序计集合
       Set set = jedis.zrangeByScore("Zsetkey",0, 100);
       for (Object val : set) {
           System.out.println(val);
       }
    }
}

执行结果:

代码语言:javascript复制
RedisServer:10.20.10.248:6379
Password:weiyigeek.top
正在Redis认证连接...
服务正在运行: PONG

当前数据库总键数:3
Redis中WeyiGeek键存储的字符串为:www.weiyigeek.top
其类型为 : string
incr key = 2
incrby key 5 = 7
count - 7
WeiyiGeek - www.weiyigeek.top
Key - 60 Sec

列表中元素数量:  3
列表项为: Taobao
列表项为: Google
列表项为: WeiyiGeek

返回哈希表中给定field的值:1024
哈希表所有中field域名称:  [field1, field3, field2, field4]
哈希表所有中field域的值:  [1024, whoami, 10.24, This]
域:field3, 值:  This
域:field2, 值:  whoami
域:field1, 值:  1024
域:field4, 值:  10.24

set 集合中setkey2键中元素数量 : 2
Java
Redis
交集:[Java]
并集:[Java, Python, Redis]
差集:[Python]

有序集合元素个数: 6
指定集合范围内的元素个数: 4
java
redis
WeiyiGeek
eclipse
Weiyi
极客

WeiyiGeek.Redis


0x03 工具包
Jedis连接池

描述:jedis连接资源的创建与销毁是很消耗程序性能,所以jedis为我们提供了jedis的池化技术,在创建时初始化一些连接资源存储到连接池中,使用jedis连接资源时不需要创建,而是从连接池中获取一个资源进行redis的操作,使用完毕后不需要销毁该jedis连接资源,而是将该资源归还给连接池供其他请求使用。

注意事项:

1) Redis连接池建立依赖Jedis与commons-pool2-2.8.0.jar包;

2) JedisPoolConfig继承关系,上面我们说到JedisPoolConfig需要依赖Apache common pool,其中pool配置依赖 common pool中的BaseObjectPoolConfig类中定义了相关属性的缺省值,在JedisPoolConfig中定义了相关的属性;

代码语言:javascript复制
#Jedispoolconfig继承关系
JedisPoolConfig -> GenericObjectPoolConfig -> BaseObjectPoolConfig -> Cloneable

#属性值&缺省值
setTestWhileIdle();  | true
setMinEvictableIdleTimeMillis(); | 60000
setTimeBetweenEvictionRunsMillis(); | 30000
setNumTestsPerEvictionRun(); | -1

3) JedisPoolConfig参数一览

代码语言:javascript复制
setJmxEnabled                          #设置是否启用JMX,默认true
setJmxNameBase(String jmxNameBase)     #设置JMX基础名
setJmxNamePrefix(String jmxNamePrefix) #设置JMX前缀名,默认值pool

setLifo(boolean lifo)  #设置连接对象是否后进先出默认true
setMaxIdle(int maxIdle) #设置最大空闲连接数满了则将逐出,默认为8(一般为最大连接数的45%~50%)
setMinIdle(int minIdle)  #设置无连接时池中最小的连接个数,默认连接0
setMaxTotal(int maxTotal) #设置最大连接数,默认18个
setMaxWaitMillis(long maxWaitMillis) #获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1

setTestOnBorrow(boolean testOnBorrow)  #从池中获取连接时是否测试连接的有效性,默认false(会消耗一定的时间但是为了稳定建议开启)
setTestOnCreate(boolean testOnCreate)  #在连接对象创建时测试连接对象的有效性,默认false
setTestOnReturn(boolean testOnReturn)  #在连接对象返回时是否测试对象的有效性,默认false
setTestWhileIdle(boolean testWhileIdle)  #在连接池空闲时是否测试连接对象的有效性,默认false
setBlockWhenExhausted(boolean blockWhenExhausted)  #当池中的资源耗尽时是否进行阻塞,设置false直接报错,true表示会一直等待,直到有可用资源

setNumTestsPerEvictionRun(int numTestsPerEvictionRun)              #每次逐出检查时,逐出连接的个数
setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis)     #设置连接最小的逐出间隔时间,默认1800000毫秒
setSoftMinEvictableIdleTimeMillis(softMinEvictableIdleTimeMillis); #对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断
setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) #设置连接对象有效性扫描间隔,设置为-1,则不运行逐出线程

setFairness(boolean fairness)  #当从池中获取资源或者将资源还回池中时 是否使用java.util.concurrent.locks.ReentrantLock.ReentrantLock 的公平锁机制,默认为false
setEvictionPolicyClassName(String evictionPolicyClassName)  #设置逐出策略,默认策略为"org.apache.commons.pool2.impl.DefaultEvictionPolicy"

getNumActive() #当前池中被激活的数量

基础示例1:(单机Redis连接池) jedis.properties

代码语言:javascript复制
#Jedis Pool Configure
RedisHost=10.20.172.248
RedisPort=6379
RedisAuth=WeiyiGeek.top
RedisDBIndex=1
RedisTimeout=20
RedisMaxIdle=50
RedisMinIdle=100
RedisMaxTotal=100
RedisWaitMillis=3000
RedisTestBorrow=true
RedisTestReturn=true
RedisBlockWhenExhausted=true

/Web/src/top/weiyigeek/utils/RedisPoolUtil.java

代码语言:javascript复制
package top.weiyigeek.utils;

import java.util.ResourceBundle;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Redis 连接池工具类建立
 * @author WeiyiGeek
 * @Date: 2020年5月31日 23:47:36
 * @Desc: Jedis(2.9.0)
 */
public class RedisPoolUtil {
    private static JedisPool jedisPool;  //jedis连接池
    private static String host; //redis主机
    private static Integer port; //redis端口
    private static String auth; //redis认证
    private static Integer dbindex; //redis仓库选择(0~15)
    private static Integer timeout; //redis连接超时时间(毫秒)
    private static Integer maxIdle; //在jedispool中最大的idle状态(空闲的)jedis实例的个数
    private static Integer minIdle; //在jedispool中最小的idle状态(空闲的)jedis实例的个数
    private static Integer maxTotal; //redis最大连接数
    private static Integer maxWaitMillis; //获取连接最大的等待时间(毫秒)
    private static Boolean testOnBorrow;//在borow一个jedis实例的时候,是否要进行验证操作,如果赋值true,则得到的jedis实例肯定是可以用的。
    private static Boolean testOnReturn; //在return一个jedis实例的时候,是否要进行验证操作,如果赋值true,则放回的jedis实例肯定是可以用的。
    private static Boolean blockWhenExhausted; //连接耗尽的时候是否阻塞,false会抛出异常,true阻塞直到超时。默认true
    
    
    
    //静态代码块可以在应用启动加载时候进行
    /** 读取jedis.properties配置文件 **/
    static {
        ResourceBundle rb = ResourceBundle.getBundle("jedis");
        host = rb.getString("RedisHost");
        port = Integer.parseInt(rb.getString("RedisPort"));
        auth = rb.getString("RedisAuth"); 
        dbindex = Integer.parseInt(rb.getString("RedisDBIndex"));
        timeout = Integer.parseInt(rb.getString("RedisTimeout"));
        maxIdle = Integer.parseInt(rb.getString("RedisMaxIdle"));
        minIdle = Integer.parseInt(rb.getString("RedisMinIdle"));
        maxTotal = Integer.parseInt(rb.getString("RedisMaxTotal"));
        maxWaitMillis = Integer.parseInt(rb.getString("RedisWaitMillis"));
        testOnBorrow = Boolean.parseBoolean(rb.getString("RedisTestBorrow"));
        testOnReturn = Boolean.parseBoolean(rb.getString("RedisTestReturn"));
        blockWhenExhausted = Boolean.parseBoolean(rb.getString("RedisBlockWhenExhausted"));
    }
    
   /** 方式1.类加载的时候初始化连接池,采用静态内部类中的静态域存储唯一一个实例,
    * 既保证了线程安全又保证了懒加载  (重点) **/
    static {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxIdle(maxIdle); 
        poolConfig.setMaxTotal(maxTotal);  
        poolConfig.setMaxWaitMillis(maxWaitMillis);
        poolConfig.setTestOnBorrow(testOnBorrow);
        poolConfig.setTestOnReturn(testOnReturn);
        poolConfig.setBlockWhenExhausted(blockWhenExhausted);
        jedisPool = new JedisPool(poolConfig, host, port, timeout, auth, dbindex);
    }
    
/** 获取 jedis 连接池 中连接**/
    public static Jedis getJedis(){
        System.out.println("当前Redis连接池被使用的数量: " (jedisPool.getNumActive() 1));
        return jedisPool.getResource();
    }
/** 补充:关闭 Jedis 将3.0版本开始使用 **/
//Redis JedisPool 的 returnResource 方法遭废弃,改用 close 替代。
    public static void close(Jedis jedis){
        if(jedis!=null)
            jedis.close(); 
    }
    
/** 返还redis连接到连接池(Jedis 小于 3.0版本使用) **/
//    public static void returnResource(Jedis jedis) {
//        if (jedis != null)
//            jedisPool.returnResource(jedis);
//    }
//    
  
    
/** 方式2.在多线程环境同步初始化,保证不要创建过多的jedispool和 jedis **/
//    private static synchronized void poolInit() {
//        if (jedisPool == null) { 
//            initialPool();
//        }
//    }

/** 同步获取Jedis实例 
 *  @return Jedis */
//public synchronized static Jedis getJedis() { 
//    if (jedisPool == null) { 
//        poolInit();
//    }
//    Jedis jedis = null;
//    try { 
//        if (jedisPool != null) { 
//            // 从池中获取一个Jedis对象
//            jedis = jedisPool.getResource();
//        }
//    } catch (Exception e) { 
//        logger.error("Get jedis error : " e);
//    }finally{
//        returnResource(jedis);
//    }
//    return jedis;
//} 
//        
}
Jedis之Dao类

描述:此处是在于数据库交付操作层进行实现的工具类,只是一部分实现功能,其他功能等遇到的时候在进行补充添加;

/Web/src/top/weiyigeek/connredis/RedisDemo3.java

代码语言:javascript复制
package top.weiyigeek.connredis;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import redis.clients.jedis.Jedis;
public class RedisDemo3 {
    /* redis过期时间,以秒为单位 */
    public final static int EXRP_HOUR = 60 * 60 ;
    public final static int EXRP_DAY = 60 * 60 * 24;
    public final static int EXRP_MONTH = 60 * 60 * 24 * 30;
    
    //常用的key类型,用于辅助不区分key类型获取key的value值(非常值得学习)
    public enum GetValue {
        LIST(){
            @Override
            public String getValue(Jedis jedis,String key) {
                List<String> list = jedis.lrange(key, 0, jedis.llen(key));
                return "key=" key "|type=list|value=" toString(list);
            }
        },
        HASH(){
            @Override
            public String getValue(Jedis jedis, String key) {
                Set<String> keys = jedis.hkeys(key);
                return "key=" key "|type=hash|value=" toString(keys);
            }
        },
        SET(){
            @Override
            public String getValue(Jedis jedis, String key) {
                Set<String> smembers = jedis.smembers(key);
                return "key=" key "|type=set|value=" toString(smembers);
            }
        },
        STRING(){
            @Override
            public String getValue(Jedis jedis, String key) {
                return "key=" key "|type=string|value=" jedis.get(key);
            }
            
        },
        ZSET(){
            @Override
            public String getValue(Jedis jedis, String key) {
                Set<String> keys = jedis.zrange(key, 0, jedis.zcard(key));
                return "key=" key "|type=zset|value=" toString(keys);
            }
        };
        
        // TODO Auto-generated method stub
        public abstract String getValue(Jedis jedis,String key);
        String toString(Collection<String> collection){
            StringBuilder sb = new StringBuilder();
            Iterator<String> iterator = collection.iterator();
            while (iterator.hasNext()){
                sb.append(iterator.next()).append(",");
            }
            return sb.substring(0,sb.length()-1);
        }
    }
    
    
    /**
    * 判定指定的key是否存在
     * @param jedis
     * @param key
     * @return
     */
    public static boolean exits(Jedis jedis, String key) {
        return jedis.exists(key);
    }
    
    /**
     *  获取key的类型
     * @param jedis
     * @param key
     * @return String
     */
    public static String type(Jedis jedis, String key) {
        return jedis.type(key);
    }
    
    /**
     * 设置key的过期时间
     * @param key
     * @param seconds
     */
    public static void expire(String key, final int seconds) {
        
    }
    
    /**
     *  删除指定的key(字符串/列表/哈希/集合/有序集合)
     * @param jedis
     * @param key
     * @return
     */
    public static boolean del(Jedis jedis, String key) {
       try {
           if (jedis.exists(key)) {
               jedis.del(key);
               return true; 
           }else {
               return false;
           }
        }catch (Exception e) {
           return false;
        }
    }

    /**
     *         设置与获取String类型的Key
     * @param jedis
     * @param key
     * @param value
     * @param seconds
     * @throws Exception 
     */
    public static void setKeyString(Jedis jedis, String key, String value, int seconds) throws Exception {
        try {
          //判断字符串最安全的方法
          value = (value == null ||value.isEmpty()) ? "":value;
          jedis.setex(key, seconds, value);
        } catch (Exception e) {
            throw new Exception("Setting Key Error....");
        }
    }
    
    public static String getKeyString(Jedis jedis, String key) {
        if (jedis == null || !jedis.exists(key)) {
            return null;
        }
        return jedis.get(key);
    } 
}

使用示例:

代码语言:javascript复制
package top.weiyigeek.connredis;
import redis.clients.jedis.Jedis;
import top.weiyigeek.utils.RedisPoolUtil;
public class RedisDemo2 {
    public static void main(String[] args) {
        //1.测试工具类
        Jedis redis1 = RedisPoolUtil.getJedis();
        System.out.println(redis1.ping());
        
        Jedis redis2 = RedisPoolUtil.getJedis();
        System.out.println(redis2.ping());
        
        //2.性能测试
        Jedis redis = null;
        int loop = 1;
        while (loop < 3) {
            try {
                long start = System.currentTimeMillis();
                redis = RedisPoolUtil.getJedis();
                redis.set("k" loop, "WeiyiGeek" loop);
                String ret = redis.get("k" loop);
                long end = System.currentTimeMillis();
                System.out.printf("Get ret from redis: %s with %d millisn", ret, end-start);
            } finally {
                if (redis != null) {
                    redis.close();
                    //RedisPoolUtil.close(redis); //已经被close方法替代
                }
            }
            loop  ;
        }
        
        
       //3.Redis通用方法测试(值得学习采用枚举的形式 抽象方法实现)
       System.out.println(RedisDemo3.GetValue.STRING.getValue(redis, "Name"));       
       System.out.println(RedisDemo3.GetValue.LIST.getValue(redis, "site-list"));
       System.out.println(RedisDemo3.GetValue.HASH.getValue(redis, "hashkey1"));
       System.out.println(RedisDemo3.GetValue.SET.getValue(redis, "setkey1"));
       System.out.println(RedisDemo3.GetValue.ZSET.getValue(redis, "Zsetkey") "n");
       
       
       //4.redis工具类测试
       //String类型(其他类型遇到的时候在写)
       long start = System.currentTimeMillis();
       try {
        RedisDemo3.setKeyString(redis, "Name", "WeiyiGeek", 360);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        String value = RedisDemo3.getKeyString(redis, "Name");
        long end = System.currentTimeMillis();
        System.out.printf("Get ret from redis: %s with %d millisn", value, end-start);
        
        RedisDemo3.expire("Name", 3600);
        System.out.println("key值是否存在 = "   RedisDemo3.exits(redis, "hashkey1"));
        System.out.println("key值类型 = " RedisDemo3.type(redis, "hashkey1"));
        System.out.println("删除指定Key值状态 = "   RedisDemo3.del(redis, "hashkey1"));
    }
}

执行结果:

代码语言:javascript复制
当前Redis连接池被使用的数量: 1
PONG
当前Redis连接池被使用的数量: 2
PONG
当前Redis连接池被使用的数量: 3
Get ret from redis: WeiyiGeek1 with 2 millis
当前Redis连接池被使用的数量: 3
Get ret from redis: WeiyiGeek2 with 11 millis
key=Name|type=string|value=WeiyiGeek
key=site-list|type=list|value=Taobao,Google,WeiyiGeek
key=hashkey1|type=hash|value=Love,Name
key=setkey1|type=set|value=Python,Java
key=Zsetkey|type=zset|value=java,redis,WeiyiGeek,eclipse,Weiyi,极客

Get ret from redis: WeiyiGeek with 6 millis
key值是否存在 = true
key值类型 = hash
删除指定Key值状态 = true
Jedis订阅与发布实现

描述:Redis通过publish和subscribe命令实现订阅和发布的功能。

  • 订阅者可以通过subscribe向redis server订阅自己感兴趣的消息类型, redis将信息类型称为通道(channel)
  • 当发布者通过publish命令向redis server发送特定类型的信息时,订阅该消息类型的全部订阅者都会收到此消息。

基础示例: Redis驱动包提供了一个抽象类JedisPubSub继承这个类就完成了对客户端对订阅的监听

/Web/src/top/weiyigeek/utils/RedisPubSubListener.java

代码语言:javascript复制
package top.weiyigeek.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.JedisPubSub;

public class RedisPubSubListener extends JedisPubSub {
    //日志打印:Slf4j-simple
    private static final Logger logger = LoggerFactory.getLogger(RedisPubSubListener.class);
    
    @Override
    public void unsubscribe() {
        super.unsubscribe();
    }
 
    @Override
    public void unsubscribe(String... channels) {
        super.unsubscribe(channels);
    }
    
 
    @Override
    public void subscribe(String... channels) {
        super.subscribe(channels);
    }
 
    @Override
    public void psubscribe(String... patterns) {
        super.psubscribe(patterns);
    }
 
    @Override
    public void punsubscribe() {
        super.punsubscribe();
    }
 
    @Override
    public void punsubscribe(String... patterns) {
        super.punsubscribe(patterns);
    }
 
    //监听到订阅频道接受到消息时的回调 (onMessage )  
    @Override
    public void onMessage(String channel, String message) {
        logger.info("onMessage: channel[{}], message[{}]",channel, message);
    }
    
    //监听到订阅模式接受到消息时的回调 (onPMessage)
    @Override
    public void onPMessage(String pattern, String channel, String message) {
        logger.info("onPMessage: pattern[{}], channel[{}], message[{}]", pattern, channel, message);
    }
    
    
    //订阅频道时的回调( onSubscribe )
    @Override
    public void onSubscribe(String channel, int subscribedChannels) {
        logger.info("onSubscribe: channel[{}], subscribedChannels[{}]", channel, subscribedChannels);
    }
    
    //取消订阅频道时的回调( onUnsubscribe )
    @Override
    public void onUnsubscribe(String channel, int subscribedChannels) {
        logger.info("channel:{} is been subscribed:{}", channel, subscribedChannels);
    }
 
 
    //订阅频道模式时的回调 ( onPSubscribe )
    @Override
    public void onPSubscribe(String pattern, int subscribedChannels) {
        logger.info("onPSubscribe: pattern[{}], subscribedChannels[{}]", pattern, subscribedChannels);
    }
    
    //取消订阅模式时的回调 ( onPUnSubscribe )    @Override
    public void onPUnsubscribe(String pattern, int subscribedChannels) {
        logger.info("onPUnsubscribe: pattern[{}], subscribedChannels[{}]", pattern, subscribedChannels);
    }
}

基础示例:订阅者

代码语言:javascript复制
package top.weiyigeek.connredis;

import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;
import top.weiyigeek.utils.RedisPoolUtil;
import top.weiyigeek.utils.RedisPubSubListener;

//subscribe订阅者
public class RedisSub {
    @Test
    public void sub() {
       System.out.println("--------订阅者------------------- "); 
       Jedis jedis = RedisPoolUtil.getJedis();
       try {
        RedisPubSubListener rpsl = new RedisPubSubListener();
        //jedis.subscribe(rpsl, "news.blog","news.share"); //方式1.订阅频道
        jedis.psubscribe(rpsl,"news.*");   //方式2.订阅频道模式采用通配符模式订阅多个频道
       } catch (Exception e) {
           e.printStackTrace();
       } finally {
           if (jedis != null)
               jedis.close();
       }
    }
}

基础示例:发布者

代码语言:javascript复制
package top.weiyigeek.connredis;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import top.weiyigeek.utils.RedisPoolUtil;
//publish 发布者
public class RedisPub {
    @Test
    public void Pub() {
        System.out.println("-------发布者------------- ");
        Jedis jedis = RedisPoolUtil.getJedis();
        try {
            jedis.publish("news.share", "WeiyiGeek-个人分享");
            Thread.sleep(2000);
            jedis.publish("news.blog", "WeiyiGeek-个人博客");
            Thread.sleep(1000);
            jedis.publish("new", "最新信息");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // TODO: handle finally clause
            if (jedis != null)
                   jedis.close();
        }
    } 
}

执行结果: 此时当在有客户端向new.share或者new.blog通道publish消息时jedis.publish(channel, message),onMessage方法即可被触发。

代码语言:javascript复制
#--------订阅者------------------- 
#当前Redis连接池被使用的数量: 0
[main] INFO top.weiyigeek.utils.RedisPubSubListener - onPSubscribe: pattern[news.*], subscribedChannels[1]
[main] INFO top.weiyigeek.utils.RedisPubSubListener - onPMessage: pattern[news.*], channel[news.blog], message[www.weiyigeek.top]
[main] INFO top.weiyigeek.utils.RedisPubSubListener - onPMessage: pattern[news.*], channel[news.share], message[WeiyiGeek-个人分享]
[main] INFO top.weiyigeek.utils.RedisPubSubListener - onPMessage: pattern[news.*], channel[news.blog], message[WeiyiGeek-个人博客]

#-------发布者------------- 
当前Redis连接池被使用的数量: 0

借鉴参考:

  • https://www.cnblogs.com/onlymate/p/9524960.html

0x04 入坑解决

0) 针对Redis实例JedisPool提示JedisPoolConfig报错问题 答:在进行测试Redis连接池的使用时候,jedisPoolConfig无补充方法且报启动错误信息org.apache.commons.pool2; 解决办法:

  • 1.Apache中下载Commons.pool2.jar包然后导入添加到工程里面;

1) 回收函数returnResource弃用说明 答:其中有个函数returnResource已经deprecated了,现在Jedis的close方法重写了,用Jedis.close来释放资源。

代码语言:javascript复制
/**
* @deprecated starting from Jedis 3.0 this method will not be exposed.
* Resource cleanup should be done using @see {@link redis.clients.jedis.Jedis#close()}
*/
@Override
@Deprecated
public void returnResource(final Jedis resource) {
    if (resource != null) {
        try {
        resource.resetState();
        returnResourceObject(resource);
        } catch (Exception e) {
        returnBrokenResource(resource);
        throw new JedisException("Could not return the resource to the pool", e);
        }
    }
}

//自Jedis3.0版本后jedisPool.returnResource()遭弃用,官方重写了Jedis的close方法用以代替进行资源回收,官方代码如下:
@Override
public void close() {
    if (dataSource != null) {
        if (client.isBroken()) {
            this.dataSource.returnBrokenResource(this);
        } else {
            this.dataSource.returnResource(this);
        }
    } else {
        client.close();
    }
}

2) 在订阅与发布进行日志输出的时候slf4j的jar包导入错误导致不支持logger.info方法 描述:slf4j-simple-1.7.9下载地址然后导包到工程之中;

代码语言:javascript复制
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

private static final Logger logger = LoggerFactory.getLogger(RedisPubSubListener.class);
 logger.info("onMessage: channel[{}], message[{}]",channel, message); //采用占位符

0x05 补充案例

SpringCloud 框架Until包:

代码语言:javascript复制
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
 
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
 
    @Value("${spring.redis.database}")
    private int index;
 
    @Value("${spring.redis.host}")
    private String host;
 
    @Value("${spring.redis.port}")
    private int port;
 
    @Value("${spring.redis.timeout}")
    private int timeout;
 
    @Value("${spring.redis.pool.max-idle}")
    private int maxIdle;
 
    @Value("${spring.redis.pool.max-wait}")
    private long maxWaitMillis;
 
    @Value("${spring.redis.password}")
    private String password;
 
    @Bean
    public JedisPool redisPoolFactory() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password,index);
        return jedisPool;
    }
}

基础示例: redis配置

代码语言:javascript复制
#redis实例
redis.info=192.168.127.128:6379,192.168.127.128:6380
#redis最大连接数
redis.pool.maxTotal=50
redis.pool.maxIdle=20
redis.pool.minIdle=10
redis.pool.maxWaitMillis=1000
代码语言:javascript复制
@Component
@Data
@PropertySource("config/properties")
public class RedisConfig {

    @Value("${redis.info}")
    private String redisInfo;

    @Value("${redis.pool.maxTotal}")
    private int maxTotal;

    @Value("${redis.pool.maxIdle}")
    private int maxIdle;

    @Value("${redis.pool.minIdle}")
    private int minIdle;

    @Value("${redis.pool.maxWaitMillis}")
    private int maxWaitMillis;
}

shardjedis采用一致hash算法实现key的分片,通过计算key的hash值将key分布到不同的redis服务器上,从而达到横向扩展的目的。 以下介绍shardjedis的常用操作。

配置shardjedispool

代码语言:javascript复制
@Configuration
@ComponentScan(basePackages = "config")
public class ShardJedisPool {
    @Autowired
    RedisConfig redisConfig;

    /**
     *获取jedisinfo信息
     * @return
     */
    private List<JedisShardInfo> jedisShardInfos(){
        String[] redisInfos = redisConfig.getRedisInfo().split(",");
        List<JedisShardInfo> jedisShardInfos = new ArrayList<>();
        for(String redisInfo :redisInfos){
            String[] part = redisInfo.split(":");
            jedisShardInfos.add(new JedisShardInfo(part[0],Integer.parseInt(part[1])));
        }
        return jedisShardInfos;
    }

    /**
     * 设置redis的参数配置
     * @return
     */
    @Bean
    public GenericObjectPoolConfig genericObjectPoolConfig(){
        GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
        genericObjectPoolConfig.setMaxIdle(redisConfig.getMaxIdle());
        genericObjectPoolConfig.setMaxTotal(redisConfig.getMaxTotal());
        genericObjectPoolConfig.setMinIdle(redisConfig.getMinIdle());
        genericObjectPoolConfig.setMaxWaitMillis(redisConfig.getMaxWaitMillis());
        return genericObjectPoolConfig;
    }

    /**
     * 获取shardjedis连接池
     * @param genericObjectPoolConfig
     * @return
     */
    @Bean
    public ShardedJedisPool shardedJedisPool(GenericObjectPoolConfig genericObjectPoolConfig){
        return new ShardedJedisPool(genericObjectPoolConfig,jedisShardInfos());
    }

常用方法:

代码语言:javascript复制
@Component
public class JedisAction {

    @Autowired
    private ShardedJedisPool shardedJedisPool;

    /**
     * 获取string类型的value
     * @param key
     * @return
     */
    public String get(String key){
        try(ShardedJedis shardedJedis = shardedJedisPool.getResource()) {
            return shardedJedis.get(key);
        }
    }

    /**
     * 设置string类型的值
     * @param key
     * @param value
     */
    public void set(String key,String value){
       try (ShardedJedis shardedJedis = shardedJedisPool.getResource()){
           shardedJedis.set(key,value);
       }
    }

    /**
     * 删除key
     * @param key
     */
    public void del(String key){
        try(ShardedJedis shardedJedis = shardedJedisPool.getResource()){
            shardedJedis.del(key);
        }
    }

    /**
     * 判断key是否存在
     * @param key
     * @return
     */
    public boolean exits(String key){
        try(ShardedJedis shardedJedis = shardedJedisPool.getResource()){
            return shardedJedis.exists(key);
        }
    }

    /**
     * 获取key的类型
     * @param key
     * @return
     */
    public String type(String key){
        try(ShardedJedis shardedJedis = shardedJedisPool.getResource()){
            return shardedJedis.type(key);
        }
    }

    /**
     * 设置key的过期时间
     * @param key
     * @param seconds
     */
    public void expire(String key,final int seconds){
        try(ShardedJedis shardedJedis = shardedJedisPool.getResource()){
            shardedJedis.expire(key,seconds);
        }
    }

    /**
     * 根据key的模糊类型获取所有的key
     * @param pattern
     * @return
     */
    public List<String> keys(String pattern){
        try(ShardedJedis shardedJedis = shardedJedisPool.getResource()){
            Collection<Jedis> allShards = shardedJedis.getAllShards();
            List<String> keyList = new ArrayList<>();
            for(Jedis jedis:allShards){
                Set<String> keys = jedis.keys(pattern);
                keyList.addAll(keys);
            }
            return keyList;
        }
    }

    /**
     * 获取所有匹配模糊key的value值
     * @param pattern
     * @return
     */
    public List<String> getValues(String pattern){
        try(ShardedJedis shardedJedis = shardedJedisPool.getResource()){
            Collection<Jedis> allShards = shardedJedis.getAllShards();
            List<String> values = new ArrayList<>();
            for(Jedis jedis:allShards){
                Set<String> keys = jedis.keys(pattern);
                for(String key:keys){
                    values.add(getValue(jedis,key));
                }
            }
            return values;
        }
    }

    /**
     * 不区分key的类型获取key的value,可以匹配string,list,hash,set四种类型
     * @param jedis
     * @param key
     * @return
     */
    public String getValue(Jedis jedis,String key){
        String type = jedis.type(key);
        GetValue value = GetValue.valueOf(type.toUpperCase());
        return value.getValue(jedis,key);
    }
}
jedisCluster连接redis(集群)

描述:

代码语言:javascript复制
@Test
public void testJedisCluster()throws Exception{
    //创建jedisCluster对象,有一个参数 nodes是Set类型,Set包含若干个HostAndPort对象
    Set<HostAndPort> nodes = new HashSet<>();
    nodes.add(new HostAndPort("192.168.241.133",7001));
    nodes.add(new HostAndPort("192.168.241.133",7002));
    nodes.add(new HostAndPort("192.168.241.133",7003));
    nodes.add(new HostAndPort("192.168.241.133",7004));
    nodes.add(new HostAndPort("192.168.241.133",7005));
    nodes.add(new HostAndPort("192.168.241.133",7006));
    JedisCluster jedisCluster = new JedisCluster(nodes);
    //使用jedisCluster操作redis
    jedisCluster.set("test", "my forst jedis");
    String str = jedisCluster.get("test");
    System.out.println(str);
    //关闭连接池
    jedisCluster.close();
}

JedisCluster设置密码
1. 修改配置文件,通过requirePass指定密码
2. 通过JedisCluster构造方法指定密码
new JedisCluster(node, connectionTimeout, soTimeout, maxAttempts, password, poolConfig)
new JedisCluster(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, password, poolConfig)

0 人点赞