当前位置: 首页 > news >正文

SpringBoot使用Redis(事务异步add + 更新)

1,简单介绍redis

Redis(Remote Dictionary Server)是一个开源的内存中数据结构存储系统。

主要特点:

  • 内存存储: Redis 主要将数据存储在内存中,因此读写速度非常快,适合需要高性能的应用场景。

  • 持久化: Redis 支持将内存中的数据持久化到磁盘,可以通过RDB快照(Snapshotting)或者日志(Append-only file, AOF)的方式实现数据持久化,保证数据在服务重启后不丢失。

  • 数据结构多样性: Redis 不仅仅是键值存储(key-value store),还支持多种复杂的数据结构,每种数据结构都有专门的操作命令,使得开发者可以根据需求选择合适的数据结构存储数据。

  • 原子操作: Redis 提供的命令支持原子操作,可以确保复杂操作的原子性,避免并发问题。

  • 分布式: Redis 提供了主从复制(Replication)和分片(Sharding)等功能,支持构建高可用、高扩展性的分布式系统。

  • 高性能: Redis 使用单线程模型来处理请求,避免了多线程间的锁竞争和上下文切换开销,因此能够达到很高的单实例性能。

  • 丰富的扩展功能: Redis 提供了许多扩展功能,如事务(Transaction)、发布订阅(Pub/Sub)、Lua 脚本扩展、过期自动删除等,使其在不同场景下有更广泛的应用。

2,如何在实际项目中使用

正常项目中使用思路

  • 使用redis目的是为了提高我们接口响应速度,所以经常用于查询的数据可以用redis
  • 必须考虑保证redis与数据库的数据一致性
  • 作为辅助提升响应的手段,redis故障不能影响服务自身的业务,也就是不能导致服务故障
  • 主要是为了获取而用,所以redis数据的add,delete等操作应该由多线程进行

redis与数据库的数据一致性,后面会有出别的文章。这里主要展示如何使用

引入依赖

根据自己springBoot的版本选择合适的版本

 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.5.4</version></dependency><!-- 使用连接池进行管理redis --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.11.1</version></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.8.0</version></dependency>

编写配置文件

# redis 连接信息
spring.redis.host=127.0.0.1
spring.redis.port=6379
# 这里为了信息安全使用了加密
spring.redis.password={decrypt}cm9qZXIxMjM0
# redis 连接池的配置信息
spring.redis.jedis.pool.max-active=10
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.min-idle=2
spring.redis.jedis.pool.max-wait=1500ms

关于如何在springboot中使用配置文件的加解密请查看

SpringBoot启动自动解密加密配置项_springboot environment 配置项解密-CSDN博客

编写config类

