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中定义了相关的属性;
#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方法即可被触发。
#--------订阅者-------------------
#当前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)