Redis客户端组件——Lettuce

小龙 1,013 2019-11-21

Redis是一个分布式的缓存数据库,对于Redis数据库来讲,如果想让其正确的实现缓存处理,那么一定要有相应的程序与之匹配。最为常用的Redis客户端组件有两个

Jedis

jedis是一个老牌的Redis访问客户端,在使用jedis的 时候,所有的访问操作都是采用直连的形式进行处理,既:只要进行了访问,那么就立即将访问请求直连到Redis服务器上,由Redis服务器进行资源的分配,同时实现相应的处理操作。但其在Redis5.x版本之后没能提供良好的Stream(Redis5.x的新特性)支持,同时也无法实现安全的连接共享操作。

Lettuce

Lettuce是一个线程安全的Redis客户端,提供同步与异步处理两中操作方案,可以避免阻塞操作带来的性能浪费,同时多个项目可以安全的实现连接共享机制。

本次学习的主要就是Lettuce,需要引入Lettuce的依赖
gradle

compile group: 'io.lettuce', name: 'lettuce-core', version: '5.2.1.RELEASE'

maven

<dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
    <version>5.2.1.RELEASE</version>
</dependency>

连接Redis数据库

在Lettuce组件之中为了方便进行数据库的连接操作,实际上提供有三种的操作模式:

  1. 直接通过程序的方式进行各种数据的配置
  2. 进行访问路径的配置构建
  3. 直接基于字符串进行访问

不管使用的是那种连接的处理模式,都会使用到一个连接的核心类“io.lettuce.RedisURI”,该类不负责进行连接,只负责定义连接的相关信息(主机地址、端口等内容),可以直接使用该类的创建方法进行实例构建:

创建连接:public static RedisURI create(String host,int port);

当获取到了RedisURI的对象实例之后,就可以通过“io.lettuce.core.RedisClient”类实现客户端的连接配置,这个类有两个关键的方法:

  1. 构建RedisClient:public static RedisClient create(RedisURI,redisURI);
  2. 获取数据库连接:public StatefulRedisConnection<String,String> connect();

利用RedisURi中的create()方法构建一个连接的路径,随后通过RedisClient进行客户端对象的构建,利用客户端的对象可以创建 若干个redis数据库连接。

package cn.rsthe.lettuce.connecct.connecct;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
public class RedisServerConnectionDemoA {
    /** Redis数据库连接地址*/
    private static final String REDIS_HOST = "192.168.2.106";
    /** Redis数据库连接端口*/
    private static final int REDIS_PORT = 6379;
    /** Redis数据库认证信息 */
    private static final String REDIS_AUTH = "hellolee";
    /** Redis数据库索引 */
    private static final int REDIS_DATABASE_INDEX = 0;
    public static void main(String[] args) {
        //定义连接地址
        RedisURI redisURI = RedisURI.create(REDIS_HOST,REDIS_PORT);
        //连接的数据库
        redisURI.setDatabase(REDIS_DATABASE_INDEX);
        //认证信息
        redisURI.setPassword(REDIS_AUTH);
        //创建RedisClient实例
        RedisClient redisClient = RedisClient.create(redisURI);
        //创建连接
        StatefulRedisConnection<String,String> connection = redisClient.connect();
        System.out.println("【连接实例】"+connection);
        //关闭连接
        connection.close();
        //关闭Redis客户端
        redisClient.shutdown();
    }
}

结果:
redisdemoa.png
当前的数据库处理操作实际上和传统的JDBC配置形式是非常接近的,利用各种配置信息创建各种的地址个连接对象,最终实现相关的数据库处理操作。
除了可以使用RedisURI进行手工构建之外,也可以直接通过RedisURI.Builder进行连接的构建处理。
在RedisURI类之中提供有一个Builder构建内部类,在这个类中提供有一些操作方法,可以用于进行RedisURI的创建:

  1. 获取RedisURL.Builder实例:public static RedisUrl.Builder redis(String host);
  2. 获取RedisURL.Builder实例:public static RedisUrl.Builder redis(String host,int port);
  3. 配置认证信息:public RedisURI.Builder withPassWord(String password);
  4. 配置端口:public RedisURI.Builder withPort(int port);
  5. 配置数据库:public RedisURI.Builder withDataBase(int dataBase);
  6. 配置延迟时间:public RedisURI.Builder withTimeout(Duration timeout);
  7. 创建RedisURI实例:public RedisURI build();
