我是靠谱客的博主 优美太阳,这篇文章主要介绍「进击Redis」二十、万字长文解析Redis 高级客户端Lettuce,现在分享给大家,希望可以做个参考。

前言

不知不觉已经是 Redis 系列的第二十篇了,也是开始写博客的第二十二天,现在的文章数量二十五篇,因为 Redis 的前几篇还是篇基础的,所以大概的话也就是每天一篇的样子。一直坚持写是真的挺难的,不过收获还是挺多的,如果有好哥哥现在遇到技术瓶颈了,不妨试试写博客出来分享。我保证能颠覆你对某个技术的认知。昨天有个同学好哥哥问我真的值得吗,我很肯定的回答是值得。因为这里面的收获只有我自己知道,哪怕没人看,那又怎样,我搞定了我自己就 OK(希望不要被打)。
今天的话还是和上一篇Redis 客户端 Jedis 详解 一样,讲的是另外一个 Redis 的高级客户端,是真的高级。为什么要讲两个客户端呢,实际上跟下篇的内容有关系,弄懂了这两个客户端下篇就很简单了。
太难了

概述

Lettuce是一个 Redis 的Java驱动包,Lettuce翻译为生菜,没错,就是吃的那种生菜,所以它的 Logo 是这样的
在这里插入图片描述
Lettuce是一个高性能基于Java编写可伸缩线程安全的 Redis 客户端,底层集成了Project Reactor提供天然的反应式编程,通信框架集成了Netty(不熟悉的好哥哥先忍忍,有时间弄这个的源码解析)使用了非阻塞 IO,提供同步, 异步和反应式 API。如果多个线程避免阻塞和事务操作(例如BLPOPMULTI),则可以共享一个连接EXEC。出色的netty NIO框架可有效管理多个连接。包括对高级 Redis 功能的支持,例如SentinelCluster和 Redis 数据模型。
5.x版本之后融合了JDK1.8的异步编程特性,在保证高性能的同时提供了十分丰富易用的 API。5.1版本提供了很多新的特性,如下:

  1. 支持 Redis 的新增命令ZPOPMINZPOPMAXBZPOPMINBZPOPMAX
  2. 支持通过Brave模块跟踪 Redis 命令执行。
  3. 支持Redis Streams
  4. 支持异步的主从连接。
  5. 支持异步连接池。
  6. 新增命令最多执行一次模式(禁止自动重连)。
  7. 全局命令超时设置(对异步和反应式命令也有效)。
  8. …等等

使用

1 maven 依赖

至于版本的会好哥哥们自行选择,我这个目前是最新版本

复制代码
1
2
3
4
5
6
<dependency> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> <version>6.0.1.RELEASE</version> </dependency>

2 连接 Redis

