Springboot Retry组件@Recover失效问题解决方法

目录
  • 背景
  • 问题复现
  • 问题解决

背景

在使用springboot的retry模块时,你是否出现过@Recover注解失效的问题呢?下面我会对该问题进行复现,并且简要的说下解决方法。

问题复现

首先我们引入maven

        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

主类配置EnableRetry注解

package ai.guiji.csdn;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.retry.annotation.EnableRetry;

@EnableRetry
@SpringBootApplication
public class CsdnApplication {

  public static void main(String[] args) {
    SpringApplication.run(CsdnApplication.class, args);
  }
}

准备测试的Retry组件类代码

package ai.guiji.csdn.component;

import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;

import java.util.function.Supplier;

/** @Author 剑客阿良_ALiang @Date 2021/4/22 16:07 @Description: 重试工具 */
@Slf4j
@Component
public class RetryUtil {
  @Retryable(
      value = Exception.class,
      maxAttempts = 3,
      backoff = @Backoff(delay = 5000, multiplier = 1.5))
  public String retry(Supplier<String> supplier) throws Exception {
    String result = null;
    try {
      result = supplier.get();
    } catch (Exception exception) {
      log.error("异常报错:{}", exception.getMessage());
      throw exception;
    }
    return result;
  }

  @Recover
  public void recover(Exception e) {
    log.error("调用超过3次异常");
  }
}

代码说明

1、我们可以看到retry方法会重试supplier的get结果,捕获异常并抛出异常。这里抛出后会被retry捕获并且重试。

2、maxAttempts参数为重试的最大次数。

3、backoff中的delay为两次重试之间的延迟,multiplier为重试阻尼,可以这么理解,每次重试间隔时间为上一次重试间隔时间的倍数。

4、如果3次重试均抛出异常,则进入recover方法。

编写测试代码

package ai.guiji.csdn.component;

import cn.hutool.core.convert.Convert;
import cn.hutool.http.HttpUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/** @Author 剑客阿良_ALaing @Date 2021/11/30 13:08 @Description: */
@SpringBootTest
class RetryUtilTest {
  @Autowired private RetryUtil retryUtil;

  @Test
  void retry() {
    try {
      System.out.println(Convert.toStr(retryUtil.retry(() -> HttpUtil.post("xxxx", "")), "haha"));
    } catch (Exception exception) {
      exception.printStackTrace();
    }
  }
}

执行测试结果

2021-11-30 13:37:44.012 ERROR 13600 --- [           main] ai.guiji.csdn.component.RetryUtil        : 异常报错:UnknownHostException: xxxx
2021-11-30 13:37:49.019 ERROR 13600 --- [           main] ai.guiji.csdn.component.RetryUtil        : 异常报错:UnknownHostException: xxxx
2021-11-30 13:37:58.787 ERROR 13600 --- [           main] ai.guiji.csdn.component.RetryUtil        : 异常报错:UnknownHostException: xxxx
org.springframework.retry.ExhaustedRetryException: Cannot locate recovery method; nested exception is cn.hutool.core.io.IORuntimeException: UnknownHostException: xxxx
	at org.springframework.retry.annotation.RecoverAnnotationRecoveryHandler.recover(RecoverAnnotationRecoveryHandler.java:70)
	at org.springframework.retry.interceptor.RetryOperationsInterceptor$ItemRecovererCallback.recover(RetryOperationsInterceptor.java:142)
	at org.springframework.retry.support.RetryTemplate.handleRetryExhausted(RetryTemplate.java:539)
	at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:387)
	at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:225)
	at org.springframework.retry.interceptor.RetryOperationsInterceptor.invoke(RetryOperationsInterceptor.java:116)
	at org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.invoke(AnnotationAwareRetryOperationsInterceptor.java:163)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)
	at ai.guiji.csdn.component.RetryUtil$$EnhancerBySpringCGLIB$$d209cbc6.retry(<generated>)
	at ai.guiji.csdn.component.RetryUtilTest.retry(RetryUtilTest.java:17)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at java.util.ArrayList.forEach(ArrayList.java:1257)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at java.util.ArrayList.forEach(ArrayList.java:1257)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: cn.hutool.core.io.IORuntimeException: UnknownHostException: xxxx
	at cn.hutool.http.HttpRequest.send(HttpRequest.java:1153)
	at cn.hutool.http.HttpRequest.execute(HttpRequest.java:969)
	at cn.hutool.http.HttpRequest.execute(HttpRequest.java:940)
	at cn.hutool.http.HttpUtil.post(HttpUtil.java:216)
	at cn.hutool.http.HttpUtil.post(HttpUtil.java:197)
	at ai.guiji.csdn.component.RetryUtilTest.lambda$retry$0(RetryUtilTest.java:17)
	at ai.guiji.csdn.component.RetryUtil.retry(RetryUtil.java:22)
	at ai.guiji.csdn.component.RetryUtil$$FastClassBySpringCGLIB$$a565f63f.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
	at org.springframework.retry.interceptor.RetryOperationsInterceptor$1.doWithRetry(RetryOperationsInterceptor.java:93)
	at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:329)
	... 73 more