package cn.rsthe.lettuce.connecct.connecct;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
public class RedisServerConnectionDemoB {
    /** Redis数据库连接地址*/
    private static final String REDIS_HOST = "192.168.2.106";
    /** Redis数据库连接端口*/
    private static final int REDIS_PORT = 6379;
    /** Redis数据库认证信息 */
    private static final String REDIS_AUTH = "hellolee";
    /** Redis数据库索引 */
    private static final int REDIS_DATABASE_INDEX = 0;
    public static void main(String[] args) {
        //定义连接地址
        RedisURI redisURI = RedisURI.Builder.redis(REDIS_HOST).withPort(REDIS_PORT)
                .withPassword(REDIS_AUTH).withDatabase(REDIS_DATABASE_INDEX).build();
        RedisClient redisClient = RedisClient.create(redisURI);
        StatefulRedisConnection connection = redisClient.connect();
        System.out.println("【连接实例】"+connection);
        //关闭连接
        connection.close();
        //关闭Redis客户端
        redisClient.shutdown();
    }
}

结果:
redisdemob.png
前面的两种创建连接的方案都属于传统的的程序连接构建,为了充分发挥出字符串操作的优势,所有在RedisURI类提供一个可以使用字符串进行RedisURI实例创建的方法:“public static RedisURI create(String uri);”

定义连接信息:“redis://认证信息@连接地址:端口/数据库”

package cn.rsthe.lettuce.connecct.connecct;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
public class RedisServerConnectionDemoC {
    /** Redis数据库连接地址 */
    private static final String REDIS_ADDRESS = "redis://hellolee@192.168.2.106:6379/0";
    public static void main(String[] args) {
        //定义连接地址
        RedisURI redisURI = RedisURI.create(REDIS_ADDRESS);
        RedisClient redisClient = RedisClient.create(redisURI);
        StatefulRedisConnection connection = redisClient.connect();
        System.out.println("【连接实例】"+connection);
        //关闭连接
        connection.close();
        //关闭Redis客户端
        redisClient.shutdown();
    }
}

结果:
redisdemoc.png

Lettuce的基本操作

当获取了Redis的连接之后,最为重要的目的是要进行Redis数据库的操作,而使用Lettuce组件有一个最大的特点在于,其可以创建同步操作也可以创建异步操作。如果要进行数据库的操作则一定要创建Redis的命令对象实例,在Redis里面有两类命令处理对象,都是通过“io.lettuce.core.api.StatefulRedisConnection”接口创建的,该接口有几个重要的方法:

  1. 同步命令:public RedisCommands<K,V> sync();
  2. 异步命令:public RedisAsyncCommands<K,V> async();
  3. 创建响应命令:public RedisReactiveCommands<K,V> reactive();

一、创建一个Redis数据库连接工具类

package cn.rsthe.lettuce.util;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
public class RedisConnectionUtil {
    /**Redis数据库连接地址*/
    private static final String REDIS_ADDRESS = "redis://hellolee@192.168.2.106:6379/0";
    private static final RedisURI REDIS_URI = RedisURI.create(REDIS_ADDRESS);
    private static final RedisClient REDIS_CLIENT = RedisClient.create(REDIS_URI);
    private static final ThreadLocal<StatefulRedisConnection> REDIS_CONNECTION_THREAD_LOCAL = new ThreadLocal<>();
    public static StatefulRedisConnection getConnection(){
        StatefulRedisConnection<String,String> connection = REDIS_CLIENT.connect();
        if (connection == null){
            connection = build();
            REDIS_CONNECTION_THREAD_LOCAL.set(connection);
        }
        return connection;
    }
    public static void close(){
        StatefulRedisConnection connection = REDIS_CONNECTION_THREAD_LOCAL.get();
        if (connection != null){
            connection.close();
            REDIS_CONNECTION_THREAD_LOCAL.remove();
        }
    }
    private static StatefulRedisConnection build(){
        StatefulRedisConnection<String ,String > connection = REDIS_CLIENT.connect();
        return connection;
    }
}

二、(1)创建同步操作命令

