Redis 学习笔记 2:Java 客户端

Redis 学习笔记 2:Java 客户端

常见的 Redis Java 客户端有三种:

  • Jedis,优点是API 风格与 Redis 命令命名保持一致,容易上手,缺点是连接实例是线程不安全的,多线程场景需要用线程池来管理连接。
  • Redisson,在Redis基础上实现了分布式的可伸缩的java数据结构,例如Map、Queue等,而且支持跨进程的同步机制:Lock、Semaphore等待,比较适合用来实现特殊的功能需求。
  • lettuce,基于 Netty 实现,支持同步/异步和响应式编程,并且是线程安全的。支持 Redis 的哨兵模式、集群模式和管道模式。

Spring 对 Jedis 和 lettuce 进行了封装,spring-data-redis 提供统一的 API 进行操作。

Jedis

单个连接

下面是一个简单的 Jedis 连接示例。

创建一个 mvn 工程,并添加 Jedis 和 Junit 依赖:

<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.10.0</version><scope>test</scope>
</dependency>
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>5.0.0</version>
</dependency>

编写一个单元测试:

public class AppTests {private Jedis jedis;@BeforeEachpublic void beforeEach() {jedis = new Jedis("192.168.0.88", 6379);jedis.auth("123321");jedis.select(0);}@Testpublic void testString() {String res = jedis.set("name", "Jack");System.out.println(res);res = jedis.get("name");System.out.println(res);}@Testpublic void testHash() {jedis.hset("user:1", "name", "Jack");jedis.hset("user:1", "age", "18");Map<String, String> map = jedis.hgetAll("user:1");System.out.println(map);}@AfterEachpublic void afterEach() {if (jedis != null) {jedis.close();}}
}

在这个单元测试中,展示了如何使用 Jedis 客户端连接 Redis,并用 API 操作 String 类型和 Hash 类型的数据。基本上,这些 API 的命名和使用方式与前文介绍的 Redis 命令是相似的。

连接池

创建一个 Jedis 连接池的工具类:

public class JedisConnectionFactory {// Jedis 连接池private static JedisPool jedisPool;// 初始化 Jedis 连接池static {// 设置连接池配置JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();// 最大连接数jedisPoolConfig.setMaxTotal(8);// 最大空闲连接数jedisPoolConfig.setMaxIdle(8);// 最小空闲连接数jedisPoolConfig.setMinIdle(0);// 尝试从连接池中获取空闲连接时的等待时间(如果没有空闲连接),超时会产生错误jedisPoolConfig.setMaxWait(Duration.ofSeconds(5));// 创建连接池jedisPool = new JedisPool(jedisPoolConfig,"192.168.0.88", 6379, 1000, "123321");}/*** 返回一个空闲的 Redis 连接实例* @return Redis 连接实例*/public static Jedis getJedisConnection() {return jedisPool.getResource();}
}

之前的 Jedis 测试用例修改为使用连接池的版本:

@BeforeEach
public void beforeEach() {jedis = JedisConnectionFactory.getJedisConnection();jedis.auth("123321");jedis.select(0);
}

spring-data-redis

SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,官网地址:https://spring.io/projects/spring-data-redis

spring-data-redis 包含以下特性:

  • 提供了对不同Redis客户端的整合(Lettuce和Jedis)
  • 提供了RedisTemplate统一API来操作Redis
  • 支持Redis的发布订阅模型
  • 支持Redis哨兵和Redis集群
  • 支持基于Lettuce的响应式编程
  • 支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化
  • 支持基于Redis的JDKCollection实现

SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:

image-20240128141547857

示例

创建一个 Spring 项目,并添加以下依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId>
</dependency>

这里的commons-pool2是一个连接池依赖。

appication.yml中添加以下配置:

spring:data:redis:host: 192.168.0.88port: 6379password: 123321database: 0 # 默认连接的数据库lettuce:pool:max-active: 8max-idle: 8min-idle: 0max-wait: 100ms

注意,因为 Spring-data-redis 默认使用 lettuce 作为底层的 Redis 客户端,所以这里配置的是 lettuce 的连接池。

单元测试:

@SpringBootTest
class SpringDataRedisDemoApplicationTests {@Autowiredprivate RedisTemplate redisTemplate;@Testvoid testString() {ValueOperations ops = redisTemplate.opsForValue();ops.set("name", "王二");String val = (String) ops.get("name");System.out.println(val);}
}

这里注入RedisTemplate实例,并用它实现对 Redis 的操作。

序列化和反序列化

Redis 本身只能处理字符串形式的 Key 和 Value,而 RedisTemplate 默认设置的 Key 和 Value 可以是 Object 类型,因此 RedisTemplate 底层实现了 Object 的序列化和反序列化,这些序列化和反序列化的实现是由RedisTemplate 中的四个属性决定的:

@Nullable
private RedisSerializer keySerializer = null;
@Nullable
private RedisSerializer valueSerializer = null;
@Nullable
private RedisSerializer hashKeySerializer = null;
@Nullable
private RedisSerializer hashValueSerializer = null;

默认情况下,这些序列化和反序列化的实现都是基于 JDK 的对象流实现的:

public class DefaultSerializer implements Serializer<Object> {public DefaultSerializer() {}public void serialize(Object object, OutputStream outputStream) throws IOException {if (!(object instanceof Serializable)) {String var10002 = this.getClass().getSimpleName();throw new IllegalArgumentException(var10002 + " requires a Serializable payload but received an object of type [" + object.getClass().getName() + "]");} else {ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);objectOutputStream.writeObject(object);objectOutputStream.flush();}}
}

因此,之前的示例中虽然在代码中是设置了一个 Key 为name的键值对,但实际上在 Redis 服务器上创建的是一个\xac\xed\x00\x05t\x00\x04name这样的键,一般来说我们是不能接受的。

因此我们需要自己定义一个使用特定序列化实现的RedisTemplate,而不是使用默认实现:

@Configuration
public class WebConfig {@BeanRedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(redisConnectionFactory);GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();redisTemplate.setKeySerializer(RedisSerializer.string());redisTemplate.setValueSerializer(jsonRedisSerializer);redisTemplate.setHashKeySerializer(RedisSerializer.string());redisTemplate.setHashValueSerializer(jsonRedisSerializer);return redisTemplate;}
}

这里对RedisTemplate使用了类型参数,因为一般而言,key 和 HashKey 都是 String 类型的。在这种情况下他们都只需要使用RedisSerializer.string()进行序列化和反序列化,这个序列化器实际上就是将字符串按照 UTF-8 编码转换为字节(或者相反)。对于 Value 和 HashValue,这里使用 Jackson 将其转换为 JSON 字符串(或者相反)。

因为这里需要使用 Jackson,所以需要添加相应的依赖:

<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.16.0</version>
</dependency>

一般的 Spring 项目不需要额外引入,因为 spring-mvc 默认包含 Jackson 依赖。

重新编写测试用例,使用类型参数:

@SpringBootTest
class SpringDataRedisDemoApplicationTests {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Testvoid testString() {ValueOperations<String, Object> ops = redisTemplate.opsForValue();ops.set("name", "王二");String val = (String) ops.get("name");System.out.println(val);ops.set("user:2", new User("Jack", 18));User user = (User) ops.get("user:2");System.out.println(user);}
}

这里使用了一个自定义的 POJO 类:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {private String name;private Integer age;
}

需要添加 Lombok 依赖。

现在在 Redis 服务器上就能看到 Key 为user:2,值为 JSON 串的键值对:

{"@class": "cn.icexmoon.springdataredisdemo.pojo.User","name": "Jack","age": 18
}

可以看到,Value 中包含类的完整包名,这也是为什么可以反序列化出具体类型的对象。

StringRedisTemplate

上面的方案虽然可以很好的解决序列化和反序列化的问题,但有一个缺点:Value 中包含完整类名,占用 Redis 的存储空间。如果不希望 Redis 的 Value 中包含完整类名占用额外空间,就需要手动序列化和反序列化:这样我们只需要向 RedisTemplate 中传入 String 类型的 Key 和 Value,此时我们可以使用一个更简单的类型——StringRedisTemplate:

