Java 操作 Redis 的库有两个,Jedis 和 Lettuce,目前 SpringBoot 2.x 中已经将 Jedis 换成了 Lettuce,让我们一起来看看这个东西。

Redis介绍

Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。相比Memcached它支持存储的类型相对更多(字符、哈希、集合、有序集合、列表、GEO),同时Redis是线程安全的。2010年3月15日起,Redis的开发工作由VMware主持,2013年5月开始,Redis的开发由Pivotal赞助。

Lettuce

Lettuce和Jedis的都是连接Redis Server的客户端程序。Jedis在实现上是直连redis server,多线程环境下非线程安全,除非使用连接池,为每个Jedis实例增加物理连接。Lettuce基于Netty的连接实例(StatefulRedisConnection),可以在多个线程间并发访问,且线程安全,满足多线程环境下的并发访问,同时它是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例

导入依赖

 

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

1、入门

@Test
    public void test1() {
        // client
        RedisClient client = RedisClient.create("redis://106.12.180.180");
        // connection, 线程安全的长连接,连接丢失时会自动重连,直到调用 close 关闭连接。
        StatefulRedisConnection<String, String> connection = client.connect();

        // sync, 默认超时时间为 60s.
        RedisCommands<String, String> redisCommands = connection.sync();
        redisCommands.set("name", "老五");
        String value = redisCommands.get("name");
        System.out.println("姓名:"+value);

        // close connection
        connection.close();
        // shutdown
        client.shutdown();
    }

2、集群模式

@Test
    public void test2() {
        //创建RedisURI的两种方式
        RedisURI redisURI1 = RedisURI.create("redis://106.12.180.180:6380");
        RedisURI redisURI2 = RedisURI.Builder.redis("106.12.180.180").withPort(6381).withDatabase(0).build();
        List<RedisURI> list = Arrays.asList( redisURI1,redisURI2);

        RedisClusterClient client = RedisClusterClient.create(list);
        //超时时间
        client.setDefaultTimeout(Duration.ofSeconds(20));

        StatefulRedisClusterConnection<String, String> connect = client.connect();

        /* 同步执行的命令 */
        RedisAdvancedClusterCommands<String, String> commands = connect.sync();
        commands.set("name", "老五");
        String value = commands.get("name");
        System.out.println("姓名:" + value);

        /*  异步执行的命令  */
//      RedisAdvancedClusterAsyncCommands<String, String> commands= connect.async();
//      RedisFuture<String> future = commands.get("test2");
//      try {
//          String str = future.get();
//          System.out.println(str);
//      } catch (InterruptedException e) {
//          e.printStackTrace();
//      } catch (ExecutionException e) {
//          e.printStackTrace();
//      }

        connect.close();
        client.shutdown();
    }

 

3、异步使用

异步调用,可以避免将 CPU 浪费在等待网络 IO 和磁盘 IO 时上,实现提高资源使用率。

基本例子

@Test
    public void test3() throws Exception {
        // client
        RedisClient client = RedisClient.create("redis://106.12.180.184:6380");
        StatefulRedisConnection<String, String> connection = client.connect();

        // async
        RedisStringAsyncCommands<String, String> asyncCommands = connection.async();

        asyncCommands.set("name", "老五");
        RedisFuture<String> future = asyncCommands.get("name");
        future.thenAccept((str) -> {
            System.out.println("姓名:" + str);
        });

        System.out.println("END");
        Thread.sleep(10*1000);
        connection.close();
        client.shutdown();
    }

 

END
姓名:老五

4、Reactive 调用

@Test
    public void test4() throws Exception {

        // client
        RedisClient client = RedisClient.create("redis://106.12.180.184:6380");

        // connect
        StatefulRedisConnection<String, String> connection = client.connect();

        // reactive
        RedisStringReactiveCommands<String, String> reactiveCommands = connection.reactive();
        reactiveCommands.set("name", "老五");
        reactiveCommands.get("name").subscribe((str) -> {
            System.out.println("姓名:" + str);
        });

        System.out.println("END");
        Thread.sleep(10 * 1000);
        connection.close();
        client.shutdown();
    }
END
姓名:老五

5、Pub/Sub

@Test
    public void test5() throws Exception {

        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        //发布Pub
        threadPool.submit(() -> {
            RedisClient client = RedisClient.create("redis://106.12.180.184:6380");
            StatefulRedisPubSubConnection<String, String> pubSubConnection = client.connectPubSub();
            RedisPubSubCommands<String, String> pubSubCommands = pubSubConnection.sync();
            while (true) {
                pubSubCommands.publish("CCTV1", "新文联播" + System.currentTimeMillis());
                pubSubCommands.publish("CBA", "篮球" + System.currentTimeMillis());
                pubSubCommands.publish("BCC", "动物世界" + System.currentTimeMillis());
                Thread.sleep(2000);
            }
        });

        //订阅Sub
        threadPool.submit(() -> {
            RedisClient client = RedisClient.create("redis://106.12.180.184:6380");
            StatefulRedisPubSubConnection<String, String> pubSubConnection = client.connectPubSub();
            pubSubConnection.addListener(new MyRedisPubSubListener());
            RedisPubSubCommands<String, String> pubSubCommands = pubSubConnection.sync();
            pubSubCommands.subscribe( "BCC");
            pubSubCommands.psubscribe("CCTV*");
        });
        
       while (true){
           Thread.sleep(1000);
       }
    }


class MyRedisPubSubListener implements RedisPubSubListener<String, String> {

    //===============普通频道====================
    @Override
    public void subscribed(String channel, long count) {
        System.out.println(toJson(new HashMap() {{
            put("methodName", "订阅频道");
            put("channel", channel);
            put("count", count);
        }}));
    }

    @Override
    public void message(String channel, String message) {
        System.out.println(toJson(new HashMap() {{
            put("methodName", "从频道订阅中接收消息");
            put("channel", channel);
            put("message", message);
        }}));
    }

    @Override
    public void unsubscribed(String channel, long count) {
        System.out.println(toJson(new HashMap() {{
            put("methodName", "取消订阅频道");
            put("channel", channel);
            put("count", count);
        }}));
    }

    //================给定模式频道===================
    @Override
    public void psubscribed(String pattern, long count) {
        System.out.println(toJson(new HashMap() {{
            put("methodName", "订阅给定模式频道");
            put("pattern", pattern);
            put("count", count);
        }}));
    }

    @Override
    public void message(String pattern, String channel, String message) {
        System.out.println(toJson(new HashMap() {{
            put("methodName", "从给定模式频道订阅中接收消息");
            put("pattern", pattern);
            put("channel", channel);
            put("message", message);
        }}));
    }

    @Override
    public void punsubscribed(String pattern, long count) {
        System.out.println(toJson(new HashMap() {{
            put("methodName", "取消订阅给定模式频道");
            put("pattern", pattern);
            put("count", count);
        }}));
    }

    private String toJson(Object obj) {
        return JSONObject.toJSONString(obj);
    }
}
{"channel":"BCC","count":1,"methodName":"订阅频道"}
{"pattern":"CCTV*","count":2,"methodName":"订阅给定模式频道"}
{"channel":"BCC","methodName":"从频道订阅中接收消息","message":"动物世界1577021706587"}
{"pattern":"CCTV*","channel":"CCTV1","methodName":"从给定模式频道订阅中接收消息","message":"新文联播1577021708616"}
……