package cn.rsthe.lettuce.base;
import cn.rsthe.lettuce.util.RedisConnectionUtil;
import io.lettuce.core.api.sync.RedisCommands;
public class SyncCommandDemo {
    public static void main(String[] args) {
        //创建一个同步操作命令
        RedisCommands commands = RedisConnectionUtil.getConnection().sync();
        //向Redis之中保存数据
        commands.set("lee","HelloRedis");
        System.out.println("【获取指定Key的数据】"+commands.get("lee"));
        //关闭连接
        RedisConnectionUtil.close();

    }
}

执行结果:
tongbu.png
二、(2)创建异步操作命令

package cn.rsthe.lettuce.base;
import cn.rsthe.lettuce.util.RedisConnectionUtil;
import io.lettuce.core.RedisFuture;
import io.lettuce.core.api.async.RedisAsyncCommands;
import java.util.concurrent.TimeUnit;
public class ASyncCommandDemo {
    public static void main(String[] args) throws Exception {
        //创建一个同步操作命令
        RedisAsyncCommands commands = RedisConnectionUtil.getConnection().async();
        //向Redis之中保存数据
        commands.setex("lee",3,"HelloRedis");
        //延迟一秒
        TimeUnit.SECONDS.sleep(1);
        RedisFuture future1 = commands.get("lee");
        System.out.println("【延迟一秒获取数据】"+future1.get());
        //延迟三秒
        TimeUnit.SECONDS.sleep(3);
        RedisFuture future3 = commands.get("lee");
        System.out.println("【延迟一秒获取数据】"+future3.get());
        //关闭连接
        RedisConnectionUtil.close();

    }
}

执行结果:
yibu.png
其余的操作命令与Redis服务器上的操作都是一致的。
查询所有的Key

public class ASyncCommandDemo {
    public static void main(String[] args) throws Exception {
        //创建一个同步操作命令
        RedisAsyncCommands commands = RedisConnectionUtil.getConnection().async();
        commands.set("lee","HelloRedis");
        System.out.println("【所有的key】"+commands.keys("*").get());
        //关闭连接
        RedisConnectionUtil.close();
    }
}

flushdb、flushall操作

public class ASyncCommandDemo {
    public static void main(String[] args) throws Exception {
        //创建一个同步操作命令
        RedisAsyncCommands commands = RedisConnectionUtil.getConnection().async();
        //清空当前所在数据库
        commands.flushdb();
        //清空整个Redis数据库
        commands.flushall();
	System.out.println("【所有的key】"+commands.keys("*").get());
        //关闭连接
        RedisConnectionUtil.close();
    }
}

操作Hash数据

public class AsyncHashCommandDemo {
    public static void main(String[] args) throws Exception {
        //创建一个同步操作命令
        RedisAsyncCommands commands = RedisConnectionUtil.getConnection().async();
        //向Redis之中保存数据
        commands.hset("hello-shu","name","ShuXiaoL").get();
        Map<String,String> map = new HashMap<>(3);
        map.put("age",String.valueOf(18));
        map.put("salary",String.valueOf(999.9));
        commands.hmset("hello-shu",map).get();
        System.out.println("【获取Hash数据 —— 一个Key】"+commands.hget("hello-shu","name").get());
        Map<String,String > getMap = new HashMap<>(3);
        getMap.put("age","age");
        getMap.put("salary","salary");
        System.out.println("【获取Hash数据 —— 多个Key】"+commands.hmget("hello-shu","name","age","salary").get());
        System.out.println("【获取Hash数据 —— 设置重复数据】"+commands.hsetnx("hello-shu","salary",String.valueOf(1999.9)).get());
        System.out.println("【获取Hash数据 —— 删除数据】"+commands.hdel("hello-shu","name").get());
        System.out.println("【获取Hash数据 —— “name”删除后】"+commands.hget("hello-shu","name").get());
        System.out.println("【获取Hash数据 —— 获取所有数据(Value)】"+commands.hvals("hello-shu").get());
        System.out.println("【获取Hash数据 —— 获取所有数据(Key=Value)】"+commands.hgetall("hello-shu").get());
        //关闭连接
        RedisConnectionUtil.close();
    }
}

执行结果:
hash.png

操作list数据

