困扰我的问题,我用 SpringBoot+Redis 解决了!

阿里云中Redis远程连接不上解决方案(连接RedisDesktopManager,连接springboot项目) 今天有必要说说困扰了我几天的一个问题,springboot项目远程连接阿里云中的redis不成功的问题。 看了网上很多解决方案,都是端口问题,将127.0.0.1注释或改为0.0.0.0,但是本人的问题不是端口问题,这些配置之前也都改过了。 其实redis无法连接数据库就只有这几种可能,防火墙,安全组,密码,绑定IP。 按照顺序依次检查了防火墙,安全组,密码,配置文件,都确认没有问题之后,再次启动redis,依然是服务器可以访问,本地telnet都无法连接。这个问题很像是防火墙和绑定IP的原因。 所以我再次 阅读详情

前言

在实际的开发项目中,一个对外暴露的接口往往会面临很多次请求,我们来解释一下幂等的概念:任意多次执行所产生的影响均与一次执行的影响相同。按照这个含义,最终的含义就是 对数据库的影响只能是一次性的,不能重复处理。如何保证其幂等性,通常有以下手段:

  1. 数据库建立唯一性索引,可以保证最终插入数据库的只有一条数据
  2. token机制,每次接口请求前先获取一个token,然后再下次请求的时候在请求的header体中加上这个token,后台进行验证,如果验证通过删除token,下次请求再次判断token
  3. 悲观锁或者乐观锁,悲观锁可以保证每次for update的时候其他sql无法update数据(在数据库引擎是innodb的时候,select的条件必须是唯一索引,防止锁全表)
  4. 先查询后判断,首先通过查询数据库是否存在数据,如果存在证明已经请求过了,直接拒绝该请求,如果没有存在,就证明是第一次进来,直接放行。

redis实现自动幂等的原理图:

搭建redis的服务Api

  • 首先是搭建redis服务器。
  • 引入springboot中到的redis的stater,或者Spring封装的jedis也可以,后面主要用到的api就是它的set方法和exists方法,这里我们使用springboot的封装好的redisTemplate
/** * redis工具类 */@Componentpublic class RedisService {    @Autowired    private RedisTemplate redisTemplate;    /**     * 写入缓存     * @param key     * @param value     * @return     */    public boolean set(final String key, Object value) {        boolean result = false;        try {            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();            operations.set(key, value);            result = true;        } catch (Exception e) {            e.printStackTrace();        }        return result;    }    /**     * 写入缓存设置时效时间     * @param key     * @param value     * @return     */    public boolean setEx(final String key, Object value, Long expireTime) {        boolean result = false;        try {            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();            operations.set(key, value);            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);            result = true;        } catch (Exception e) {            e.printStackTrace();        }        return result;    }    /**     * 判断缓存中是否有对应的value     * @param key     * @return     */    public boolean exists(final String key) {        return redisTemplate.hasKey(key);    }    /**     * 读取缓存     * @param key     * @return     */    public Object get(final String key) {        Object result = null;        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();        result = operations.get(key);        return result;    }    /**     * 删除对应的value     * @param key     */    public boolean remove(final String key) {        if (exists(key)) {            Boolean delete = redisTemplate.delete(key);            return delete;        }        return false;    }}

自定义注解AutoIdempotent

自定义一个注解,定义此注解的主要目的是把它添加在需要实现幂等的方法上,凡是某个方法注解了它,都会实现自动幂等。后台利用反射如果扫描到这个注解,就会处理这个方法实现自动幂等,使用元注解ElementType.METHOD表示它只能放在方法上,etentionPolicy.RUNTIME表示它在运行时

@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface AutoIdempotent {  }

token创建和检验

  • token服务接口:我们新建一个接口,创建token服务,里面主要是两个方法,一个用来创建token,一个用来验证token。创建token主要产生的是一个字符串,检验token的话主要是传达request对象,为什么要传request对象呢?主要作用就是获取header里面的token,然后检验,通过抛出的Exception来获取具体的报错信息返回给前端
public interface TokenService {    /**     * 创建token     * @return     */    public  String createToken();    /**     * 检验token     * @param request     * @return     */    public boolean checkToken(HttpServletRequest request) throws Exception;}
  • token的服务实现类:token引用了redis服务,创建token采用随机算法工具类生成随机uuid字符串,然后放入到redis中(为了防止数据的冗余保留,这里设置过期时间为10000秒,具体可视业务而定),如果放入成功,最后返回这个token值。checkToken方法就是从header中获取token到值(如果header中拿不到,就从paramter中获取),如若不存在,直接抛出异常。这个异常信息可以被拦截器捕捉到,然后返回给前端。
