环境
Lettuce的官网( https://lettuce.io/core/release/reference/index.html )对Lettuce的描述是: Lettuce is a scalable thread-safe Redis client based on netty and Reactor. Lettuce provides synchronous, asynchronous and reactive APIs to interact with Redis.
翻译过来就是:Lettuce是可伸缩的、线程安全的Redis客户端,它基于netty和Reactor。Lettuce提供了同步、异步和反应式API来与Redis交互。
注:关于如何在Java项目中配置Lettuce,参见我另一篇文档。
Lettuce的“hello world”代码如下:
RedisURI redisUri = RedisURI.builder().withHost("localhost").withPort(6379).build();
RedisClient redisClient = RedisClient.create(redisUri);
StatefulRedisConnection<String, String> connection = redisClient.connect();
RedisCommands<String, String> syncCommands = connection.sync();
syncCommands.set("mykey1", "hello");
connection.close();
redisClient.shutdown();
Lettuce的核心如下:
RedisURI
RedisClient
StatefulConnection
RedisCommands
RedisURI
封装了Redis服务器的信息。可用以下几种方法来创建 RedisURI
:
RedisURI.create("redis://localhost/");
RedisURI.Builder.redis("localhost", 6379).auth("password").database(1).build();
new RedisURI("localhost", 6379, 60, TimeUnit.SECONDS);
上面的“hello world”代码使用的是其中第2种方法。
注:连接Redis又可分为Standalone(单机),Sentinel(哨兵),Cluster(集群)等几种不同的模式。本文只涉及Standalone模式。
代表Redis的客户端,通过调用 RedisClient.create(redisUri)
方法来创建,最后别忘了调用 shutdown()
方法来关闭。
注:与之类似的还有 RedisClusterClient
类。
代表Redis连接,通过调用 redisClient.connect()
方法来创建,最后别忘了调用 close()
方法来关闭。
注: StatefulConnection
是根接口。本例中,调用 redisClient.connect()
方法返回的是其子接口 StatefulRedisConnection
,其实现类是 StatefulRedisConnectionImpl
。
StatefulConnection
还有其它子接口,如 StatefulRedisPubSubConnection
、 StatefulRedisSentinelConnection
等。
前面提到,Lettuce提供了同步、异步和反应式API来与Redis交互,其对应不同的 RedisXxxCommands
接口:
RedisCommands
:同步模式,通过 connection.sync()
方法创建;RedisAsyncCommands
:异步模式,通过 connection.async()
方法创建;RedisReactiveCommands
:反应式模式,通过 connection.reactive()
方法创建;此外,每个接口还有支持发布-订阅的变种接口:
RedisPubSubCommands
:同步模式,支持发布-订阅;RedisPubSubAsyncCommands
:异步模式,支持发布-订阅;RedisPubSubReactiveCommands
:反应式模式,支持发布-订阅;RedisXxxCommands
非常类似于 redis-cli
工具,用来执行各种Redis命令。在 redis-cli
命令行可以运行的命令,基本上 RedisXxxCommands
都提供了对等的方法。
参见上例。
例如:
RedisURI redisUri = RedisURI.builder().withHost("localhost").withPort(6379).build();
RedisClient redisClient = RedisClient.create(redisUri);
StatefulRedisConnection<String, String> connection = redisClient.connect();
RedisAsyncCommands<String, String> asyncCommands = connection.async();
asyncCommands.set("mykey1", "hello").thenAccept(System.out::println);
Thread.sleep(1000);
connection.close();
redisClient.shutdown();
本例中, set()
方法是异步的,返回 RedisFuture
对象,可用 thenAccept()
方法来处理返回结果。这和Java 8中的 CompletableFuture
非常类似。
另外,因为使用了异步模式,为了防止主程序结束的太快,所以让主程序sleep了一会儿,以等待异步线程结束。
例如:
RedisURI redisUri = RedisURI.builder().withHost("localhost").withPort(6379).build();
RedisClient redisClient = RedisClient.create(redisUri);
StatefulRedisConnection<String, String> connection = redisClient.connect();
RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();
reactiveCommands.set("mykey1", "hello").subscribe(System.out::println);
Thread.sleep(1000);
connection.close();
redisClient.shutdown();
本例中, set()
方法是异步的,返回 Mono
对象,可用 subscribe()
方法来订阅返回结果。
同样,因为反应式模式也是异步的,为了防止主程序结束的太快,所以让主程序sleep了一会儿,以等待异步线程结束。
代码如下:
RedisURI redisUri = RedisURI.builder().withHost("localhost").withPort(6379).build();
RedisClient redisClient = RedisClient.create(redisUri);
StatefulRedisPubSubConnection<String, String> connection = redisClient.connectPubSub();
RedisPubSubCommands<String, String> pubSubCommands = connection.sync();
pubSubCommands.publish("mychannel1", "hello world");
connection.close();
redisClient.shutdown();
打开 redis-cli
命令行,订阅 mychannel1
:
127.0.0.1:6379> subscribe mychannel1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "mychannel1"
3) (integer) 1
然后运行程序,发布消息,在命令行就能捕获该消息:
1) "message"
2) "mychannel1"
3) "hello world"
代码如下:
RedisURI redisUri = RedisURI.builder().withHost("localhost").withPort(6379).build();
RedisClient redisClient = RedisClient.create(redisUri);
StatefulRedisPubSubConnection<String, String> connection = redisClient.connectPubSub();
connection.addListener(new RedisPubSubAdapter<>() {
@Override
public void subscribed(String channel, long count) {
System.out.println("subscribed, channel: " + channel + ", count: " + count);
}
@Override
public void message(String channel, String message) {
System.out.println("channel: " + channel + ", message: " + message);
}
});
RedisPubSubCommands<String, String> pubSubCommands = connection.sync();
pubSubCommands.subscribe("mychannel1");
Thread.sleep(10000);
connection.close();
redisClient.shutdown();
注意,需要调用 connection.addListener()
方法,指定“订阅成功”和“捕获消息”时的回调方法。
运行程序,监听消息(回调 subscribed()
方法):
subscribed, channel: mychannel1, count: 1
然后在命令行发布消息:
127.0.0.1:6379> publish mychannel1 "Are you OK?"
(integer) 1
127.0.0.1:6379>
程序就会捕获该消息(回调 message()
方法):
channel: mychannel1, message: Are you OK?
10秒钟之后,程序退出。
上例是在命令行和程序之间做发布-订阅,当然,也可以在程序与程序之间做发布-订阅。
此外,也可以在多处订阅同一个channel,比如程序和命令行都订阅了 mychannel1
,然后在另一个程序或者命令行发布消息,则程序和命令行都会收到消息。
使用Lettuce线程池需要Apache Commons Pool2的支持。在 pom.xml
文件中添加如下依赖:
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
<version>2.11.1version>
dependency>
程序代码如下:
RedisURI redisUri = RedisURI.builder().withHost("localhost").withPort(6379).build();
RedisClient redisClient = RedisClient.create(redisUri);
GenericObjectPoolConfig<StatefulRedisConnection<String, String>> conf = new GenericObjectPoolConfig<>();
conf.setMaxTotal(20);
GenericObjectPool<StatefulRedisConnection<String, String>> pool =
ConnectionPoolSupport.createGenericObjectPool(redisClient::connect, conf);
StatefulRedisConnection<String, String> connection = pool.borrowObject();
RedisCommands<String, String> syncCommands = connection.sync();
syncCommands.set("mykey1", "hello");
connection.close();
pool.close();
redisClient.shutdown();