一:简介
在浏览本篇文章时务必要会Redis的基本命令,因为在一些主流语言中使用一些其它Redis客户端时都是基于基本命令来操作的;比如在java项目中使用连接Redis客户端有Jedis、Redisson、Jredis、JDBC-Redis;不过官方推荐我们使用Jedis和Redisson;本文章中我会使用Jedis和SpringDataRedis来操作远程的Redis服务端。
Jedis封装了Redis的一些命令操作,它是Redis的Java客户端,和我们在Redis自带的客户端输入的命令一样,Jedis也提供了连接池管理,可以不用每次关闭连接Redis而消耗时间。
SpringDataRedis是Spring大家族的一部分,提供了在Spring应用中通过简单的配置访问Redis服务,对Redis底层开发包(Jedis, JRedis, and RJC)进行了高度封装,RedisTemplate提供了redis各种操作、异常处理及序列化,支持发布订阅等,不过在高版本中SpringDataRedis底层则不在使用Jedis。(后续介绍SpringDataRedis)
1:务必看看
注:本篇文章的命令都在我之前的 【Redis命令】 一文中都详细说明了,我也是参照那篇文章转换为Java程序来实现;遇到不会的命令只须参照【Redis命令】一文 Ctrl+F 直接搜索那个命令即可;而且使用Jedis只要找到你需要的方法,方法参数和官方命令顺序都是一样的,而且简单易上手,主要是知道某个命令干啥的就行;还有就是我的文章只是参考,具体的大家可以参考官方文档!
二:Jedis继承关系分析
1:Jedis类
Jedis提供了Redis客户端的连接和命令查询,从Jedis继承关系中发现它实现很多的命令接口,每个接口都定义了不同的操作形式,这符合面向对象开发原则中的接口隔离原则和单一职责原则。下面的接口声明了相关的redis命令操作,每个接口都负责对一部分的命令进行方法声明
Jedis父类BinaryJedis所依赖的接口: BasicCommands: 提供基础的查询命令,如ping,quit,flushdb BinaryJedisCommands: 提供了针对redis数据结构的CURD等操作,其中参数(K-V)必须以byte数组形式提供 MultiKeyBinaryCommands: 提供了针对redis数据结构的CURD等批量操作,其中参数(K-V)必须以byte数组形式提供 AdvancedBinaryJedisCommands: 提供高级操作redis的命令,如config相关,slowlog,client等命令,其中参数(K-V)必须以byte数组形式提供 BinaryScriptingCommands: 提供Lua脚本运行命令,命令必须以byte数组形式提供。 Jedis所依赖的接口: JedisCommands: 提供了针对redis数据结构的CURD等操作,其中参数(K-V)必须以String形式提供 MultiKeyCommands: 提供了针对redis数据结构的CURD等批量操作,其中参数(K-V)必须以String数组形式提供 AdvancedJedisCommands: 提供高级操作redis的命令,如config相关,slowlog,client等命令,其中参数(K-V)必须以String形式提供 ScriptingCommands: 提供Lua脚本运行命令,命令必须以String形式提供。 BasicCommands: 提供如ping,quit,flushDb运维类命令 ClusterCommands: 提供集群状态查看,集群操作的命令 SentinelCommands: 提供哨兵操作命令 ModuleCommands: 提供redis模块加载和卸载命令
2:Client类
除了方法接口声明之外,Jedis还提供了客户端Client,使用该类仍然可以连接Redis服务端并进行相关的命令操作。Client本身只是提供相关的命令方法,这些方法声明定义在Commands接口,连接操作则定义在Connection类,也就是说Client类类似于一个前台,可以提供各种服务,而具体的实现则依赖于Connection和Commands,Client提供方法与Jedis基本一致,不同的是Client类里获取命令是没有返回值的,所有我们一般不用Client,都是使用Jedis,不过Jedis底层必须有Client的存在
Client所依赖的接口: Commands: 内部都是Redis基本命令的封装 Client所继承的类: BinaryClient: 用来最终和Redis服务端交互的客户端,以二进制交互,内部继承Connection类
3:Jedis如何发送命令到Redis服务端
Jedis是通过Socket对Redis服务端进行连接的,而Jedis和Client本身并没有Socket连接方法的实现,相关的连接方法都在Connection类中,观察继承关系,Connection类是其顶层的实现类。Jedis或者Client发送命令时,必须通过Connection类的connect()方法建立TCP连接。
在使用Connection类时,需提供相关的参数进行实例化,从构造方法可以看到,需要提供主机名,如果不设置,则使用默认名localhost.连接端口,不设置则使用默认端口6379,连接超时时长(如果不进行设置,则默认与socketTimeout保持一致),Socket读取超时时长(如果不进行设置,则使用默认时长2000ms),SSL加密传输则是可选的。在连接时必须判断当前对象的连接状态,如果符合条件则新建Socket实例。
Jedis和Client类通过Connection对象的sendCommand()方法进行命令传输,在传输命令时必须将字符串转为字节数组,如果类似于ping,ask这样的命令,只需要将命令转为字节即可。转换完成之后写入到输出流中,将字节流发送给Redis 服务端。
三:Jedis操作使用
1:基本案例
基本案例中我使用maven的一个空项目(maven骨架:maven-archetype-quickstart)来进行简单的操作;
<!--导入Jedis坐标,用来操作Redis的,主要就这个包--> <dependencies> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.7.0</version> </dependency> <!--导入fastjson,用于对象的转换JSON格式--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.78</version> </dependency> </dependencies>
public static void main(String[] args){ //创建一个连接 Jedis jedis = new Jedis("localhost", 6379); //ping一下Redis服务端是否在线,成功则返回 “PONG” 反之报错超时 String ping = jedis.ping(); //Jedis它实现了各式各样接口,最终汇聚一个类Jedis,它内部封装了全部Redis命令 //比如下面的”String类型命令set“保存两条数据,保存成功则返回ok jedis.set("name","zhangsan"); jedis.set("age","22"); //在redis中获取name值 String name = jedis.get("name"); System.out.println(name); jedis.close();//关闭连接 //也可以直接使用Client,但是我们还是使用Jedis吧,让Jedis底层调用Client吧,我们直接使用是没有返回值的 Client client = new Client("localhost", 6379); client.set("name","lisi"); client.close(); }
2:Jedis连接池
#最大活动对象数 redis.pool.maxTotal=1000 #最大能够保持idel状态的对象数 redis.pool.maxIdle=100 #最小能够保持idel状态的对象数 redis.pool.minIdle=50 #当池内没有返回对象时,最大等待时间 redis.pool.maxWaitMillis=10000 #当调用borrow Object方法时,是否进行有效性检查 redis.pool.testOnBorrow=true #当调用return Object方法时,是否进行有效性检查 redis.pool.testOnReturn=true #“空闲链接”检测线程,检测的周期,毫秒数。如果为负值,表示不运行“检测线程”。默认为-1 redis.pool.timeBetweenEvictionRunsMillis=30000 #向调用者输出“链接”对象时,是否检测它的空闲超时 redis.pool.testWhileIdle=true # 对于“空闲链接”检测线程而言,每次检测的链接资源的个数。默认为3 redis.pool.numTestsPerEvictionRun=50 #redis服务器的IP redis.ip=localhost #redis服务器的Port redis.port=6379 #连接Redis超时时间秒 redis.timeout=2000 #连接Redis的password,不写则没有密码 redis.password=jedisPool.properties
import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import java.io.IOException; import java.io.InputStream; import java.time.Duration; import java.util.Properties; /** * @Author AnHui_XiaoYang * @Email 939209948@qq.com * @Date 2021/10/17 20:44 * @Description Jedis连接池工具 */ public class JedisPoolUtils { private static JedisPool jedisPool; static { //类加载时,读取配置文件 InputStream in = JedisPoolUtils.class.getClassLoader().getResourceAsStream("jedisPool.properties"); //创建Properties对象并关联对象 Properties pro = new Properties(); try { pro.load(in); } catch (IOException e) { e.printStackTrace(); } //创建连接池配置并设置值 JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(Integer.parseInt(pro.getProperty("redis.pool.maxTotal"))); config.setMaxIdle(Integer.parseInt(pro.getProperty("redis.pool.maxIdle"))); config.setMinIdle(Integer.parseInt(pro.getProperty("redis.pool.minIdle"))); config.setMaxWaitMillis(Long.parseLong(pro.getProperty("redis.pool.maxWaitMillis"))); config.setTestOnBorrow(Boolean.parseBoolean(pro.getProperty("redis.pool.testOnBorrow"))); config.setTestOnReturn(Boolean.parseBoolean(pro.getProperty("redis.pool.testOnReturn"))); Duration duration = Duration.ofMinutes(Long.parseLong(pro.getProperty("redis.pool.timeBetweenEvictionRunsMillis"))); config.setTimeBetweenEvictionRuns(duration); config.setTestWhileIdle(Boolean.parseBoolean(pro.getProperty("redis.pool.testWhileIdle"))); config.setNumTestsPerEvictionRun(Integer.parseInt(pro.getProperty("redis.pool.numTestsPerEvictionRun"))); String host = pro.getProperty("redis.ip"); Integer port = Integer.valueOf(pro.getProperty("redis.port")); Integer timeout = Integer.valueOf(pro.getProperty("redis.timeout")); String password = pro.getProperty("redis.password"); if (password == null || "".equals(password)) jedisPool = new JedisPool(config, host, port, timeout); else jedisPool = new JedisPool(config, host, port, timeout, password); } /** * @return 返回一个JedisClient */ public static Jedis getJedisClient() { return jedisPool.getResource(); } }JedisPoolUtil连接工具类
public static void main(String[] args) { //平时使用连接池对象即可,每次都重新创建获取会消耗大量时间 Jedis jedis = JedisPoolUtils.getJedisClient(); jedis.set("name","zhangsan"); String s = jedis.get("name"); System.out.println(s); jedis.close();//不用时直接close即可 }
总结:现在在项目中一般不会单独引用Jedis了,通常会以SpringBoot集成Redis,通过RedisTemplate模板来操作Redis;具体使用请参考 SpringDataRedis入门到深入