@Servicepublic class TokenServiceImpl implements TokenService {    @Autowired    private RedisService redisService;    /**     * 创建token     *     * @return     */    @Override    public String createToken() {        String str = RandomUtil.randomUUID();        StrBuilder token = new StrBuilder();        try {            token.append(Constant.Redis.TOKEN_PREFIX).append(str);            redisService.setEx(token.toString(), token.toString(),10000L);            boolean notEmpty = StrUtil.isNotEmpty(token.toString());            if (notEmpty) {                return token.toString();            }        }catch (Exception ex){            ex.printStackTrace();        }        return null;    }    /**     * 检验token     *     * @param request     * @return     */    @Override    public boolean checkToken(HttpServletRequest request) throws Exception {        String token = request.getHeader(Constant.TOKEN_NAME);        if (StrUtil.isBlank(token)) {// header中不存在token            token = request.getParameter(Constant.TOKEN_NAME);            if (StrUtil.isBlank(token)) {// parameter中也不存在token                throw new ServiceException(Constant.ResponseCode.ILLEGAL_ARGUMENT, 100);            }        }        if (!redisService.exists(token)) {            throw new ServiceException(Constant.ResponseCode.REPETITIVE_OPERATION, 200);        }        boolean remove = redisService.remove(token);        if (!remove) {            throw new ServiceException(Constant.ResponseCode.REPETITIVE_OPERATION, 200);        }        return true;    }}

拦截器的配置

