吃透Redis系列(十一):Jedis和Lettuce客户端详细介绍

2024-09-03 01:32

本文主要是介绍吃透Redis系列(十一):Jedis和Lettuce客户端详细介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Redis系列文章:

吃透Redis系列(一):Linux下Redis安装

吃透Redis系列(二):Redis六大数据类型详细用法

吃透Redis系列(三):Redis管道,发布/订阅,事物,过期时间 详细介绍

吃透Redis系列(四):布隆(bloom)过滤器详细介绍

吃透Redis系列(五):RDB和AOF持久化详细介绍

吃透Redis系列(六):主从复制详细介绍

吃透Redis系列(七):哨兵机制详细介绍

吃透Redis系列(八):集群详细介绍

吃透Redis系列(九):Redis代理twemproxy和predixy详细介绍

吃透Redis系列(十):Redis内存模型详细介绍

吃透Redis系列(十一):Jedis和Lettuce客户端详细介绍

文章目录

    • 前言
    • 一,Jedis使用
      • 1,导入maven包
      • 2,五种基本类型
      • 3,管道
      • 4,事物
      • 5,哨兵
      • 6,集群
    • 二,Lettuce使用
      • 1,Lettuce简单使用
      • 2,API
        • 2.1,同步API
        • 2.2,异步API
        • 2.3,反应式API
      • 3,发布/订阅
      • 4,管道
      • 5,事物
      • 6,普通主从模式
      • 7,哨兵
      • 8,集群

前言