public class Tests2 {@Autowiredprivate StringRedisTemplate redisTemplate;private static final ObjectMapper mapper = new ObjectMapper();@Testpublic void test() throws JsonProcessingException {ValueOperations<String, String> ops = redisTemplate.opsForValue();User user = new User("Jack", 18);String jsonUser = mapper.writeValueAsString(user);ops.set("user:3", jsonUser);jsonUser = ops.get("user:3");user = mapper.readValue(jsonUser, User.class);System.out.println(user);}
}

此时user:3中的 Value:

{"name": "Jack","age": 18
}

本文的完整示例代码可以从这里获取。

参考资料

  • 黑马程序员Redis入门到实战教程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/653710.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么是JDBC?JDBC的作用是什么?

什么是JDBC&#xff1f;JDBC的作用是什么&#xff1f; JDBC是Java数据库连接&#xff08;Java Database Connectivity&#xff09;的简称&#xff0c;它是一套用于执行SQL语句的Java API。 JDBC的作用在于为Java程序提供了一个标准化的接口&#xff0c;用于访问和操作各种关系…

一文搞懂设计模式—策略模式

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 微信公众号&#xff1a;Java随想录 文章目录 使用场景策略模式实现策略模式的优缺点策略模式优化使用Map取消 Context 类策略枚举解决策略类膨胀SpringBoot中的策略模式 总结 在软件开发中&#xff0c;经常会…

pcl应用八叉树实例

pcl应用八叉树实例 文章目录 pcl应用八叉树实例1、基本概念2、基于八叉树的空间划分及搜索操作2.1、关键函数说明2.1.2 OctreePointCloudSearch 类2.1.2 voxelSearch 函数 3、无序点云数据集的空间变化检测 1、基本概念 八叉树结构通过循环递归的划分方法对大小为2 n ∗ 2 n ∗…

C++面试宝典第25题:阶乘末尾零的个数

题目 给定一个整数n,返回n!(n的阶乘)结果尾数中零的个数。 示例 1: 输入:3 输出:0 解释:3! = 6,尾数中没有零。 示例 2: 输入:5 输出:1 解释:5! = 120,尾数中有1个零。 解析 这道题主要考察应聘者对于数学问题的分析和理解能力,以及在多个解决方案中,寻求最优…

elementUI的el-select传递item对象或其他参数的2种方法

方法1 :value“item” 绑定对象 只要:value绑定item对象就可以 value-key"value" 必须是item里的一个属性&#xff0c;绑定值为对象类型时必填 <el-select v-model"value" placeholder"请选择" value-key"value" change"cha…

Qt/QML编程之路:qml通过C++传递变量给另一个qml(42)

Qml的项目设计时,不可避免涉及到qml之间的调用,此时很正常想象到的是如何进行事件传输和参数传输。一般就是在qml文件中定义property int, 或者定义 signal mySignal。 我就遇到了这么一种情况,需要把一个image的source,也就是图片的路径信息,从设置界面直接传递到要设置背…

[EFI]戴尔T5810电脑 Hackintosh 黑苹果efi引导文件

硬件型号驱动情况主板 戴尔T5810&#xff0c;C610/612芯片 处理器英特尔至强E5-2620 v3已驱动内存12 GB已驱动硬盘500GB WD Blue Solid State Drive & 2TB Seagate Mobile Hard Drive (Upgraded)已驱动显卡RX 570 4Gb已驱动声卡瑞昱 Realtek ALC662 英特尔 High Definitio…

(蓝桥杯每日一题)求最长回文串

问题描述 给出一个长度为 n 的小写字符串&#xff0c;求一个最长的子串 S&#xff0c;满足SXY,X&#xff0c;Y>1&#xff0c;且X,Y 均为回文串。 输入格式 输入包括一行: 第一行是一个长度为 n 的小写字符串。 输出格式 输出包括一行&#xff1a; 一行一个整数&#xff0c;表…

Uboot中ARMV7和ARMV8 MMU配置

问题概述 Uboot中如果打开MMU&#xff0c;则MMU需要配置MMU table来管理不同的地址空间。 其中ARMV7和ARMV8中这部分的配置代码是不同。 ARMV7的配置过程 代码参考&#xff1a;u-boot-2020.04/arch/arm/lib/cache-cp15.c mmu_setup →set_section_dcache →dram_bank_mmu_…

【GPU驱动开发】-Mesa ST和GLSL编译器衔接交互分析