2.1 使用 URI
复制代码
1
2
3
4
public static void main(String[] args) { RedisURI redisURI = RedisURI.create("redis://localhost/"); }
2.2 使用构造器
复制代码
1
2
3
4
5
6
public static void main(String[] args) { // 需要注意的是不同版本的包对应的API也可能不一样 RedisURI redisURI = RedisURI.Builder.redis("127.0.0.1", 6379).withDatabase(0).build(); RedisClient client = RedisClient.create(redisURI); }
2.3 使用构造函数
复制代码
1
2
3
4
5
6
public static void main(String[] args) { // 需要注意的是不同版本的包对应的API也可能不一样 RedisURI redisURI = new RedisURI("localhost", 6379, Duration.ofSeconds(60)); RedisClient client = RedisClient.create(redisURI); }

3 基础使用

3.1 获取 key
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) { RedisURI redisURI = RedisURI.Builder.redis("127.0.0.1", 6379).withDatabase(0).build(); // 1. 创建连接 RedisClient redisClient = RedisClient.create(redisURI); // 2. 打开Redis连接 StatefulRedisConnection<String, String> connection = redisClient.connect(); // 3. 获取用于同步执行的命令API RedisCommands<String, String> redisCommands = connection.sync(); redisCommands.get("hello"); // 4. 关闭连接 connection.close(); // 5. 关闭客户端 redisClient.shutdown(); }
3.2 三种模式 API

上面有提到Lettuce支持同步(sync)、异步(async)、反应式(reactive),对应的 API 是RedisCommandsRedisAsyncCommandsRedisReactiveCommands
在使用其操作其他数据结构 API 之前,先把上面代码进行一下优化(将连接抽离成一个方法),不然会有很多重复代码,篇幅会过长。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/** * 创建成功的URI */ private static RedisClient redisClient = null; /** * 同步执行的命令API */ private static StatefulRedisConnection<String, String> connection = null; /** * 初始化操作 * * @return */ public static void getConnection() { RedisURI redisURI = RedisURI.Builder.redis("127.0.0.1", 6379).withDatabase(0).build(); // 1. 创建连接 redisClient = RedisClient.create(redisURI); // 2. 打开Redis连接 connection = redisClient.connect(); }
3.2.1 同步 API
复制代码
1
2
3
4
5
6
7
public static void main(String[] args) { // 1. 获取一个连接,方法在上面 getConnection(); // 2. 获取用于同步执行的命令API RedisCommands<String, String> redisCommands = connection.sync(); }
3.2.2 异步 API
复制代码
1
2
3
4
5
6
7
public static void main(String[] args) { // 1. 获取一个连接,方法在上面 getConnection(); // 2. 获取用于同步执行的命令API RedisAsyncCommands<String, String> async = connection.async(); }
3.2.3 反应式 API
复制代码
1
2
3
4
5
6
7
public static void main(String[] args) { // 1. 获取一个连接,方法在上面 getConnection(); // 2. 获取用于同步执行的命令API RedisReactiveCommands<String, String> reactive = connection.reactive(); }
3.3 操作基础数据类型

由于Lettuce支持三种模式的 API,其实主要是获取对应的模式上会有差异,在操作其他 API 上基本还是一样的,下面使用同步模式来操作。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public static void main(String[] args) { // 1. 获取一个连接,方法在上面 getConnection(); // 2. 获取用于同步执行的命令API RedisCommands<String, String> redisCommands = connection.sync(); // 3. 操作字符串 redisCommands.set("hello", "world"); // 3.1. 获取字符串 String hello = redisCommands.get("hello"); // 4. 设置hash redisCommands.hset("myHash", "key1", "val1"); // 4.1. 获取hash redisCommands.hget("myHash", "key1"); // 5. 操作list redisCommands.rpush("myList", "val1"); redisCommands.rpush("myList", "val2"); // 5.1. 获取list redisCommands.lrange("myList", 0, -1); // 6. 操作set redisCommands.sadd("mySet", "val1"); redisCommands.sadd("mySet", "val2"); // 6.1. 获取作set redisCommands.smembers("mySet"); // 7. 操作sorted set redisCommands.zadd("mySortedSet", 100, "member1"); redisCommands.zadd("mySortedSet", 99, "member2"); // 7.1. 获取sorted set redisCommands.zrangeWithScores("mySortedSet", 0, -1); }
3.4 发布/订阅

Lettuce提供了对单机和集群连接上的发布/订阅的支持。订阅频道或模式后,将在消息/已订阅/未订阅事件中通知连接。提供了同步,异步和反应性API来与 Redis Pub/Sub功能进行交互。非集群模式下的发布订阅依赖于定制的连接StatefulRedisPubSubConnection,集群模式下的发布订阅依赖于定制的连接StatefulRedisClusterPubSubConnection,两者分别来源于RedisClient#connectPubSub()系列方法和RedisClusterClient#connectPubSub()

3.4.1 自定义一个监听器
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public static class MyRedisPubSubListener implements RedisPubSubListener { @Override public void message(Object channel, Object message) { System.out.println("channel:" + channel + " say: " + message); } @Override public void message(Object pattern, Object channel, Object message) { System.out.println("pattern:" + pattern + "say: " + message); } @Override public void subscribed(Object channel, long count) { System.out.println(channel + " subscribed"); } @Override public void psubscribed(Object pattern, long count) { System.out.println(pattern + " psubscribed"); } @Override public void unsubscribed(Object channel, long count) { System.out.println(channel + " unsubscribed"); } @Override public void punsubscribed(Object pattern, long count) { System.out.println(pattern + " punsubscribed"); } }
3.4.2 三种方式订阅
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) throws ExecutionException, InterruptedException { // 1. 获取一个连接,方法在上面(可能是单机、普通主从、哨兵等非集群模式的客户端) getConnection(); StatefulRedisPubSubConnection<String, String> connection = redisClient.connectPubSub(); connection.addListener(new MyRedisPubSubListener()); // 同步订阅 RedisPubSubCommands<String, String> sync = connection.sync(); sync.subscribe("channel"); // 异步订阅 RedisPubSubAsyncCommands<String, String> async = connection.async(); RedisFuture<Void> future = async.subscribe("channel"); // 反应式订阅 RedisPubSubReactiveCommands<String, String> reactive = connection.reactive(); reactive.subscribe("channel").subscribe(); reactive.observeChannels().doOnNext(patternMessage -> { System.out.println("observeChannels doOnNext " + patternMessage.getMessage()); }).subscribe(); }
3.4.3 发布消息

这里测试的时候需要注意的是不能使用同一个连接,否则会报错的。

复制代码
1
2
3
4
5
6
7
public static void main(String[] args) { // 重新初始化连接 LettuceTest.getConnection(); // 发布消息,执行完后再看另外一个控制台能把接受到的消息打印 LettuceTest.connection.sync().publish("channel", "hello"); }
3.5 执行 Lua

有不熟悉 Redis 执行Lua的好哥哥可以看Redis 万字长文 Lua 详解

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) throws ExecutionException, InterruptedException { // 1. 获取一个连接,方法在上面(可能是单机、普通主从、哨兵等非集群模式的客户端) getConnection(); RedisCommands<String, String> redisCommands = connection.sync(); // 2. 定义一个简单的脚本 String script = "return redis.call('get',KEYS[1])"; // 3. 第一种方式:直接执行脚本 Object hello = redisCommands.eval(script, ScriptOutputType.VALUE, "hello"); // 4. 第二种方式:加载脚本到Redis,然后执行 String scriptSha = redisCommands.scriptLoad(script); // 4.1. 根据sha执行脚本 Object helloResult = redisCommands.evalsha(scriptSha, ScriptOutputType.VALUE, "hello"); }
3.6 流 API

这个跟 Java8 中的流是一个概念,就是Lettuce支持了这种方式。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) throws ExecutionException, InterruptedException { // 1. 获取一个连接,方法在上面(可能是单机、普通主从、哨兵等非集群模式的客户端) getConnection(); RedisCommands<String, String> redisCommands = connection.sync(); // 添加元素到list redisCommands.lpush("key", "one"); redisCommands.lpush("key", "two"); redisCommands.lpush("key", "three"); // 流式返回 Long count = redisCommands.lrange(new ValueStreamingChannel<String>() { @Override public void onValue(String value) { System.out.println("Value: " + value); } }, "key", 0, -1); }

与 Jedis 对比

  1. 线程安全上,Jedis 是线程不安全的,Lettuce 是线程安全的。
    这个指的是直连方式来说,实际上Jedis使用连接池也是线程安全的。
  2. 从实现方式上来说,Jedis实现简单、使用简单,而Lettuce 实现是很复杂的,但是还好使用起来简单。
  3. 从网络上来说,Jedis 使用的是阻塞I/O,而Lettuce是基于Netty使用NIO
  4. 从抽象程度来说,Jedis并没有做特别的抽象处理,而Lettuce抽象了三种处理命令的模式(同步、异步、反应式)。
  5. 从获取连接上来说,Jedis一般使用的是多个连接(从连接池获取),而Lettuce共享连接,连接是 long-lived 的和线程安全的,并且会自动重连。
  6. 从社区上来讲,Jedis社区较活跃,版本更新快,而Lettuce的话活跃度就一般了,版本更新较慢(很少关于Lettuce的资料)。

总结

还有一些关于Lettuce API、高可用配置等相关的东西没有写进来。一方面是篇幅的原因,一方面的话也是我们平时不会单独使用Lettuce,都是基于 Spring 的spring-boot-starter-data-redis来对 Redis 做相关操作。这里贴一下Lettuce 的源码地址(Lettuce 源码),感兴趣的好哥哥可以上去了解一下。

本期就到这啦,有不对的地方欢迎好哥哥们评论区留言,另外求关注、求点赞

上一篇: Redis 客户端 Jedis 详解

最后

以上就是优美太阳最近收集整理的关于「进击Redis」二十、万字长文解析Redis 高级客户端Lettuce的全部内容,更多相关「进击Redis」二十、万字长文解析Redis内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(59)

评论列表共有 0 条评论

立即
投稿
返回
顶部