Caused by: java.net.UnknownHostException: xxxx
	at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184)
	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
	at java.net.Socket.connect(Socket.java:589)
	at java.net.Socket.connect(Socket.java:538)
	at sun.net.NetworkClient.doConnect(NetworkClient.java:180)
	at sun.net.www.http.HttpClient.openServer(HttpClient.java:463)
	at sun.net.www.http.HttpClient.openServer(HttpClient.java:558)
	at sun.net.www.http.HttpClient.<init>(HttpClient.java:242)
	at sun.net.www.http.HttpClient.New(HttpClient.java:339)
	at sun.net.www.http.HttpClient.New(HttpClient.java:357)
	at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1220)
	at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1156)
	at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1050)
	at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:984)
	at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1334)
	at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1309)
	at cn.hutool.http.HttpConnection.getOutputStream(HttpConnection.java:451)
	at cn.hutool.http.HttpRequest.sendFormUrlEncoded(HttpRequest.java:1176)
	at cn.hutool.http.HttpRequest.send(HttpRequest.java:1145)
	... 86 more

Process finished with exit code 0

并没有进入recover方法,注解未触发。

问题解决

这里有个很容易忽视的点,就是retry方法是有返回值的,所以recover方法也必须是相同类型带返回值的方法。所以要把recover方法改一下。

package ai.guiji.csdn.component;

import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;

import java.util.function.Supplier;

/** @Author 剑客阿良_ALiang @Date 2021/4/22 16:07 @Description: 重试工具 */
@Slf4j
@Component
public class RetryUtil {
  @Retryable(
      value = Exception.class,
      maxAttempts = 3,
      backoff = @Backoff(delay = 5000, multiplier = 1.5))
  public String retry(Supplier<String> supplier) throws Exception {
    String result = null;
    try {
      result = supplier.get();
    } catch (Exception exception) {
      log.error("异常报错:{}", exception.getMessage());
      throw exception;
    }
    return result;
  }

  @Recover
  public String recover(Exception e) {
    log.error("调用超过3次异常");
    return "调用超过3次异常";
  }
}

重新执行测试看下结果

以上就是Springboot Retry组件@Recover失效问题解决方法的详细内容,更多关于Springboot Retry 解决@Recover失效的资料请关注我们其它相关文章!

(0)