public class AsyncListCommandDemo {
    public static void main(String[] args) throws Exception {
        //创建一个同步操作命令
        RedisAsyncCommands commands = RedisConnectionUtil.getConnection().async();
        //向队列左边添加数据
        System.out.println("【获取List数据 —— 左边保存后个数】" + commands.lpush("queue", "hello-a", "hello-b", "hello-c", "hello-d").get());
        //向队列右边添加数据
        System.out.println("【获取List数据 —— 右边保存后的个数】" + commands.rpush("queue", "word_1", "word_2", "word_3", "word_4").get());
        System.out.println("【获取List数据 —— 内容】" + commands.lrange("queue", 0, -1).get());
        System.out.println("【获取List数据 —— 长度】" + commands.llen("queue").get());
        long size = (long) commands.llen("queue").get();
        for (long x = 0; x < size; x++) {
            System.out.println("【List数据左边弹出】" + commands.lpop("queue").get());
            //System.out.println("【List数据右边弹出】" + commands.rpop("queue").get());
        }
        //关闭连接
        RedisConnectionUtil.close();
    }
}

执行结果:
list.png

操作Set数据

public class AsyncSetCommandDemo {
    public static void main(String[] args) throws Exception {
        //创建一个同步操作命令
        RedisAsyncCommands commands = RedisConnectionUtil.getConnection().async();
        System.out.println("【获取Set数据 —— A集合保存后个数】" + commands.sadd("set-a","a","b","c","d","e","f","g","a","f").get());
        System.out.println("【获取Set数据 —— B集合保存后个数】" + commands.sadd("set-b","1","t","1","a","c","d","e","k","2").get());
        System.out.println("【获取Set数据 —— 查看A集合数据】" + commands.smembers("set-a").get());
        System.out.println("【获取Set数据 —— 查看B集合数据】" + commands.smembers("set-b").get());
        System.out.println("【获取Set数据 —— 差集运算】" + commands.sdiff("set-a","set-b").get());
        System.out.println("【获取Set数据 —— 交集运算】" + commands.sinter("set-a","set-b").get());
        System.out.println("【获取Set数据 —— 并集运算】" + commands.sunion("set-a","set-b").get());
        //关闭连接
        RedisConnectionUtil.close();
    }
}

执行结果:
set.png

操作ZSet数据

public class AsyncZSetCommandDemo {
    public static void main(String[] args) throws Exception {
        //创建一个同步操作命令
        RedisAsyncCommands commands = RedisConnectionUtil.getConnection().async();
        commands.zadd("hotword",12.0,"HelloWorld");
        commands.zadd("hotword",22.0,"Java");
        commands.zadd("hotword",1.0,"C");
        commands.zadd("hotword",2.0,"C++");
        System.out.println("【获取ZSet数据 —— 升序排列】" + commands.zrange("hotword",0,-1).get());
        System.out.println("【获取ZSet数据 —— 降序排列】" + commands.zrevrange("hotword",0,-1).get());
        System.out.println("=======================================================================");
        System.out.println("【获取ZSet数据 —— 评分成绩降序排列】" + commands.zrangeWithScores("hotword",0,-1).get());
        System.out.println("【获取ZSet数据 —— 评分成绩升序排列】" + commands.zrevrangeWithScores("hotword",0,-1).get());
        //关闭连接
        RedisConnectionUtil.close();
    }
}

执行结果:
zset.png
操作GEO数据

public class AsyncGEOCommandDemo {
    public static void main(String[] args) throws Exception {
        //创建一个同步操作命令
        RedisAsyncCommands commands = RedisConnectionUtil.getConnection().async();
        commands.geoadd("me",125.5847949057,34.9957012967,"mywork");
        commands.geoadd("me",125.5484640337,34.9326152290,"myhome");
        commands.geoadd("me",124.9216173050,34.9799318876,"mylove");
        System.out.println("【获取GEO数据】" + commands.geopos("me","mywork","myhome","mylove").get());
        System.out.println("【获取GEO数据 —— 工作到家的距离】" + commands.geodist("me","myhome","mywork", GeoArgs.Unit.km).get()+" Km");
        //关闭连接
        RedisConnectionUtil.close();
    }
}

执行结果:
GEO.png


# redis # Lettuce