Redis實(shí)戰(zhàn)之Lettuce的使用技巧詳解
一、摘要
Lettuce 是 Redis 的一款高級 Java 客戶端,與 Jedis 并列成為最熱門的客戶端之一,目前已成為 SpringBoot 2.0 版本默認(rèn)的 redis 客戶端。
相比老牌 Jedis,Lettuce 屬于后起之秀,不僅功能豐富,而且提供了很多新的功能特性,比如異步操作、響應(yīng)式編程等等,同時(shí)還解決了 Jedis 中線程不安全的問題。
廢話不多說了,如何使用呢?請看下文!
二、Lettuce
2.1、基本使用
首先,創(chuàng)建一個(gè) maven 項(xiàng)目,引入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("判斷某個(gè)鍵是否存在:"+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的過期時(shí)間為5s:"+commands.expire("username",?5L)); ????????System.out.println("查看鍵username的剩余生存時(shí)間:"+commands.ttl("username")); ????????System.out.println("移除鍵username的生存時(shí)間:"+commands.persist("username")); ????????System.out.println("查看鍵username的剩余生存時(shí)間:"+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("判斷某個(gè)鍵是否存在:"+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的過期時(shí)間為5s:"+commands.expire("username",?5L).get()); ????????System.out.println("查看鍵username的剩余生存時(shí)間:"+commands.ttl("username").get()); ????????System.out.println("移除鍵username的生存時(shí)間:"+commands.persist("username").get()); ????????System.out.println("查看鍵username的剩余生存時(shí)間:"+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)驗(yàn)可以先自行了解一下,訪問地址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); ????????//開啟一個(gè)事務(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ā)布和訂閱,具體實(shí)現(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)驗(yàn)的前提下憑直覺修改默認(rèn)值,保持默認(rèn)配置就行。
非集群環(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)//是否校驗(yàn)集群節(jié)點(diǎn)的成員關(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è)計(jì)的時(shí)候,就是線程安全的,所以一個(gè)連接可以被多個(gè)線程共享,同時(shí) lettuce 連接默認(rèn)是自動重連的,使用單連接基本可以滿足業(yè)務(wù)需求,大多數(shù)情況下不需要配置連接池,多連接并不會給操作帶來性能上的提升。
但在某些特殊場景下,比如事物操作,使用連接池會是一個(gè)比較好的方案,那么如何配置線程池呢?
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),簡單的說就一個(gè)主節(jié)點(diǎn),多個(gè)從節(jié)點(diǎn),自動從主節(jié)點(diǎn)同步最新數(shù)據(jù)。
Lettuce 支持自動發(fā)現(xiàn)主從模式下的節(jié)點(diǎn)信息,然后保存到本地,具體配置如下:
public?class?LettuceMain?{ ????public?static?void?main(String[]?args)?throws?Exception?{ ????????//這里只需要配置一個(gè)節(jié)點(diǎn)的連接信息,不一定需要是主節(jié)點(diǎn)的信息,從節(jié)點(diǎn)也可以;可以自動發(fā)現(xiàn)主從節(jié)點(diǎn) ????????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é)點(diǎn)讀取數(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(); ????} }
當(dāng)然我們也可以手動指定集群節(jié)點(diǎn)來加載,具體配置如下:
public?class?LettuceMain?{ ????public?static?void?main(String[]?args)?throws?Exception?{ ????????//集群節(jié)點(diǎn) ????????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é)點(diǎn)讀取數(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 實(shí)現(xiàn)服務(wù)高可用的一大亮點(diǎn),具體配置實(shí)現(xiàn)如下:
public?class?LettuceMain?{ ????public?static?void?main(String[]?args)?throws?Exception?{ ????????//集群節(jié)點(diǎn) ????????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é)點(diǎn)讀取數(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é)點(diǎn),只讀 ????????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 客戶端,功能更加強(qiáng)大,不僅解決了線程安全的問題,還支持異步和響應(yīng)式編程,支持集群,Sentinel,管道和編碼器等等功能。
以上介紹的可能只是冰山一角,如果想要了解更多的信息,可以訪問它的官網(wǎng)地址:https://lettuce.io/
到此這篇關(guān)于Redis實(shí)戰(zhàn)之Lettuce的使用技巧詳解的文章就介紹到這了,更多相關(guān)Redis Lettuce內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis 對比 Memcached 并在 CentOS 下進(jìn)行安裝配置詳解
Redis 是一個(gè)開源、支持網(wǎng)絡(luò)、基于內(nèi)存、鍵值對的 Key-Value 數(shù)據(jù)庫,本篇文章主要介紹了Redis 對比 Memcached 并在 CentOS 下進(jìn)行安裝配置詳解,有興趣的可以了解一下。2016-11-11Redis 實(shí)現(xiàn)隊(duì)列原理的實(shí)例詳解
這篇文章主要介紹了Redis 實(shí)現(xiàn)隊(duì)列原理的實(shí)例詳解的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-09-09基于Redis實(shí)現(xiàn)分布式鎖以及任務(wù)隊(duì)列
這篇文章主要介紹了基于Redis實(shí)現(xiàn)分布式鎖以及任務(wù)隊(duì)列,需要的朋友可以參考下2015-11-11Springboot/Springcloud項(xiàng)目集成redis進(jìn)行存取的過程解析
大家都知道Redis支持五種數(shù)據(jù)類型:string(字符串),hash(哈希),list(列表),set(集合),zset(sorted set:有序集合),本文重點(diǎn)給大家介紹Springboot/Springcloud項(xiàng)目集成redis進(jìn)行存取的過程,需要的朋友參考下吧2021-12-12Redis主從架構(gòu)和高可用性實(shí)現(xiàn)過程
本文詳細(xì)介紹了使用Redis主從架構(gòu)和Linux虛擬服務(wù)器(LVS)實(shí)現(xiàn)高可用性的方法,并回顧了最近完成的Redis集群遷移部署過程,主從架構(gòu)通過復(fù)制數(shù)據(jù)來提高性能和數(shù)據(jù)冗余,而LVS用于實(shí)現(xiàn)負(fù)載均衡和故障切換,感興趣的朋友跟隨小編一起看看吧2024-09-09