相关推荐

  • SpringBoot整合spring-retry实现接口请求重试机制及注意事项

    目录 一.重试机制 二.重试机制要素 三.重试机制注意事项 四.SpringBoot整合spring-retry 1)添加依赖 2)添加@EnableRetry注解 3)添加@Retryable注解 4)Controller测试代码 5)发送请求 6)补充:@Recover 一.重试机制 由于网络不稳定或网络抖动经常会造成接口请求失败的情况,当我们再去尝试就成功了,这就是重试机制. 其主要目的就是要尽可能地提高请求成功的概率,但一般情况下,我们请求第一次失败,代码运行就抛出异常结束了,如果想再次

  • springboot整合spring-retry的实现示例

    1.背景 本系统调用外围系统接口(http+json),但是发现有时外围系统服务不太稳定,有时候会出现返回一串xml或者gateway bad的信息,导致调用失败,基于这一原因,采用基于springboot,整合spring-retry的重试机制到系统工程中,demo已经放到github上. 2.解决方案 简要说明:demo工程基于springboot,为了方便验证,采用swagger进行测试验证. 2.1 pom文件 <?xml version="1.0" encoding=&

  • SpringBoot @Retryable注解方式

    背景 在调用第三方接口或者使用MQ时,会出现网络抖动,连接超时等网络异常,所以需要重试.为了使处理更加健壮并且不太容易出现故障,后续的尝试操作,有时候会帮助失败的操作最后执行成功.一般情况下,需要我们自行实现重试机制,一般是在业务代码中加入一层循环,如果失败后,再尝试重试,但是这样实现并不优雅.在SpringBoot中,已经实现了相关的能力,通过@Retryable注解可以实现我们想要的结果. @Retryable 首先来看一下Spring官方文档的解释: @Retryable注解可以注解于方法

  • Springboot Retry组件@Recover失效问题解决方法

    目录 背景 问题复现 问题解决 背景 在使用springboot的retry模块时,你是否出现过@Recover注解失效的问题呢?下面我会对该问题进行复现,并且简要的说下解决方法. 问题复现 首先我们引入maven <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> &

  • Laravel的throttle中间件失效问题解决方法

    本文实例讲述了Laravel的throttle中间件失效问题解决方法.分享给大家供大家参考,具体如下: 按官方解释,实现访问频率限制非常简单: Route::get('test', function(){ return 'helle world' ; })->middleware('throttle'); 也确实如此,cache存储访问次数,做出判断. 之前使用了zizaco/entrust(一个基于角色的权限管理包),其中把 .env 中的CACHE_DRIVER=file 改为了 CACHE

  • Android关于Button背景或样式失效问题解决方法

    目录 前言 问题描述: 问题原因: 解决方法: 总结 前言 最近在学习安卓开发的时候遇到了一个问题,使用Android Studio在为Button设置背景颜色的时候发现设置好后却在运行模拟机上失效了.经过一番查阅资料后才有了正确的解决办法,相信这是很多初学Android开发的朋友都会遇到的一个问题,希望此篇对大家有所帮助. 问题描述: 使用Android Studio进行安卓开发时Button的背景色一直无法修改,呈现亮紫色(呈现颜色额和主题有关,我的是亮紫色). 以其中一个Button举例,

  • jquery动态添加元素事件失效问题解决方法

    今天在写JQUERY时遇到个问题:当使用脚本动态添加元素xxx时,但事件$(".x .y").click(function)...失效问题. 刚开始网上找到了用live函数,方法如下: 事件绑定:$(".x .y").click(function) 需改为: 复制代码 代码如下: $(".x .y").live('click',function); 但是由于1.8版本后live这种方法不再支持,所以需要用下述jquery中on方法解决脚本动态添加

  • win2003服务器使用WPS的COM组件的一些问题解决方法

    由于COM组件只能在windows上使用,因为程序必须放在windows的服务器上运行.在本地xp系统上搭建安装没任何问题,在服务器win2003上安装,碰到了N个问题,最后还是gump同学出手才解决,整整折腾了不少于10个小时才搞定. 安装完Wps office个人版最新的12012版后,按理说就完成了,但运行程序在服务器上报错如下: Fatal error: Uncaught exception 'com_exception' with message 'Failed to create C

  • vue+mousemove实现鼠标拖动功能(拖动过快失效问题解决方法)

    今天用vue+原生js的mousemove事件,写了个拖动,发现只能慢慢拖动才行,鼠标只要移动快了,就失效,不能拖动了: 搞了半天在,总算解决了,但是问题的深层原理还没搞清楚,知道的大侠可以留言分享,下面直接上代码: 只能慢速拖动的代码: <!DOCTYPE html> <html> <head> <title>vue结合原生js实现拖动</title> <script src="https://cdn.bootcss.com/v

  • 微信小程序new Date()方法失效问题解决方法

    iOS系统对js中的new Date()方法有格式要求 let dt = new Date("2019-07-24 19:57") // dt会返回valid Date 正确写法应该是 let dt = new Date("2019/07/24 19:57") 而实际应该过程中日期格式大部分都是2019-07-24这种,所以在实际应用过程中需要用正则对字符串进行预处理 let tm = "2019-07-24 19:57" let dt = ne

  • SpringBoot 监控管理模块actuator没有权限的问题解决方法

    SpringBoot 1.5.9 版本加入actuator依赖后,访问/beans 等敏感的信息时候报错,如下 Tue Mar 07 21:18:57 GMT+08:00 2017 There was an unexpected error (type=Unauthorized, status=401). Full authentication is required to access this resource. 肯定是权限问题了.有两种方式: 1.关闭权限:application.prop

  • vue+springboot前后端分离实现单点登录跨域问题解决方法

    最近在做一个后台管理系统,前端是用时下火热的vue.js,后台是基于springboot的.因为后台系统没有登录功能,但是公司要求统一登录,登录认证统一使用.net项目组的认证系统.那就意味着做单点登录咯,至于不知道什么是单点登录的同学,建议去找一下万能的度娘. 刚接到这个需求的时候,老夫心里便不屑的认为:区区登录何足挂齿,但是,开发的过程狠狠的打了我一巴掌(火辣辣的一巴掌)...,所以这次必须得好好记录一下这次教训,以免以后再踩这样的坑. 我面临的第一个问题是跨域,浏览器控制台直接报CORS,

  • springboot反爬虫组件kk-anti-reptile的使用方法

      大家好,我是为广大程序员兄弟操碎了心的小编,每天推荐一个小工具/源码,装满你的收藏夹,每天分享一个小技巧,让你轻松节省开发效率,实现不加班不熬夜不掉头发,是我的目标!   今天小编推荐一款反爬虫组件叫kk-anti-reptile,一款可快速接入的反爬虫.接口防盗刷spring boot stater组件. 1. 系统要求 基于spring-boot开发(spring-boot1.x, spring-boot2.x均可) 需要使用redis 2. 工作流程   kk-anti-reptile

随机推荐