我们打开Redis官网(https://redis.io/clients)发现,Redis支持以下语言:

在这里插入图片描述

因为Redis是二进制安全的,只存字节数组,所以只要各种语言客户端相互编解码一致,那么就可以互相通用,比如c存java取,等等。

当然,我们最关心的还是在Java中的使用,所以我们还是点开Java,看都有哪些客户端支持:

在这里插入图片描述

有如上几种客户端,那么你看最常用的就是标星的Jedis,Lettuce,Redisson。

接下来我们只详细了解Jedis,Lettuce,因为我们Spring中也是用了这两种客户端,具体在Spring中怎么使用,请参考挂网 Spring Data Redis

一,Jedis使用

首先打开Jedis的github:https://github.com/redis/jedis?_ga=2.241806790.538319447.1611192789-1620311179.1610019103

1,导入maven包

<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.5.0</version><type>jar</type><scope>compile</scope>
</dependency>

2,五种基本类型

public class MyRedisTest {public static void main(String[] args) {Jedis jedis = new Jedis("127.0.0.1", 6379);/*** 1,字符串操作*/jedis.set("name", "bobo");jedis.set("age", "18");jedis.incr("age");System.out.println("name:" + jedis.get("name") + " age:" + jedis.get("age"));/*** 2,list操作*/String keyList = "myList";jedis.rpush(keyList, "1");jedis.rpush(keyList, "2");jedis.rpush(keyList, "3");System.out.println(jedis.lrange(keyList, 0, -1));/*** 3,set操作*/String keySet1 = "mySet1";String keySet2 = "mySet2";jedis.sadd(keySet1, "a", "b", "c");jedis.sadd(keySet2, "d", "b", "f");//取交集jedis.sinterstore("result1", keySet1, keySet2);System.out.println("交集:" + jedis.smembers("result1"));//取并集jedis.sunionstore("result2", keySet1, keySet2);System.out.println("并集:" + jedis.smembers("result2"));/*** 4,有序集合*/String keyStoreSet = "myStoreSet";jedis.zadd(keyStoreSet, 3, "score3");jedis.zadd(keyStoreSet, 1, "score1");jedis.zadd(keyStoreSet, 2, "score2");System.out.println("zSet:" + jedis.zrange(keyStoreSet, 0, -1));/*** 5,哈希操作*/String hashKey = "myHashKey";jedis.hset(hashKey, "name", "bobo");jedis.hset(hashKey, "age", "18");System.out.println("hash value:" + jedis.hvals(hashKey));}
}

运行输出:

name:bobo age:19
[1, 2, 3]
交集:[b]
并集:[f, a, b, c, d]
zSet:[score1, score2, score3]
hash value:[bobo, 18]

redis-cli查询key:

在这里插入图片描述

3,管道

	@Testpublic void testPipe() {// 清空数据jedis.flushDB();Pipeline pipelined = jedis.pipelined();pipelined.set("name", "bobo");pipelined.zadd("storeSet", 3, "score3");pipelined.zadd("storeSet", 1, "score1");pipelined.zadd("storeSet", 2, "score2");pipelined.sync();System.out.println("name:" + jedis.get("name"));System.out.println("storeSet:" + jedis.zrange("storeSet", 0, -1));}

运行输出:

name:bobo
storeSet:[score1, score2, score3]

4,事物

	@Testpublic void testTransaction() {jedis.flushDB();Transaction transaction = jedis.multi();transaction.set("name", "bobo");transaction.lpush("myList", "a", "b", "c");transaction.lpop("myList");transaction.exec();System.out.println("name:" + jedis.get("name"));System.out.println("myList:" + jedis.lrange("myList", 0, -1));}

运行输出:

name:bobo
myList:[b, a]

5,哨兵

部署三个哨兵节点

哨兵部署详细操作请看:吃透Redis系列(七):哨兵机制详细介绍

# sentinel_26379.conf
port 26379
daemonize yes
logfile /var/lib/sentinel_26379.log
sentinel monitor mymaster 127.0.0.1 6379 2# sentinel_26380.conf
port 26380
daemonize yes
logfile /var/lib/sentinel_26380.log
sentinel monitor mymaster 127.0.0.1 6379 2# sentinel_26381.conf
port 26381
daemonize yes
logfile /var/lib/sentinel_26381.log
sentinel monitor mymaster 127.0.0.1 6379 2

并启动以上三个哨兵

sudo redis-sentinel /etc/redis/sentinel_26379.conf
sudo redis-sentinel /etc/redis/sentinel_26380.conf
sudo redis-sentinel /etc/redis/sentinel_26381.conf

查看哨兵运行状态:

在这里插入图片描述

jedis访问:

	@Testpublic void testSentinel(){Set<String> set = new HashSet<String>();set.add("127.0.0.1:26379");set.add("127.0.0.1:26380");set.add("127.0.0.1:26381");JedisSentinelPool sentinelPool = new JedisSentinelPool("mymaster", set);Jedis jedis = sentinelPool.getResource();jedis.set("name","bo");System.out.println(jedis.get("name"));}

运行输出:

bo

6,集群

部署redis cluster集群

redis cluster集群的部署详细介绍参考:吃透Redis系列(八):集群详细介绍

部署集群,其中7000,7001,7002是三个主节点

8000,8001,8002是三个从节点

搭建完成之后,查看当前集群节点状态:

在这里插入图片描述

Jedis访问集群:

	@Testpublic void testCluster(){Set<HostAndPort> set = new HashSet<HostAndPort>();set.add(new HostAndPort("127.0.0.1",7000));set.add(new HostAndPort("127.0.0.1",7001));set.add(new HostAndPort("127.0.0.1",7002));JedisCluster jedisCluster = new JedisCluster(set);jedisCluster.set("name","helloword");System.out.println(jedisCluster.get("name"));}

运行输出:

helloword

二,Lettuce使用

Lettuce是一个高性能基于Java编写的Redis驱动框架,底层集成了Project Reactor提供天然的反应式编程,通信框架集成了Netty使用了非阻塞IO,5.x版本之后融合了JDK1.8的异步编程特性,在保证高性能的同时提供了十分丰富易用的API。

Lettuce使用的时候依赖于四个主要组件:

  • RedisURI:连接信息。
  • RedisClientRedis客户端,特殊地,集群连接有一个定制的RedisClusterClient
  • ConnectionRedis连接,主要是StatefulConnection或者StatefulRedisConnection的子类,连接的类型主要由连接的具体方式(单机、哨兵、集群、订阅发布等等)选定,比较重要。
  • RedisCommandsRedis命令API接口,基本上覆盖了Redis发行版本的所有命令,提供了同步(sync)、异步(async)、反应式(reative)的调用方式,对于使用者而言,会经常跟RedisCommands系列接口打交道。

使用到的软件版本:Java 1.8.0_191、Redis 6.0.6、lettuce 5.3.1.RELEASE。

Lettuce内容参考连接:https://www.cnblogs.com/throwable/p/11601538.html#lettuce

1,Lettuce简单使用

导入maven包

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

基本使用

	@Testpublic void testSetGet() {// 连接信息RedisURI redisURI = RedisURI.builder().withHost("127.0.0.1").withPort(6379).withTimeout(Duration.of(10, ChronoUnit.SECONDS)).build();// redis客户端RedisClient redisClient = RedisClient.create(redisURI);// 创建redis连接StatefulRedisConnection<String, String> connect = redisClient.connect();// 同步调用方式RedisCommands<String, String> redisCommands = connect.sync();// set参数,设置5秒过期时间SetArgs setArgs = SetArgs.Builder.nx().ex(5);// 设置key,valueredisCommands.set("name", "bobo", setArgs);// 获取name的值System.out.println(redisCommands.get("name"));// 关闭连接connect.close();// 关闭客户端redisClient.shutdown();}

运行输出:

bobo

2,API

Lettuce主要提供三种API

  • 同步(sync):RedisCommands
  • 异步(async):RedisAsyncCommands
  • 反应式(reactive):RedisReactiveCommands

先准备好一个单机Redis连接备用:

	// redis客户端private RedisClient redisClient;// redis连接private StatefulRedisConnection<String, String> connection;@Beforepublic void before() {RedisURI redisURI = RedisURI.builder().withHost("127.0.0.1").withPort(6379).withTimeout(Duration.of(10, ChronoUnit.SECONDS)).build();// redis客户端redisClient = RedisClient.create(redisURI);// 创建redis连接connection = redisClient.connect();}@Afterpublic void after() {connection.close();redisClient.shutdown();}

Redis命令API的具体实现可以直接从StatefulRedisConnection实例获取,见其接口定义:

public interface StatefulRedisConnection<K, V> extends StatefulConnection<K, V> {boolean isMulti();RedisCommands<K, V> sync();RedisAsyncCommands<K, V> async();RedisReactiveCommands<K, V> reactive();
}    

值得注意的是,在不指定编码解码器RedisCodec的前提下,RedisClient创建的StatefulRedisConnection实例一般是泛型实例StatefulRedisConnection<String,String>,也就是所有命令APIKEYVALUE都是String类型,这种使用方式能满足大部分的使用场景。当然,必要的时候可以定制编码解码器RedisCodec<K,V>

2.1,同步API

同步API在所有命令调用之后会立即返回结果。和上一篇介绍的Jedis差不多,RedisCommands的用法其实和它相差不大。

	@Testpublic void testSync(){RedisCommands<String, String> redisCommands = connection.sync();redisCommands.set("name","bobo");System.out.println(redisCommands.get("name"));}
2.2,异步API
	@Testpublic void testAsync() throws ExecutionException, InterruptedException {RedisAsyncCommands<String, String> redisAsyncCommands = connection.async();RedisFuture<String> redisFuture = redisAsyncCommands.set("name", "bobo");System.out.println(redisFuture.get());System.out.println("get2:" + redisAsyncCommands.get("name").get());}

RedisAsyncCommands所有方法执行返回结果都是RedisFuture实例,而RedisFuture接口的定义如下:

public interface RedisFuture<V> extends CompletionStage<V>, Future<V> {String getError();boolean await(long timeout, TimeUnit unit) throws InterruptedException;
}    

也就是,RedisFuture可以无缝使用Future或者JDK1.8中引入的CompletableFuture提供的方法。

2.3,反应式API

Lettuce引入的反应式编程框架是Project Reactor,如果没有反应式编程经验可以先自行了解一下Project Reactor

构建RedisReactiveCommands实例:

	@Testpublic void testReactive(){RedisReactiveCommands<String, String> redisReactiveCommands = connection.reactive();redisReactiveCommands.set("name", "bobo");System.out.println(redisReactiveCommands.get("name"));redisReactiveCommands.sadd("fruit","apple","banana");System.out.println(redisReactiveCommands.smembers("fruit"));}

运行输出:

MonoFromPublisher
FluxSource

RedisReactiveCommands的方法如果返回的结果只包含0或1个元素,那么返回值类型是Mono,如果返回的结果包含0到N(N大于0)个元素,那么返回值是Flux

3,发布/订阅

非集群模式下的发布订阅依赖于定制的连接StatefulRedisPubSubConnection,集群模式下的发布订阅依赖于定制的连接StatefulRedisClusterPubSubConnection,两者分别来源于RedisClient#connectPubSub()系列方法和RedisClusterClient#connectPubSub()

非集群模式:

// 可能是单机、普通主从、哨兵等非集群模式的客户端
RedisClient client = ...
StatefulRedisPubSubConnection<String, String> connection = client.connectPubSub();
connection.addListener(new RedisPubSubListener<String, String>() { ... });// 同步命令
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 -> {...}).subscribe()

集群模式:

// 使用方式其实和非集群模式基本一致
RedisClusterClient clusterClient = ...
StatefulRedisClusterPubSubConnection<String, String> connection = clusterClient.connectPubSub();
connection.addListener(new RedisPubSubListener<String, String>() { ... });
RedisPubSubCommands<String, String> sync = connection.sync();
sync.subscribe("channel");
// ...

4,管道

RedisPipeline也就是管道机制可以理解为把多个命令打包在一次请求发送到Redis服务端,然后Redis服务端把所有的响应结果打包好一次性返回,从而节省不必要的网络资源(最主要是减少网络请求次数)。Redis对于Pipeline机制如何实现并没有明确的规定,也没有提供特殊的命令支持Pipeline机制。Jedis中底层采用BIO(阻塞IO)通讯,所以它的做法是客户端缓存将要发送的命令,最后需要触发然后同步发送一个巨大的命令列表包,再接收和解析一个巨大的响应列表包。

PipelineLettuce中对使用者是透明的,由于底层的通讯框架是Netty,所以网络通讯层面的优化Lettuce不需要过多干预,换言之可以这样理解:NettyLettuce从底层实现了RedisPipeline机制。

5,事物

事务相关的命令就是WATCHUNWATCHEXECMULTIDISCARD,在RedisCommands系列接口中有对应的方法。举个例子:

	@Testpublic void testMulti() {RedisCommands<String, String> redisCommands = connection.sync();redisCommands.multi();redisCommands.set("name", "bobo");redisCommands.set("age", "18");redisCommands.exec();System.out.println(redisCommands.get("name"));System.out.println(redisCommands.get("age"));}

运行输出:

bobo
18

6,普通主从模式

假设现在有三个Redis服务形成树状主从关系如下:

  • 节点一:localhost:6379,角色为Master。
  • 节点二:localhost:6380,角色为Slavor,节点一的从节点。
  • 节点三:localhost:6381,角色为Slavor,节点二的从节点。
@Test
public void testStaticReplica() throws Exception {List<RedisURI> uris = new ArrayList<>();RedisURI uri1 = RedisURI.builder().withHost("localhost").withPort(6379).build();RedisURI uri2 = RedisURI.builder().withHost("localhost").withPort(6380).build();RedisURI uri3 = RedisURI.builder().withHost("localhost").withPort(6381).build();uris.add(uri1);uris.add(uri2);uris.add(uri3);RedisClient redisClient = RedisClient.create();StatefulRedisMasterSlaveConnection<String, String> connection = MasterSlave.connect(redisClient,new Utf8StringCodec(), uris);// 只从主节点读取数据connection.setReadFrom(ReadFrom.MASTER);// 执行其他Redis命令connection.close();redisClient.shutdown();
}

7,哨兵

由于Lettuce自身提供了哨兵的拓扑发现机制,所以只需要随便配置一个哨兵节点的RedisURI实例即可:

@Test
public void testDynamicSentinel() throws Exception {RedisURI redisUri = RedisURI.builder().withPassword("你的密码").withSentinel("localhost", 26379).withSentinelMasterId("哨兵Master的ID").build();RedisClient redisClient = RedisClient.create();StatefulRedisMasterSlaveConnection<String, String> connection = MasterSlave.connect(redisClient, new Utf8StringCodec(), redisUri);// 只允许从从节点读取数据connection.setReadFrom(ReadFrom.SLAVE);RedisCommands<String, String> command = connection.sync();SetArgs setArgs = SetArgs.Builder.nx().ex(5);command.set("name", "throwable", setArgs);String value = command.get("name");log.info("Get value:{}", value);
}

8,集群

下面的API提供跨槽位(Slot)调用的功能

  • RedisAdvancedClusterCommands
  • RedisAdvancedClusterAsyncCommands
  • RedisAdvancedClusterReactiveCommands

静态节点选择功能:

  • masters:选择所有主节点执行命令。
  • slaves:选择所有从节点执行命令,其实就是只读模式。
  • all nodes:命令可以在所有节点执行。

集群拓扑视图动态更新功能:

  • 手动更新,主动调用RedisClusterClient#reloadPartitions()
  • 后台定时更新。
  • 自适应更新,基于连接断开和MOVED/ASK命令重定向自动更新。

简单的集群连接和使用方式如下:

	@Testpublic void testCluster() {Set<RedisURI> redisURISet = new HashSet<>();redisURISet.add(RedisURI.builder().withHost("127.0.0.1").withPort(7000).build());redisURISet.add(RedisURI.builder().withHost("127.0.0.1").withPort(7001).build());redisURISet.add(RedisURI.builder().withHost("127.0.0.1").withPort(7002).build());RedisClusterClient redisClusterClient = RedisClusterClient.create(redisURISet);StatefulRedisClusterConnection<String, String> connect = redisClusterClient.connect();RedisAdvancedClusterCommands<String, String> clusterCommands = connect.sync();clusterCommands.set("name","bobo");System.out.println(clusterCommands.get("name"));}

集群节点信息如下:

在这里插入图片描述

这篇关于吃透Redis系列(十一):Jedis和Lettuce客户端详细介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1131644

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

图神经网络模型介绍(1)

我们将图神经网络分为基于谱域的模型和基于空域的模型,并按照发展顺序详解每个类别中的重要模型。 1.1基于谱域的图神经网络         谱域上的图卷积在图学习迈向深度学习的发展历程中起到了关键的作用。本节主要介绍三个具有代表性的谱域图神经网络:谱图卷积网络、切比雪夫网络和图卷积网络。 (1)谱图卷积网络 卷积定理:函数卷积的傅里叶变换是函数傅里叶变换的乘积,即F{f*g}

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0