Redis實戰(zhàn)之Lettuce的使用技巧詳解
一、摘要
Lettuce 是 Redis 的一款高級 Java 客戶端,與 Jedis 并列成為最熱門的客戶端之一,目前已成為 SpringBoot 2.0 版本默認的 redis 客戶端。

相比老牌 Jedis,Lettuce 屬于后起之秀,不僅功能豐富,而且提供了很多新的功能特性,比如異步操作、響應(yīng)式編程等等,同時還解決了 Jedis 中線程不安全的問題。
廢話不多說了,如何使用呢?請看下文!
二、Lettuce
2.1、基本使用
首先,創(chuàng)建一個 maven 項目,引入lettuce-core包,就可以使用了。
<dependency> ??<groupId>io.lettuce</groupId> ??<artifactId>lettuce-core</artifactId> ??<version>5.3.1.RELEASE</version> </dependency>
使用 lettuce 連接 redis,測試是否能正常聯(lián)通!
public?class?LettuceMain?{
????public?static?void?main(String[]?args)?{
????????RedisURI?redisUri?=?RedisURI.builder()
????????????????.withHost("127.0.0.1")
????????????????.withPort(6379)
????????????????.withPassword("111111")
????????????????.withTimeout(Duration.of(10,?ChronoUnit.SECONDS))
????????????????.build();
????????RedisClient?redisClient?=?RedisClient.create(redisUri);
????????StatefulRedisConnection<String,?String>?connection?=?redisClient.connect();
????????RedisCommands<String,?String>?commands?=?connection.sync();
????????System.out.println(commands.ping());
????????connection.close();
????????redisClient.shutdown();
????}
}
2.2、同步操作
基本上只要是 Jedis 支持的同步命令操作,Lettuce 都支持。
下面,我們以同步操作字符串為例,Lettuce 的 api 操作如下!
public?class?LettuceSyncMain?{
????public?static?void?main(String[]?args)?{
????????RedisURI?redisUri?=?RedisURI.builder()
????????????????.withHost("127.0.0.1").withPort(6379).withPassword("111111")
????????????????.withTimeout(Duration.of(10,?ChronoUnit.SECONDS))
????????????????.build();
????????RedisClient?redisClient?=?RedisClient.create(redisUri);
????????StatefulRedisConnection<String,?String>?connection?=?redisClient.connect();
????????//獲取同步操作命令工具
????????RedisCommands<String,?String>?commands?=?connection.sync();
????????System.out.println("清空數(shù)據(jù):"+commands.flushdb());
????????System.out.println("判斷某個鍵是否存在:"+commands.exists("username"));
????????System.out.println("新增<'username','xmr'>的鍵值對:"+commands.set("username",?"xmr"));
????????System.out.println("新增<'password','password'>的鍵值對:"+commands.set("password",?"123"));
????????System.out.println("獲取<'password'>鍵的值:"+commands.get("password"));
????????System.out.println("系統(tǒng)中所有的鍵如下:"?+?commands.keys("*"));
????????System.out.println("刪除鍵password:"+commands.del("password"));
????????System.out.println("判斷鍵password是否存在:"+commands.exists("password"));
????????System.out.println("設(shè)置鍵username的過期時間為5s:"+commands.expire("username",?5L));
????????System.out.println("查看鍵username的剩余生存時間:"+commands.ttl("username"));
????????System.out.println("移除鍵username的生存時間:"+commands.persist("username"));
????????System.out.println("查看鍵username的剩余生存時間:"+commands.ttl("username"));
????????System.out.println("查看鍵username所存儲的值的類型:"+commands.type("username"));
????????connection.close();
????????redisClient.shutdown();
????}
}
2.3、異步操作
除此之外,Lettuce 還支持異步操作,將上面的操作改成異步處理,結(jié)果如下!
public?class?LettuceASyncMain?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????RedisURI?redisUri?=?RedisURI.builder()
????????????????.withHost("127.0.0.1").withPort(6379).withPassword("111111")
????????????????.withTimeout(Duration.of(10,?ChronoUnit.SECONDS))
????????????????.build();
????????RedisClient?redisClient?=?RedisClient.create(redisUri);
????????StatefulRedisConnection<String,?String>?connection?=?redisClient.connect();
????????//獲取異步操作命令工具
????????RedisAsyncCommands<String,?String>?commands?=?connection.async();
????????System.out.println("清空數(shù)據(jù):"+commands.flushdb().get());
????????System.out.println("判斷某個鍵是否存在:"+commands.exists("username").get());
????????System.out.println("新增<'username','xmr'>的鍵值對:"+commands.set("username",?"xmr").get());
????????System.out.println("新增<'password','password'>的鍵值對:"+commands.set("password",?"123").get());
????????System.out.println("獲取<'password'>鍵的值:"+commands.get("password").get());
????????System.out.println("系統(tǒng)中所有的鍵如下:"?+?commands.keys("*").get());
????????System.out.println("刪除鍵password:"+commands.del("password").get());
????????System.out.println("判斷鍵password是否存在:"+commands.exists("password").get());
????????System.out.println("設(shè)置鍵username的過期時間為5s:"+commands.expire("username",?5L).get());
????????System.out.println("查看鍵username的剩余生存時間:"+commands.ttl("username").get());
????????System.out.println("移除鍵username的生存時間:"+commands.persist("username").get());
????????System.out.println("查看鍵username的剩余生存時間:"+commands.ttl("username").get());
????????System.out.println("查看鍵username所存儲的值的類型:"+commands.type("username").get());
????????connection.close();
????????redisClient.shutdown();
????}
}
2.4、響應(yīng)式編程
Lettuce 除了支持異步編程以外,還支持響應(yīng)式編程,Lettuce 引入的響應(yīng)式編程框架是Project Reactor,如果沒有響應(yīng)式編程經(jīng)驗可以先自行了解一下,訪問地址https://projectreactor.io/。
響應(yīng)式編程使用案例如下:
public?class?LettuceMain?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????RedisURI?redisUri?=?RedisURI.builder()
????????????????.withHost("127.0.0.1").withPort(6379).withPassword("111111")
????????????????.withTimeout(Duration.of(10,?ChronoUnit.SECONDS))
????????????????.build();
????????RedisClient?redisClient?=?RedisClient.create(redisUri);
????????StatefulRedisConnection<String,?String>?connection?=?redisClient.connect();
????????//獲取響應(yīng)式API操作命令工具
????????RedisReactiveCommands<String,?String>?commands?=?connection.reactive();
????????Mono<String>?setc?=?commands.set("name",?"mayun");
????????System.out.println(setc.block());
????????Mono<String>?getc?=?commands.get("name");
????????getc.subscribe(System.out::println);
????????Flux<String>?keys?=?commands.keys("*");
????????keys.subscribe(System.out::println);
????????//開啟一個事務(wù),先把count設(shè)置為1,再將count自增1
????????commands.multi().doOnSuccess(r?->?{
????????????commands.set("count",?"1").doOnNext(value?->?System.out.println("count1:"?+??value)).subscribe();
????????????commands.incr("count").doOnNext(value?->?System.out.println("count2:"?+??value)).subscribe();
????????}).flatMap(s?->?commands.exec())
????????????????.doOnNext(transactionResult?->?System.out.println("transactionResult:"?+?transactionResult.wasDiscarded())).subscribe();
????????Thread.sleep(1000?*?5);
????????connection.close();
????????redisClient.shutdown();
????}
}
2.5、發(fā)布和訂閱
Lettuce 還支持 redis 的消息發(fā)布和訂閱,具體實現(xiàn)案例如下:
public?class?LettuceReactiveMain1?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????RedisURI?redisUri?=?RedisURI.builder()
????????????????.withHost("127.0.0.1").withPort(6379).withPassword("111111")
????????????????.withTimeout(Duration.of(10,?ChronoUnit.SECONDS))
????????????????.build();
????????RedisClient?redisClient?=?RedisClient.create(redisUri);
????????//獲取發(fā)布訂閱操作命令工具
????????StatefulRedisPubSubConnection<String,?String>?pubsubConn?=?redisClient.connectPubSub();
????????pubsubConn.addListener(new?RedisPubSubListener<String,?String>()?{
????????????@Override
????????????public?void?unsubscribed(String?channel,?long?count)?{
????????????????System.out.println("[unsubscribed]"?+?channel);
????????????}
????????????@Override
????????????public?void?subscribed(String?channel,?long?count)?{
????????????????System.out.println("[subscribed]"?+?channel);
????????????}
????????????@Override
????????????public?void?punsubscribed(String?pattern,?long?count)?{
????????????????System.out.println("[punsubscribed]"?+?pattern);
????????????}
????????????@Override
????????????public?void?psubscribed(String?pattern,?long?count)?{
????????????????System.out.println("[psubscribed]"?+?pattern);
????????????}
????????????@Override
????????????public?void?message(String?pattern,?String?channel,?String?message)?{
????????????????System.out.println("[message]"?+?pattern?+?"?->?"?+?channel?+?"?->?"?+?message);
????????????}
????????????@Override
????????????public?void?message(String?channel,?String?message)?{
????????????????System.out.println("[message]"?+?channel?+?"?->?"?+?message);
????????????}
????????});
????????RedisPubSubAsyncCommands<String,?String>?pubsubCmd?=?pubsubConn.async();
????????pubsubCmd.psubscribe("CH");
????????pubsubCmd.psubscribe("CH2");
????????pubsubCmd.unsubscribe("CH");
????????Thread.sleep(100?*?5);
????????pubsubConn.close();
????????redisClient.shutdown();
????}
}
2.6、客戶端資源與參數(shù)配置
Lettuce 客戶端的通信框架集成了 Netty 的非阻塞 IO 操作,客戶端資源的設(shè)置與 Lettuce 的性能、并發(fā)和事件處理緊密相關(guān),如果不是特別熟悉客戶端參數(shù)配置,不建議在沒有經(jīng)驗的前提下憑直覺修改默認值,保持默認配置就行。
非集群環(huán)境下,具體的配置案例如下:
public?class?LettuceMain?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????ClientResources?resources?=?DefaultClientResources.builder()
????????????????.ioThreadPoolSize(4)?//I/O線程數(shù)
????????????????.computationThreadPoolSize(4)?//任務(wù)線程數(shù)
????????????????.build();
????????RedisURI?redisUri?=?RedisURI.builder()
????????????????.withHost("127.0.0.1").withPort(6379).withPassword("111111")
????????????????.withTimeout(Duration.of(10,?ChronoUnit.SECONDS))
????????????????.build();
????????ClientOptions?options?=?ClientOptions.builder()
????????????????.autoReconnect(true)//是否自動重連
????????????????.pingBeforeActivateConnection(true)//連接激活之前是否執(zhí)行PING命令
????????????????.build();
????????RedisClient?client?=?RedisClient.create(resources,?redisUri);
????????client.setOptions(options);
????????StatefulRedisConnection<String,?String>?connection?=?client.connect();
????????RedisCommands<String,?String>?commands?=?connection.sync();
????????commands.set("name",?"關(guān)羽");
????????System.out.println(commands.get("name"));
????????connection.close();
????????client.shutdown();
????????resources.shutdown();
????}
}
集群環(huán)境下,具體的配置案例如下:
public?class?LettuceMain?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????ClientResources?resources?=?DefaultClientResources.builder()
????????????????.ioThreadPoolSize(4)?//I/O線程數(shù)
????????????????.computationThreadPoolSize(4)?//任務(wù)線程數(shù)
????????????????.build();
????????RedisURI?redisUri?=?RedisURI.builder()
????????????????.withHost("127.0.0.1").withPort(6379).withPassword("111111")
????????????????.withTimeout(Duration.of(10,?ChronoUnit.SECONDS))
????????????????.build();
????????ClusterClientOptions?options?=?ClusterClientOptions.builder()
????????????????.autoReconnect(true)//是否自動重連
????????????????.pingBeforeActivateConnection(true)//連接激活之前是否執(zhí)行PING命令
????????????????.validateClusterNodeMembership(true)//是否校驗集群節(jié)點的成員關(guān)系
????????????????.build();
????????RedisClusterClient?client?=?RedisClusterClient.create(resources,?redisUri);
????????client.setOptions(options);
????????StatefulRedisClusterConnection<String,?String>?connection?=?client.connect();
????????RedisAdvancedClusterCommands<String,?String>?commands?=?connection.sync();
????????commands.set("name",?"張飛");
????????System.out.println(commands.get("name"));
????????connection.close();
????????client.shutdown();
????????resources.shutdown();
????}
}
2.7、線程池配置
Lettuce 連接設(shè)計的時候,就是線程安全的,所以一個連接可以被多個線程共享,同時 lettuce 連接默認是自動重連的,使用單連接基本可以滿足業(yè)務(wù)需求,大多數(shù)情況下不需要配置連接池,多連接并不會給操作帶來性能上的提升。
但在某些特殊場景下,比如事物操作,使用連接池會是一個比較好的方案,那么如何配置線程池呢?
public?class?LettuceMain?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????RedisURI?redisUri?=?RedisURI.builder()
????????????????.withHost("127.0.0.1")
????????????????.withPort(6379)
????????????????.withPassword("111111")
????????????????.withTimeout(Duration.of(10,?ChronoUnit.SECONDS))
????????????????.build();
????????RedisClient?client?=?RedisClient.create(redisUri);
????????//連接池配置
????????GenericObjectPoolConfig?poolConfig?=?new?GenericObjectPoolConfig();
????????poolConfig.setMaxIdle(2);
????????GenericObjectPool<StatefulRedisConnection<String,?String>>?pool?=?ConnectionPoolSupport.createGenericObjectPool(client::connect,?poolConfig);
????????StatefulRedisConnection<String,?String>?connection?=?pool.borrowObject();
????????RedisCommands<String,?String>?commands?=?connection.sync();
????????commands.set("name",?"張飛");
????????System.out.println(commands.get("name"));
????????connection.close();
????????pool.close();
????????client.shutdown();
????}
}
2.8、主從模式配置
redis 一般采用主從復(fù)制模式,搭建高可用的架構(gòu),簡單的說就一個主節(jié)點,多個從節(jié)點,自動從主節(jié)點同步最新數(shù)據(jù)。
Lettuce 支持自動發(fā)現(xiàn)主從模式下的節(jié)點信息,然后保存到本地,具體配置如下:
public?class?LettuceMain?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????//這里只需要配置一個節(jié)點的連接信息,不一定需要是主節(jié)點的信息,從節(jié)點也可以;可以自動發(fā)現(xiàn)主從節(jié)點
????????RedisURI?uri?=?RedisURI.builder().withHost("192.168.31.111").withPort(6379).withPassword("123456").build();
????????RedisClient?client?=?RedisClient.create(uri);
????????StatefulRedisMasterReplicaConnection<String,?String>?connection?=?MasterReplica.connect(client,?StringCodec.UTF8,?uri);
????????//從節(jié)點讀取數(shù)據(jù)
????????connection.setReadFrom(ReadFrom.REPLICA);
????????RedisCommands<String,?String>?commands?=?connection.sync();
????????commands.set("name",?"張飛");
????????System.out.println(commands.get("name"));
????????connection.close();
????????client.shutdown();
????}
}
當然我們也可以手動指定集群節(jié)點來加載,具體配置如下:
public?class?LettuceMain?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????//集群節(jié)點
????????List<RedisURI>?uris?=?new?ArrayList();
????????uris.add(RedisURI.builder().withHost("192.168.31.111").withPort(6379).withPassword("111111").build());
????????uris.add(RedisURI.builder().withHost("192.168.31.112").withPort(6379).withPassword("111111").build());
????????uris.add(RedisURI.builder().withHost("192.168.31.113").withPort(6379).withPassword("111111").build());
????????RedisClient?client?=?RedisClient.create();
????????StatefulRedisMasterReplicaConnection<String,?String>?connection?=?MasterReplica.connect(client,?StringCodec.UTF8,?uris);
????????//從節(jié)點讀取數(shù)據(jù)
????????connection.setReadFrom(ReadFrom.REPLICA);
????????RedisCommands<String,?String>?commands?=?connection.sync();
????????commands.set("name",?"張飛");
????????System.out.println(commands.get("name"));
????????connection.close();
????????client.shutdown();
????}
}
2.9、哨兵模式配置
哨兵模式,也是 redis 實現(xiàn)服務(wù)高可用的一大亮點,具體配置實現(xiàn)如下:
public?class?LettuceMain?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????//集群節(jié)點
????????List<RedisURI>?uris?=?new?ArrayList();
????????uris.add(RedisURI.builder().withSentinel("192.168.31.111",?26379).withSentinelMasterId("mymaster").withPassword("123456").build());
????????uris.add(RedisURI.builder().withSentinel("192.168.31.112",?26379).withSentinelMasterId("mymaster").withPassword("123456").build());
????????uris.add(RedisURI.builder().withSentinel("192.168.31.113",?26379).withSentinelMasterId("mymaster").withPassword("123456").build());
????????RedisClient?client?=?RedisClient.create();
????????StatefulRedisMasterReplicaConnection<String,?String>?connection?=?MasterReplica.connect(client,?StringCodec.UTF8,?uris);
????????//從節(jié)點讀取數(shù)據(jù)
????????connection.setReadFrom(ReadFrom.REPLICA);
????????RedisCommands<String,?String>?commands?=?connection.sync();
????????commands.set("name",?"趙云");
????????System.out.println(commands.get("name"));
????????connection.close();
????????client.shutdown();
????}
}
2.10、Cluster 集群模式配置
Cluster 集群模式,是之后推出的一種高可用的架構(gòu)模型,主要是采用分片方式來存儲數(shù)據(jù),具體配置如下:
public?class?LettuceReactiveMain4?{
????public?static?void?main(String[]?args)?throws?Exception?{
????????Set<RedisURI>?uris?=?new?HashSet<>();
????????uris.add(RedisURI.builder().withHost("192.168.31.111").withPort(7000).withPassword("123456").build());
????????uris.add(RedisURI.builder().withHost("192.168.31.112").withPort(7000).withPassword("123456").build());
????????uris.add(RedisURI.builder().withHost("192.168.31.113").withPort(7000).withPassword("123456").build());
????????uris.add(RedisURI.builder().withHost("192.168.31.114").withPort(7000).withPassword("123456").build());
????????uris.add(RedisURI.builder().withHost("192.168.31.115").withPort(7000).withPassword("123456").build());
????????uris.add(RedisURI.builder().withHost("192.168.31.116").withPort(7001).withPassword("123456").build());
????????RedisClusterClient?client?=?RedisClusterClient.create(uris);
????????StatefulRedisClusterConnection<String,?String>?connection?=?client.connect();
????????RedisAdvancedClusterCommands<String,?String>?commands?=?connection.sync();
????????commands.set("name",?"關(guān)羽");
????????System.out.println(commands.get("name"));
????????//選擇從節(jié)點,只讀
????????NodeSelection<String,?String>?replicas?=?commands.replicas();
????????NodeSelectionCommands<String,?String>?nodeSelectionCommands?=?replicas.commands();
????????Executions<List<String>>?keys?=?nodeSelectionCommands.keys("*");
????????keys.forEach(key?->?System.out.println(key));
????????connection.close();
????????client.shutdown();
????}
}
三、小結(jié)
Lettuce 相比老牌的 Jedis 客戶端,功能更加強大,不僅解決了線程安全的問題,還支持異步和響應(yīng)式編程,支持集群,Sentinel,管道和編碼器等等功能。
以上介紹的可能只是冰山一角,如果想要了解更多的信息,可以訪問它的官網(wǎng)地址:https://lettuce.io/
到此這篇關(guān)于Redis實戰(zhàn)之Lettuce的使用技巧詳解的文章就介紹到這了,更多相關(guān)Redis Lettuce內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mac中Redis服務(wù)啟動時錯誤信息:NOAUTH Authentication required
這篇文章主要介紹了Mac中使用Redis服務(wù)啟動時錯誤信息:"NOAUTH Authentication required"問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08
Redis基本數(shù)據(jù)類型String常用操作命令
這篇文章主要為大家介紹了Redis基本數(shù)據(jù)類型String常用操作命令,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-05-05
Redis學(xué)習(xí)教程之命令的執(zhí)行過程詳解
這篇文章主要給大家介紹了關(guān)于Redis學(xué)習(xí)教程之命令的執(zhí)行過程的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-03-03