前言 不必害怕未知&#xff0c;无需恐惧犯错&#xff0c;做一个Creator&#xff01; &#xff08;基于Mesa 22.2.5版本&#xff09; Mesa State Tracker 与 GLSL 编译器的协同工作是 Mesa 3D 图形渲染管线中的关键环节。这两者的衔接确保了 OpenGL API 调用能够正确、高效地…

Shell脚本转发activemg topic消息到另个activemg服务器

如果你想通过Shell脚本将ActiveMQ中的Topic消息从一个服务器转发到另一个服务器&#xff0c;你可以使用stomp命令行工具来实现。 以下是一个示例脚本&#xff0c;演示如何使用Shell脚本将ActiveMQ中的Topic消息从一个服务器转发到另一个服务器&#xff1a; #!/bin/bash# 源Act…

【论文阅读】Membership Inference Attacks Against Machine Learning Models

基于confidence vector的MIA Machine Learning as a Service简单介绍什么是Membership Inference Attacks&#xff08;MIA&#xff09;攻击实现过程DatasetShadow trainingTrain attack model Machine Learning as a Service简单介绍 机器学习即服务&#xff08;Machine Learn…

Material Components for Android助你打造精美App

Material Components for Android助你打造精美App 简介 Material Components for Android (MDC-Android) 是帮助开发者执行 Material Design 的工具。由谷歌的核心工程师和用户体验设计师团队开发&#xff0c;这些组件使得开发者可以可靠地开发工作流来构建美观且功能齐全的 …

Spring Security 的TokenStore三种实现方式

博主介绍&#xff1a;✌专注于前后端领域开发的优质创作者、秉着互联网精神开源贡献精神&#xff0c;答疑解惑、坚持优质作品共享。本人是掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战&#xff0c;深受全网粉丝喜爱与支持✌有需要可以联系作者我哦&…

期权定价模型系列[9]SABR模型

1.简介 SABR模型是由 Hagan在 2002年提出的一种随机波动率模型&#xff0c;在抛弃了原始的BSM 模型中对于波动率为某一常数的假定&#xff0c;假设隐含波动率同样是符合几何布朗运动的&#xff0c;并且将隐含波动率设定为标的价格和合约行权价的函数&#xff0c;结合了隐含波动…

12.5内存操作流(血干JAVA系列)

12.5内存操作流 12.5内存操作流ByteArraylnputStream类的主要方法ByteArrayOutputStream 类的主要方法【例12.33】使用内存操作流完成一个大写字母转换为小写字母的程序 12.5内存操作流 在 流 的 操 作 中 除 了 进 行 文 件 的 输 入 与 输 出 操 作 之 外 &#xff0c; 也 可…

flask初体验

1、定义 Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务。 中文官网 2、初步上手 1、安装flask pip3 install flask 2、创建flask应用 # -*- coding = utf-8 -*- # @Time : 2024/1/28 23:02 # @Author: Frank # @File: main.py…

说说你对vue的mixin的理解,有什么应用场景

mixin是什么 Vue中的mixin 局部混入全局混入注意事项: 使用场景源码分析Vue 的几种类型的合并策略 替换型合并型队列性叠加型小结 此文章&#xff0c;来源于印客学院的资料&#xff0c;这里只是分享&#xff0c;便于查漏补缺。 mixin是什么 Mixin 是 面向对象程序设计语言中…

回归预测 | MATLAB实现PSO-GRNN粒子群优化广义回归神经网络多输入单输出预测(含优化前后预测可视化)

回归预测 | MATLAB实现PSO-GRNN粒子群优化广义回归神经网络多输入单输出预测 目录 回归预测 | MATLAB实现PSO-GRNN粒子群优化广义回归神经网络多输入单输出预测预测效果基本介绍程序设计参考资料预测效果 <

系统架构设计师-21年-上午试题

系统架构设计师-21年-上午试题 更多软考资料 https://ruankao.blog.csdn.net/ 1 ~ 10 1 前趋图(Precedence Graph)是一个有向无环图&#xff0c;记为:→{(Pi,Pj)|Pi must complete before Pj may strat}&#xff0c;假设系统中进程P{P1&#xff0c;P2&#xff0c;P3&#xf…