package com.luojie.config;import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;import java.time.Duration;@Configuration
public class RedisConfig {@Value("${spring.redis.host}")private String redisHost;@Value("${spring.redis.port}")private int redisPort;@Value("${spring.redis.password}")private String redisPassword;@Value("${spring.redis.jedis.pool.max-active}")private int maxTotal;@Value("${spring.redis.jedis.pool.max-idle}")private int maxIdle;@Value("${spring.redis.jedis.pool.min-idle}")private int minIdle;@Value("${spring.redis.jedis.pool.max-wait}")private Duration maxWaitMillis;/*** Redis 连接工厂配置* @return*/@Beanpublic RedisConnectionFactory redisConnectionFactory() {if (redisHost.contains(",")) {// 配置集群 Redis 连接工厂return clusterConnectionFactory();} else {//  配置单机 Redis 连接工厂return standaloneConnectionFactory();}}/*** 当redis为单机情况时* @return*/private RedisConnectionFactory standaloneConnectionFactory() {// RedisStandaloneConfiguration 设置单机 Redis 配置RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();config.setHostName(redisHost);config.setPort(redisPort);config.setPassword(redisPassword);// JedisClientConfiguration 配置 Jedis 连接JedisClientConfiguration.JedisClientConfigurationBuilder jedisClientConfiguration = JedisClientConfiguration.builder();jedisClientConfiguration.connectTimeout(Duration.ofMillis(3500)); // connection timeout// JedisPoolConfig 配置连接池参数JedisPoolConfig poolConfig = new JedisPoolConfig();poolConfig.setMaxTotal(maxTotal);poolConfig.setMaxIdle(maxIdle);poolConfig.setMinIdle(minIdle);poolConfig.setMaxWaitMillis(maxWaitMillis.toMillis());jedisClientConfiguration.usePooling().poolConfig(poolConfig);// 使用 JedisConnectionFactory 创建连接工厂return new JedisConnectionFactory(config, jedisClientConfiguration.build());}private RedisConnectionFactory clusterConnectionFactory() {// RedisClusterConfiguration 设置集群 Redis 配置RedisClusterConfiguration config = new RedisClusterConfiguration();for (String node : redisHost.split(",")) {String[] parts = node.split(":");config.clusterNode(parts[0], Integer.parseInt(parts[1]));}config.setPassword(redisPassword);// JedisClientConfiguration 配置 Jedis 连接JedisClientConfiguration.JedisClientConfigurationBuilder jedisClientConfiguration = JedisClientConfiguration.builder();jedisClientConfiguration.connectTimeout(Duration.ofMillis(3500)); // connection timeout// JedisPoolConfig 配置连接池参数JedisPoolConfig poolConfig = new JedisPoolConfig();poolConfig.setMaxTotal(maxTotal);poolConfig.setMaxIdle(maxIdle);poolConfig.setMinIdle(minIdle);poolConfig.setMaxWaitMillis(maxWaitMillis.toMillis());jedisClientConfiguration.usePooling().poolConfig(poolConfig);// 使用 JedisConnectionFactory 创建连接工厂return new JedisConnectionFactory(config, jedisClientConfiguration.build());}@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);// 使用StringRedisSerializer序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setHashKeySerializer(new StringRedisSerializer());// 使用GenericJackson2JsonRedisSerializer序列化和反序列化redis的value值template.setValueSerializer(new GenericJackson2JsonRedisSerializer());template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());template.afterPropertiesSet();return template;}
}

编写redisTemplateUtil工具类

package com.luojie.util;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;/*** redis 工具类*/
@Component
@Slf4j
public class RedisServiceUtil {@Autowiredprivate RedisTemplate redisTemplate;/*** 普通string类型,设置值** @param key* @param value*/public void set(String key, String value) {try {redisTemplate.opsForValue().set(key, value);} catch (Exception e) {log.error("fail set redis key:{},value:{}, errorMsg:{}", key, value, e.getMessage());}}/*** 普通string类型,设置值并设置超时时间** @param key* @param value*/public void setWithExpire(String key, String value, int time, TimeUnit timeUnit) {try {redisTemplate.opsForValue().set(key, value, time, timeUnit);} catch (Exception e) {log.error("fail set redis key with expire:{},value:{}, errorMsg:{}", key,value, e.getMessage());}}/*** 普通string类型,获取值** @param key*/public String get(String key) {try {return (String) redisTemplate.opsForValue().get(key);} catch (Exception e) {log.error("fail get redis key:{}, errorMsg:{}", key, e.getMessage());}return null;}/*** 普通string类型,删除key** @param key*/public void delete(String key) {try {redisTemplate.delete(key);} catch (Exception e) {log.error("fail delete redis key:{}, errorMsg:{}", key, e.getMessage());}}/*** 普通string类型,判断key是否存在** @param key*/public boolean exists(String key) {try {return redisTemplate.hasKey(key);} catch (Exception e) {log.error("fail exists redis key:{}, errorMsg:{}", key, e.getMessage());}return false;}/*** 普通string类型,为某个key单独设置超时时间** @param key*/public void expire(String key, int seconds, TimeUnit timeUnit) {try {redisTemplate.expire(key, seconds, timeUnit);} catch (Exception e) {log.error("fail expire redis key:{}, errorMsg:{}", key, e.getMessage());}}/*** 普通string类型,获取key的超时时间** @param key*/public Long getExpire(String key) {try {return redisTemplate.getExpire(key);} catch (Exception e) {log.error("fail getExpire redis key:{}, errorMsg:{}", key, e.getMessage());}return null;}/*** hash类型,设置值** @param key* @param value*/public void setHash(String key, String field, String value) {try {redisTemplate.opsForHash().put(key, field, value);} catch (Exception e) {log.error("fail setHash redis key:{}, errorMsg:{}", key, e.getMessage());}}/*** hash类型,设置值** @param key* @param value*/public void setHashWithExpire(String key, String field, String value, int time, TimeUnit timeUnit) {try {redisTemplate.opsForHash().put(key, field, value);redisTemplate.expire(key, time, timeUnit);} catch (Exception e) {log.error("fail setHash with expire redis key:{}, errorMsg:{}", key, e.getMessage());}}/*** hash类型,获取值** @param key*/public String getHash(String key, String field) {try {return (String) redisTemplate.opsForHash().get(key, field);} catch (Exception e) {log.error("fail getHash redis key:{}, errorMsg:{}", key, e.getMessage());}return null;}/*** hash类型,删除** @param key*/public void deleteHash(String key, String field) {try {redisTemplate.opsForHash().delete(key, field);} catch (Exception e) {log.error("fail deleteHash redis key:{}, errorMsg:{}", key, e.getMessage());}}
}

------往上为基本功能线------

根据上面的,直接使用controller进行访问调用已经可以用了。往下这里做一下功能升级。

主要用到异步 + event

如果想详细了解异步,了解event监听器请参考

Spring的监听器使用(实用,直接拿去修改可用)-CSDN博客

创建数据module类

package com.luojie.moudle;import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;import java.util.concurrent.TimeUnit;@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@NoArgsConstructor
public class RedisEventModule {@NonNullprivate String key;private String value;private String field;private int timeout;private TimeUnit timeoutUnit;
}

创建event类

BaseRedisEvent
package com.luojie.event;import org.springframework.context.ApplicationEvent;public abstract class BaseRedisEvent extends ApplicationEvent {public BaseRedisEvent(Object source) {super(source);}
}
RedisAddEvent
package com.luojie.event;import com.luojie.moudle.RedisEventModule;
import org.apache.commons.lang3.StringUtils;public class RedisAddEvent extends BaseRedisEvent {public RedisEventModule eventModule;public RedisAddEvent(RedisEventModule source) {super(source);if (StringUtils.isBlank(source.getValue())) {throw new IllegalArgumentException("redis value can't be empty");}this.eventModule = source;}
}
RedisDeleteEvent
package com.luojie.event;import com.luojie.moudle.RedisEventModule;public class RedisDeleteEvent extends BaseRedisEvent{public RedisEventModule eventModule;public RedisDeleteEvent(RedisEventModule source) {super(source);this.eventModule = source;}
}

创建监听类

package com.luojie.config.eventListener;import com.luojie.event.BaseRedisEvent;
import com.luojie.event.RedisAddEvent;
import com.luojie.event.RedisDeleteEvent;
import com.luojie.moudle.RedisEventModule;
import com.luojie.util.RedisServiceUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;@Component
@Slf4j
public class RedisListener implements ApplicationListener<BaseRedisEvent> {@Autowiredprivate RedisServiceUtil redisServiceUtil;@Override@Async("asyncExecutor") // 使用异步线程池进行处理public void onApplicationEvent(BaseRedisEvent redisEvent) {if (redisEvent instanceof RedisAddEvent) {handleAddEvent((RedisAddEvent) redisEvent);} else if (redisEvent instanceof RedisDeleteEvent) {handleDeleteEvent((RedisDeleteEvent) redisEvent);}}private void handleAddEvent(RedisAddEvent redisEvent) {log.info("RedisAddEvent:{}", redisEvent);RedisEventModule module = redisEvent.eventModule;if (StringUtils.isNotBlank(module.getField()) && module.getTimeout() != 0) {redisServiceUtil.setHashWithExpire(module.getKey(), module.getField(), module.getValue(),module.getTimeout(), module.getTimeoutUnit());} else if (StringUtils.isNotBlank(module.getField())) {redisServiceUtil.setHash(module.getKey(), module.getField(), module.getValue());} else if (module.getTimeout() != 0){redisServiceUtil.setWithExpire(module.getKey(), module.getValue(),module.getTimeout(), module.getTimeoutUnit());} else {redisServiceUtil.set(module.getKey(), module.getValue());}}private void handleDeleteEvent(RedisDeleteEvent redisEvent) {log.info("RedisDeleteEvent:{}", redisEvent);RedisEventModule module = redisEvent.eventModule;if (StringUtils.isNotBlank(module.getField()) ) {redisServiceUtil.deleteHash(module.getKey(), module.getField());} else {redisServiceUtil.delete(module.getKey());}}}

异步线程池类

package com.luojie.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;@Configuration
public class AsyncPools {@Bean(name = "asyncExecutor")public Executor getAsyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(10); // 设置核心线程数executor.setMaxPoolSize(20); // 设置最大线程数executor.setQueueCapacity(100); // 设置队列容量executor.setThreadNamePrefix("async-executor-"); // 设置线程名前缀executor.setKeepAliveSeconds(5000);// 设置线程最大等待时间executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());// 设置线程拒绝策略executor.initialize();return executor;}}

3,创建controller测试使用

controller

package com.luojie.controller;import com.luojie.event.RedisAddEvent;
import com.luojie.event.RedisDeleteEvent;
import com.luojie.event.RojerEvent;
import com.luojie.moudle.RedisEventModule;
import com.luojie.util.RedisServiceUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;@RestController
public class RedisTestController {@Autowiredprivate ApplicationEventPublisher eventPublisher;@Autowiredprivate RedisServiceUtil redisServiceUtil;@PostMapping("/redis/test1")public Object redisTest1(@RequestBody RedisEventModule module) throws InterruptedException {// 测试普通stringeventPublisher.publishEvent(new RedisAddEvent(module));// 获取刚存入的valueThread.sleep(3000);String re = redisServiceUtil.get(module.getKey());System.out.println(re);// 测试删除eventPublisher.publishEvent(new RedisDeleteEvent(module));Thread.sleep(3000);System.out.println("删除之后re的值: " + redisServiceUtil.get(module.getKey()));return re;}@PostMapping("/redis/test2")public Object redisTest2(@RequestBody RedisEventModule module) throws InterruptedException {// 测试普通stringeventPublisher.publishEvent(new RedisAddEvent(module));// 获取刚存入的valueThread.sleep(3000);String hash = redisServiceUtil.getHash(module.getKey(), module.getField());System.out.println(hash);eventPublisher.publishEvent(new RedisDeleteEvent(module));Thread.sleep(3000);System.out.println("删除之后re的值: " + redisServiceUtil.getHash(module.getKey(),module.getField()));return hash;}
}

测试字符串存入删除情况

存入值已获取

已存入值删除

测试成功

测试hash测存入和删除

存入后正常获取

存入值删除成功

测试成功

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 常见CSS属性
  • 学习小记-一些Redis小知识
  • 《警世贤文》摘抄:处人篇、受恩篇、宽人篇、听劝篇、劝善篇(多读书、多看报、少吃零食多睡觉)
  • 公司想无偿裁员,同事赖着不走
  • HTML+CSS+JS井字棋(来自动下棋)
  • 408数据结构-图的应用2-最短路径 自学知识点整理
  • RuntimeError: cuDNN error: CUDNN_STATUS_NOT_SUPPORTED.
  • 「Pytorch」roLabelImg 图像异常旋转 bug
  • 详解C#委托与事件
  • docker inspect 如何提取容器的ip和端口 网络信息?
  • 【JavaScript】聊一聊js中的浅拷贝与深拷贝与手写实现
  • 【vue教程】二. Vue特性原理详解
  • 漏洞挖掘 | EDU拿敏感信息的骚思路
  • 如何构建全生命周期的安全体系架构来确保容器的安全?
  • ARM功耗管理之功耗数据与功耗收益评估
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • Django 博客开发教程 8 - 博客文章详情页
  • es6(二):字符串的扩展
  • idea + plantuml 画流程图
  • JavaScript创建对象的四种方式
  • Laravel5.4 Queues队列学习
  • MySQL Access denied for user 'root'@'localhost' 解决方法
  • mysql innodb 索引使用指南
  • nodejs实现webservice问题总结
  • Python socket服务器端、客户端传送信息
  • React-Native - 收藏集 - 掘金
  • windows下如何用phpstorm同步测试服务器
  • 基于MaxCompute打造轻盈的人人车移动端数据平台
  • 前端学习笔记之观察者模式
  • 我的业余项目总结
  • 正则表达式小结
  • k8s使用glusterfs实现动态持久化存储
  • # 服务治理中间件详解:Spring Cloud与Dubbo
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • (¥1011)-(一千零一拾一元整)输出
  • (2)nginx 安装、启停
  • (C语言)二分查找 超详细
  • (附源码)springboot高校宿舍交电费系统 毕业设计031552
  • (附源码)计算机毕业设计SSM基于java的云顶博客系统
  • (回溯) LeetCode 46. 全排列
  • (利用IDEA+Maven)定制属于自己的jar包
  • (七)Knockout 创建自定义绑定
  • (三)c52学习之旅-点亮LED灯
  • (十八)用JAVA编写MP3解码器——迷你播放器
  • (四)opengl函数加载和错误处理
  • (源码分析)springsecurity认证授权
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • ... fatal error LINK1120:1个无法解析的外部命令 的解决办法
  • .360、.halo勒索病毒的最新威胁:如何恢复您的数据?
  • .NET Core 控制台程序读 appsettings.json 、注依赖、配日志、设 IOptions
  • .Net6 Api Swagger配置
  • .net6使用Sejil可视化日志
  • .NET命令行(CLI)常用命令
  • :如何用SQL脚本保存存储过程返回的结果集
  • @EventListener注解使用说明