  • web配置类,实现WebMvcConfigurerAdapter,主要作用就是添加autoIdempotentInterceptor到配置类中,这样我们到拦截器才能生效,注意使用@Configuration注解,这样在容器启动是时候就可以添加进入context中
@Configurationpublic class WebConfiguration extends WebMvcConfigurerAdapter {    @Resource   private AutoIdempotentInterceptor autoIdempotentInterceptor;    /**     * 添加拦截器     * @param registry     */    @Override    public void addInterceptors(InterceptorRegistry registry) {        registry.addInterceptor(autoIdempotentInterceptor);        super.addInterceptors(registry);    }}
  • 拦截处理器:主要的功能是拦截扫描到AutoIdempotent到注解到方法,然后调用tokenService的checkToken()方法校验token是否正确,如果捕捉到异常就将异常信息渲染成json返回给前端
/** * 拦截器 */@Componentpublic class AutoIdempotentInterceptor implements HandlerInterceptor {    @Autowired    private TokenService tokenService;    /**     * 预处理     *     * @param request     * @param response     * @param handler     * @return     * @throws Exception     */    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        if (!(handler instanceof HandlerMethod)) {            return true;        }        HandlerMethod handlerMethod = (HandlerMethod) handler;        Method method = handlerMethod.getMethod();        //被ApiIdempotment标记的扫描        AutoIdempotent methodAnnotation = method.getAnnotation(AutoIdempotent.class);        if (methodAnnotation != null) {            try {                return tokenService.checkToken(request);// 幂等性校验, 校验通过则放行, 校验失败则抛出异常, 并通过统一异常处理返回友好提示            }catch (Exception ex){                ResultVo failedResult = ResultVo.getFailedResult(101, ex.getMessage());                writeReturnJson(response, JSONUtil.toJsonStr(failedResult));                throw ex;            }        }        //必须返回true,否则会被拦截一切请求        return true;    }    @Override    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {    }    @Override    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {    }    /**     * 返回的json值     * @param response     * @param json     * @throws Exception     */    private void writeReturnJson(HttpServletResponse response, String json) throws Exception{        PrintWriter writer = null;        response.setCharacterEncoding("UTF-8");        response.setContentType("text/html; charset=utf-8");        try {            writer = response.getWriter();            writer.print(json);        } catch (IOException e) {        } finally {            if (writer != null)                writer.close();        }    }}

测试用例

  • 模拟业务请求类,首先我们需要通过/get/token路径通过getToken()方法去获取具体的token,然后我们调用testIdempotence方法,这个方法上面注解了@AutoIdempotent,拦截器会拦截所有的请求,当判断到处理的方法上面有该注解的时候,就会调用TokenService中的checkToken()方法,如果捕获到异常会将异常抛出调用者,下面我们来模拟请求一下:
@RestControllerpublic class BusinessController {    @Resource    private TokenService tokenService;    @Resource    private TestService testService;    @PostMapping("/get/token")    public String  getToken(){        String token = tokenService.createToken();        if (StrUtil.isNotEmpty(token)) {            ResultVo resultVo = new ResultVo();            resultVo.setCode(Constant.code_success);            resultVo.setMessage(Constant.SUCCESS);            resultVo.setData(token);            return JSONUtil.toJsonStr(resultVo);        }        return StrUtil.EMPTY;    }    @AutoIdempotent    @PostMapping("/test/Idempotence")    public String testIdempotence() {        String businessResult = testService.testIdempotence();        if (StrUtil.isNotEmpty(businessResult)) {            ResultVo successResult = ResultVo.getSuccessResult(businessResult);            return JSONUtil.toJsonStr(successResult);        }        return StrUtil.EMPTY;    }}
  • 使用postman请求,首先访问get/token路径获取到具体到token:

  • 利用获取到到token,然后放到具体请求到header中,可以看到第一次请求成功,接着我们请求第二次:

  • 第二次请求,返回到是重复性操作,可见重复性验证通过,再多次请求到时候我们只让其第一次成功,第二次就是失败:

总结

本篇博客介绍了使用springboot和拦截器、redis来优雅的实现接口幂等,对于幂等在实际的开发过程中是十分重要的,因为一个接口可能会被无数的客户端调用,如何保证其不影响后台的业务处理,如何保证其只影响数据一次是非常重要的,它可以防止产生脏数据或者乱数据,也可以减少并发量,实乃十分有益的一件事。而传统的做法是每次判断数据,这种做法不够智能化和自动化,比较麻烦。而今天的这种自动化处理也可以提升程序的伸缩性。

SpringBoot项目实战:5种方法解决Lettuce连接Redis超时问题(附完整代码) 本文详细介绍了SpringBoot项目中解决Lettuce连接Redis超时问题的5种实战方法,包括核心参数调优、连接健康检查机制、动态拓扑感知配置、连接池替代方案对比以及混合模式与高级调优。通过完整的代码示例和配置建议,帮助开发者有效应对RedisCommandTimeoutException,提升系统稳定性。 阅读详情

相关推荐

SpringBoot中LocalDateTime序列化问题解决方案

在Java开发中,JSON序列化是系统间数据交互的核心技术,而日期时间处理则是其中常见难点。Java 8引入的java.time API虽然解决了旧Date类的线程安全问题,但与Jackson等序列化框架的兼容性需要特殊配置。通过注册JavaTimeModule模块或自定义序列化器,开发者可以确保LocalDateTime等类型在SpringBoot项目中正确转换。这类问题在微服务架构和前后端分离场景尤为突出,涉及RestTemplate调用、Redis缓存序列化等典型应用场景。合理的日期时间处理方案能提升

weixin_33841503的博客 294

SpringBoot整合Redis,Redis突然宕机,不影响业务逻辑正常运行

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> 创建SpringBoot项目 https://start.

liwenyang1992的专栏 3945

SpringBoot整合Redis快速入门实战Demo

默认用 JDK 序列化,数据是二进制的,看不懂不说,还跨不了语言。换成 JSON 才是正道:@Bean这样一来,Redis 里看到的就是明文 JSON,运维同学再也不骂你了 😂检查项是否完成✅ 单元测试覆盖率 ≥ 80%是✅ 多环境配置分离(dev/test/prod)是✅ Redis 连接池已配置(min-idle/max-total)是✅ 序列化方式设为 JSON 而非 JDK 默认是。

weixin_32102617的博客 896

springboot项目启动自动缓存redis或初始化数据

springboot项目启动自动缓存redis或初始化数据

yushun的博客 1388

WPF MVVM实战:构建可绑定的RichTextBox富文本编辑器

本文详细介绍了如何在WPF MVVM模式下构建可绑定的RichTextBox富文本编辑器,解决标准RichTextBox.Document属性不支持数据绑定的问题。通过创建自定义BindableRichTextBox控件,实现FlowDocument的绑定支持,并分享插入图片、文本格式控制等实战技巧,帮助开发者高效开发富文本编辑功能。

weixin_33717117的博客 307

保姆级教程:用MATLAB复现格拉斯哥大学FMCW雷达数据集多普勒谱(附完整代码与避坑指南)

本文提供了一份详细的MATLAB教程,指导如何复现格拉斯哥大学FMCW雷达数据集的多普勒谱分析。从环境准备、数据获取到距离压缩、MTI滤波、时频分析(STFT)以及结果后处理,涵盖了完整流程,并附有优化代码和避坑指南,特别适合雷达信号处理研究者和工程师。

weixin_28720767的博客 117

SpringBoot集成Redis的操作流程

在yml文件中配置了redis后,此时启动会报错,因为我们在项目中引入redis是做缓存的作用,此时没有引入MySql数据库,但是启动redis之后还是会加载数据库需要使用的一些类,因此会报错。我们在前端使用URL两次访问该方法,对于前端传来的URL,如果是从缓存中查询,那么我们打印一串*********,然后打印查询到的数据,如果是从数据库中查询,我们直接打印查询到的数据。我们还设置了redis运行的ip地址,端口号,数据库,超时时间,以及最大连接数,最大阻塞等待时间,最大空闲连接等参数。

Ahjbvj的博客 282

RV1106的ISP配置实战:MIPI与LVDS接口的硬件连接与设备树优化

本文详细解析了RV1106芯片的ISP配置实战,重点对比了MIPI CSI-2与LVDS两种主流接口的硬件连接方案与数据流差异。通过手把手的设备树代码示例,深入讲解了单路MIPI与双路LVDS摄像头的配置要点、关键属性设置及常见避坑指南,并提供了从驱动加载检查到Media Controller工具使用的系统化调试方法,帮助开发者快速打通图像采集与处理全链路。

terraform7cloud的博客 474

Spring Boot Redis关闭

【代码】Spring Boot Redis关闭。

小码蚁 4538

从ADS到SystemVue:当简单链路预算不够用时,我的射频系统级仿真方案升级实录

本文详细介绍了从ADS到SystemVue的射频系统级仿真升级路径,重点分析了基础链路预算在多端口元件和复杂场景下的局限性,并提供了SystemVue在非线性系统验证和跨域协同分析中的优势。通过实际案例展示了如何平滑迁移工具链,提升仿真精度和效率,适用于5G、毫米波雷达等现代射频系统设计。

weixin_34233974的博客 345

lvgl 实现表格table,并且固定表头

嵌入式实现table,lvgl 实现原生表格table组件

m0_61704502的博客 1273

【SpringBoot】SpringBoot整合Redis

SpringBoot整合Redis 首先我是在Windows上开发的,因此需要在Windows安装Redis,安装的教程Windows下安装Redis服务,这个教程很容易懂,我按照教程一步一步安装下来,很久就可以运行Redis了。 这个教程在【JAVA】IDEA中SpringBoot整合M...

阳阳的博客 2511

比利时风电光伏负荷一体化数据集解析与应用

本文深度解析了由比利时输电系统运营商Elia集团提供的风电光伏负荷一体化数据集。该数据集整合了负荷、风电与光伏出力数据,并包含多时间尺度的官方预测值,时间跨度长达四年,是研究能源转型、构建电力预测模型及分析电网运行的宝贵资源。文章详细介绍了数据价值、字段含义、实战应用场景(如预测模型基准测试、风光互补性分析)以及数据处理指南,为相关领域的研究与开发提供了高质量数据基础和实践参考。

weixin_29200741的博客 429

SpringBoot集成Redis---这是一篇自我学习与记录问题。

文章目录前言一、如何集成Redis1.添加Redis起步依赖2. 编写测试程序配置连接信息ControllerServiceStudentServiceStudentServiceImpl结果的实现二.总结问题1. 避免忘记启动本地,Redis2. 我在确保代码都无问题时,报的错3. 还有一点注解问题总结 前言 此篇将会记录,SpringBoot项目中如何集成Redis,并且记录自己所遇到的问题。 一、如何集成Redis 在这呢,什么是Redis,Redis的安装,如何创建SpringBoot-web.

Dimples1014的博客 572

AI agents在公司财务报表分析中的应用

在当今竞争激烈的商业环境中,公司财务报表分析对于企业的决策制定、风险评估、战略规划等方面起着至关重要的作用。传统的财务报表分析方法往往依赖于人工操作,存在效率低下、容易出错、难以处理海量数据等问题。而AI agents作为人工智能领域的重要成果,具有自动化、智能化、高效性等特点,能够为财务报表分析带来新的突破和变革。

SuperAGI2025 1611

基于GA-BP遗传算法优化神经网络的多输入多输出数据预测-Python代码实现

多输入多输出的GA-BP神经网络

2301_80449389的博客 2308

大话Java系列-三英战吕布,SpringBoot+Redis+MySQL的数据不一致问题解决方案

通过SpringBoot(刘备)的统筹协调,MySQL(关羽)的乐观锁机制,以及Redis(张飞)的分布式锁技术,三英成功解决了库存超卖的问题,确保了数据的一致性。用户的购物体验得到了显著提升,平台的信誉也得到了恢复。 这场胜利不仅展示了技术的力量,更体现了团队合作的重要性。在数字化时代,只有各司其职、协同作战,才能战胜各种挑战,为企业带来更多的机遇和发展空间。

横竖撇折点 996

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

告别乱码困扰!Spring Boot 3中Redis序列化的4种方案对比及选型建议

轻松解决Spring Boot 3整合Redis序列化乱码难题,对比JSON、JDK、String及自定义序列化方案,涵盖适用场景与配置要点,提升数据存储一致性。方案清晰易实现,值得收藏参考。

StepNexus的博客 620

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

零基础入门:SpringBoot整合Redis图文教程

安装完成后,启动Redis服务,然后在Redis Desktop Manager中添加连接,填写主机地址(localhost)和端口(6379)即可连接成功。第二次调用时,就会直接从Redis中获取结果。整个过程不需要复杂的配置,也不需要深入了解Redis的底层原理,就能实现基本的缓存功能。最近在学习SpringBoot整合Redis的过程中,发现很多教程对新手不够友好,要么配置复杂,要么缺少可视化演示。观察不同参数下Redis中的数据变化,这种动手实践的方式能帮助你更好地掌握缓存的使用场景和注意事项。

YellowSun24的博客 938

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Redis导入问题

Redis导入

qq_61852781的博客 303

从‘连接超时’到成功连上:一次搞定Linux虚拟机Redis与SpringBoot的联调

本文详细解析了Linux虚拟机中Redis与SpringBoot联调时常见的`io.lettuce.core.RedisConnectionException`连接异常问题,提供了从网络配置到Redis安全设置的完整解决方案。通过桥接模式设置、Redis服务端配置优化及SpringBoot客户端精准调优,帮助开发者快速解决跨环境联调难题,实现稳定可靠的Redis连接。

weixin_30570101的博客 302

记一次SpringBoot项目使用RedisTemplate无法反序列化字节数组问题困扰8小时巨坑

一、问题描述 问题描述: 前提:向Redis中SET值的是一个JFinal项目,使用的Jedis客户端API操作的,把一个对象转成byte[]形式存入Redis中。 然后在另一个SpringBoot项目中我通过Spring自带的RedisTemplate对象来获取,一直失败。 接下来就是我的踩坑记录: 二、踩坑记录 使用StringRedisTemplate获取值(即采用StringRedisSerializer序列化方式) 然后我使用命令行获取去发现返回同样是乱码: 又换成Jackson2JsonR

DreamsArchitects的博客 2612

RabbitMQ实战:用Redis搞定消息重复消费的3种姿势(附SpringBoot代码)

本文深入探讨了RabbitMQ消息重复消费问题的三种高效解决方案,重点介绍了如何利用Redis实现消息去重。通过String存储、List存储和Key过期三种方案,结合SpringBoot代码示例,帮助开发者应对不同场景下的消息幂等性挑战,提升系统可靠性。特别适合需要处理高并发消息队列的分布式系统架构。

jwt8token的博客 768

springboot3.x和2.x整合redis报错Unable to connect to Redis连接不上的问题

在SpringBoot3.x中配置文件是在 data.redis下面配置的。在SpringBoot2.x中配置文件是在 redis下面配置的。除了基本的连接不上问题之外下面这个问题也需要看看。搞了半小时原来是这个问题。

weixin_44866321的博客 584

springboot结合redis使用CachingConfigurerSupport方法不能被继承

springboot结合redis使用CachingConfigurerSupport方法不能被继承

小白程序员的博客 1万+
上一篇: 分享 14 个 Spring MVC 顶级技巧
下一篇: 重新刷新你对Redis集群的理解
Java_ttcd
博客等级 码龄4年 198粉丝 190原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值