当前位置:   article > 正文

springboot+redis+lua脚本进行接口限流,解决高并发计数不准确问题_在高并发下 lua 也不管用

在高并发下 lua 也不管用

为啥用redis呢(只是此处的使用原因):

        因为redis是一个内存数据库,效率高;

        redis支持事务;

        redis支持分布式,与系统无强关联,不管系统是单机还是分布式部署都支持。

为啥用lua脚本呢:因为lua脚本可以原子性的执行redis命令。

注:千万不要使用网上那种在切面或者拦截器中直接使用redistemplate.opsForValue().get 与set的方式来进行接口请求数量的控制,因为当并发的时候,肯定会出现数量计算不准确的问题。

1.添加pom依赖

  1. <!-- redis引入 -->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-data-redis</artifactId>
  5. </dependency>

2.yml配置文件:

spring:
  redis:
    #密码
    password: kevin
    #连接超时时长(毫秒)
    timeout: 30000
    cluster:
      #集群节点以逗号分隔,或换行后 - 开头
      nodes:
        - 127.0.0.1:6381
        - 127.0.0.1:6382
        - 127.0.0.1:6383
        - 127.0.0.1:6384
        - 127.0.0.1:6385
        - 127.0.0.1:6386
      # 获取失败 最大重定向次数
      max-redirects: 3
    #lettuce连接池信息
    # 连接池最大连接数(使用负值表示没有限制) 默认为8
    lettuce:
      pool:
        # 连接池最大连接数
        max-active: 1000
        # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认为-1
        max-wait: -1
        # 连接池中的最大空闲连接 默认为8
        max-idle: 200
        # 连接池中的最小空闲连接 默认为 0
        min-idle: 100

3.redis配置类

  1. package com.liu.config;
  2. import com.fasterxml.jackson.annotation.JsonAutoDetect;
  3. import com.fasterxml.jackson.annotation.JsonTypeInfo;
  4. import com.fasterxml.jackson.annotation.PropertyAccessor;
  5. import com.fasterxml.jackson.databind.ObjectMapper;
  6. import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
  7. import com.liu.redisexpired.RedisMessageListenerFactory;
  8. import io.lettuce.core.TimeoutOptions;
  9. import io.lettuce.core.cluster.ClusterClientOptions;
  10. import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
  11. import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
  12. import org.springframework.beans.factory.BeanFactory;
  13. import org.springframework.beans.factory.annotation.Autowired;
  14. import org.springframework.beans.factory.annotation.Qualifier;
  15. import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  16. import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
  17. import org.springframework.boot.context.properties.ConfigurationProperties;
  18. import org.springframework.context.annotation.Bean;
  19. import org.springframework.context.annotation.Configuration;
  20. import org.springframework.context.annotation.Primary;
  21. import org.springframework.core.env.Environment;
  22. import org.springframework.data.redis.connection.MessageListener;
  23. import org.springframework.data.redis.connection.RedisClusterConfiguration;
  24. import org.springframework.data.redis.connection.RedisConnectionFactory;
  25. import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
  26. import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
  27. import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
  28. import org.springframework.data.redis.core.RedisTemplate;
  29. import org.springframework.data.redis.listener.PatternTopic;
  30. import org.springframework.data.redis.listener.RedisMessageListenerContainer;
  31. import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
  32. import org.springframework.data.redis.serializer.StringRedisSerializer;
  33. import java.time.Duration;
  34. import java.util.Arrays;
  35. @Configuration
  36. public class RedisConfig {
  37. @Autowired
  38. private Environment environment;
  39. @Bean(value = "nodes")
  40. @ConfigurationProperties(prefix = "spring.redis.cluster")
  41. public RedisNodes nodes(){
  42. return new RedisNodes();
  43. }
  44. /**
  45. * 配置lettuce连接池
  46. * @author kevin
  47. * @return org.apache.commons.pool2.impl.GenericObjectPoolConfig
  48. * @date 2022/5/26
  49. */
  50. @Bean
  51. @Primary
  52. @ConfigurationProperties(prefix = "spring.redis.cluster.lettuce.pool")
  53. public GenericObjectPoolConfig<Object> redisPool() {
  54. GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
  55. String maxActive = environment.getProperty("spring.redis.lettuce.pool.max-active");
  56. if(null != maxActive && !"".equals(maxActive)) {
  57. poolConfig.setMaxTotal(Integer.parseInt(maxActive));
  58. }
  59. String maxWait = environment.getProperty("spring.redis.lettuce.pool.max-wait");
  60. if(null != maxWait && !"".equals(maxWait)) {
  61. poolConfig.setMaxWaitMillis(Integer.parseInt(maxWait));
  62. }
  63. String maxIdle = environment.getProperty("spring.redis.lettuce.pool.max-idle");
  64. if(null != maxIdle && !"".equals(maxIdle)) {
  65. poolConfig.setMaxIdle(Integer.parseInt(maxIdle));
  66. }
  67. String minIdle = environment.getProperty("spring.redis.lettuce.pool.min-idle");
  68. if(null != minIdle && !"".equals(minIdle)) {
  69. poolConfig.setMinIdle(Integer.parseInt(minIdle));
  70. }
  71. return new GenericObjectPoolConfig<>();
  72. }
  73. /**
  74. * 配置数据源的
  75. * @author kevin
  76. * @return org.springframework.data.redis.connection.RedisClusterConfiguration
  77. * @date 2022/5/26
  78. */
  79. @Bean("redisClusterConfig")
  80. @Primary
  81. public RedisClusterConfiguration redisClusterConfig() {
  82. RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(
  83. Arrays.asList(nodes().getNodes()));
  84. redisClusterConfiguration.setPassword(environment.getProperty("spring.redis.password"));
  85. String maxRedirects = environment.getProperty("spring.redis.cluster.max-redirects");
  86. if(null != maxRedirects && !"".equals(maxRedirects)) {
  87. redisClusterConfiguration.setMaxRedirects(Integer.parseInt(maxRedirects));
  88. }
  89. return redisClusterConfiguration;
  90. }
  91. /**
  92. * 配置数据源的连接工厂
  93. * 这里注意:需要添加@Primary 指定bean的名称,目的是为了创建不同名称的LettuceConnectionFactory
  94. * @author kevin
  95. * @param redisPool :
  96. * @param redisClusterConfig :
  97. * @return org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
  98. * @date 2022/5/26
  99. */
  100. @Bean("lettuceConnectionFactory")
  101. @Primary
  102. public LettuceConnectionFactory lettuceConnectionFactory(GenericObjectPoolConfig<Object> redisPool,
  103. @Qualifier("redisClusterConfig") RedisClusterConfiguration redisClusterConfig) {
  104. // LettuceClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder()
  105. // .poolConfig(redisPool).build();
  106. LettuceClientConfiguration clientConfiguration = getClientConfiguration(redisPool);
  107. return new LettuceConnectionFactory(redisClusterConfig, clientConfiguration);
  108. }
  109. /**
  110. * 配置数据源的RedisTemplate
  111. * 注意:这里指定使用名称=factory 的 RedisConnectionFactory
  112. * @author kevin
  113. * @param redisConnectionFactory :
  114. * @return org.springframework.data.redis.core.RedisTemplate
  115. * @date 2022/5/26
  116. */
  117. @Bean("redisTemplate")
  118. @Primary
  119. public RedisTemplate<String, Object> redisTemplate(@Qualifier("lettuceConnectionFactory")
  120. RedisConnectionFactory redisConnectionFactory) {
  121. return getRedisTemplate(redisConnectionFactory);
  122. }
  123. /**
  124. * lettuce配置信息构建获取--redis集群高可用
  125. * @author kevin
  126. * @param redisPool :
  127. * @return org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration
  128. * @date 2022/5/27 12:00
  129. */
  130. private LettuceClientConfiguration getClientConfiguration(GenericObjectPoolConfig<Object> redisPool) {
  131. //支持自适应集群拓扑刷新和静态刷新源
  132. ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
  133. //.enablePeriodicRefresh(Duration.ofSeconds(10)) // 启用定期集群拓扑更新
  134. .enableAllAdaptiveRefreshTriggers() // 自适应拓扑刷新
  135. .adaptiveRefreshTriggersTimeout(Duration.ofSeconds(10)) // 自适应拓扑更新的超时时间
  136. .build();
  137. ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder().timeoutOptions(
  138. TimeoutOptions.enabled(Duration.ofSeconds(30))) // 超时修改为30秒
  139. // .autoReconnect(false) //启用或禁用连接丢失时的自动重新连接--默认true
  140. // .pingBeforeActivateConnection(Boolean.TRUE) //在激活连接标志之前设置 PING
  141. // .cancelCommandsOnReconnectFailure(Boolean.TRUE) //允许在重新连接失败的情况下取消排队的命令。默认为 false
  142. // .disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS) //设置连接处于断开状态时的命令调用行为
  143. .topologyRefreshOptions(clusterTopologyRefreshOptions) //设置ClusterTopologyRefreshOptions拓扑更新的详细控制
  144. .build();
  145. return LettucePoolingClientConfiguration.builder()
  146. .poolConfig(redisPool) //设置GenericObjectPoolConfig驱动程序使用的连接池配置
  147. //.readFrom(ReadFrom.NEAREST) //配置ReadFrom
  148. .clientOptions(clusterClientOptions) //配置ClientOptions
  149. .build();
  150. }
  151. /**
  152. * redisTemplate redis操作工具获取
  153. * @author kevin
  154. * @param factory : redis连接工厂
  155. * @return org.springframework.data.redis.core.RedisTemplate<java.lang.String,java.lang.Object>
  156. * @date 2022/5/27 12:00
  157. */
  158. private RedisTemplate<String, Object> getRedisTemplate(RedisConnectionFactory factory) {
  159. RedisTemplate<String, Object> template = new RedisTemplate<>();
  160. template.setConnectionFactory(factory);
  161. Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
  162. ObjectMapper om = new ObjectMapper();
  163. om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  164. // om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL) //过期方法
  165. om.activateDefaultTyping(
  166. LaissezFaireSubTypeValidator.instance ,
  167. ObjectMapper.DefaultTyping.NON_FINAL,
  168. JsonTypeInfo.As.WRAPPER_ARRAY);
  169. jackson2JsonRedisSerializer.setObjectMapper(om);
  170. StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
  171. // key采用String的序列化方式
  172. template.setKeySerializer(stringRedisSerializer);
  173. // hash的key也采用String的序列化方式
  174. template.setHashKeySerializer(stringRedisSerializer);
  175. // value序列化方式采用jackson
  176. template.setValueSerializer(jackson2JsonRedisSerializer);
  177. // hash的value序列化方式采用jackson
  178. template.setHashValueSerializer(jackson2JsonRedisSerializer);
  179. template.afterPropertiesSet();
  180. return template;
  181. }
  182. }

RedisNodes类:

  1. package com.liu.config;
  2. public class RedisNodes {
  3. private String[] nodes;
  4. public String[] getNodes() {
  5. return nodes;
  6. }
  7. public void setNodes(String[] nodes) {
  8. this.nodes = nodes;
  9. }
  10. }

4.编写接口限流redis工具类

  1. package com.liu.utils;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.beans.factory.annotation.Qualifier;
  5. import org.springframework.data.redis.core.RedisTemplate;
  6. import org.springframework.data.redis.core.script.DefaultRedisScript;
  7. import org.springframework.data.redis.core.script.RedisScript;
  8. import org.springframework.data.redis.serializer.GenericToStringSerializer;
  9. import org.springframework.stereotype.Component;
  10. import java.util.Collections;
  11. /**
  12. * redis工具类
  13. *
  14. * @author kevin
  15. * @date 2021/3/30
  16. */
  17. @Slf4j
  18. @Component
  19. @SuppressWarnings({"unused"})
  20. public class RedisUtils {
  21. private static final Long SUCCESS = 1L;
  22. /*
  23. * 注入redisTemplate bean(可配置不同的template secondaryRedisTemplate)
  24. */
  25. @Autowired
  26. @Qualifier(value = "redisTemplate") //指定注入的template模版
  27. private RedisTemplate<String, Object> redisTemplate;
  28. //======================接口限流计数法--处理请求单位时间内接口请求次数 开始========================
  29. /**
  30. * 判断单位时间内请求数量是否超过限制--使用lua脚本执行,保证原子性
  31. *
  32. * @param key : 键
  33. * @param time : 单位时间(秒)
  34. * @param count : 最大请求次数
  35. * @return boolean true成功 false失败
  36. * @author kevin
  37. * @date 2022/5/31
  38. */
  39. public boolean requestLimit(String key, int time, int count) {
  40. //lua 脚本,进行请求次数的叠加,并判断请求次数是否超过限制
  41. String script = "local val = redis.call('incr', KEYS[1]) " +
  42. "local expire = tonumber(ARGV[1]) " +
  43. "if val == 1 " +
  44. "then redis.call('expire', KEYS[1], expire) " +
  45. "else if redis.call('ttl', KEYS[1]) == -1 " +
  46. "then redis.call('expire', KEYS[1], expire) " +
  47. "end " +
  48. "end " +
  49. "if val > tonumber(ARGV[2]) " +
  50. "then return 0 " +
  51. "end " +
  52. "return 1";
  53. RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
  54. // execute使用的redis的默认的序列化方式,需要设置参数--arg的序列化方式,以及result结果的序列化方式
  55. // 此处传参只要能转为Object就行(因为数字不能直接强转为String,所以不能用String序列化)
  56. // 结果的类型需要根据脚本定义,此处是数字--定义的是Long类型
  57. Long result = redisTemplate.execute(redisScript, new GenericToStringSerializer<>(Object.class),
  58. new GenericToStringSerializer<>(Long.class), Collections.singletonList(key), time, count);
  59. return SUCCESS.equals(result);
  60. }
  61. //======================接口限流计数法--处理请求单位时间内接口请求次数 结束========================
  62. }

5.自定义限流注解

  1. package com.liu.requestlimit;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. import java.util.concurrent.TimeUnit;
  8. /**
  9. * 限制单位时间内,接口被请求的次数
  10. * @author kevin
  11. * @date 2022/5/26
  12. */
  13. @Target(ElementType.METHOD)
  14. @Retention(RetentionPolicy.RUNTIME)
  15. @Documented
  16. public @interface RequestLimit {
  17. /*
  18. 限流的key
  19. */
  20. String key() default "request_limit_";
  21. /*
  22. 单位时间
  23. */
  24. int time() default 60;
  25. /*
  26. 限流时间单位,默认秒
  27. */
  28. TimeUnit timeunit() default TimeUnit.SECONDS;
  29. /*
  30. 单位时间内可访问次数
  31. */
  32. int count() default 100;
  33. /*
  34. 限流的类型:全局,IP
  35. */
  36. RequestLimitType limitType() default RequestLimitType.DEFAULT;
  37. String msg() default "请求过于频繁,请稍后再试!";
  38. }

6.编写AOP切面

  1. package com.liu.requestlimit;
  2. import com.liu.utils.RedisUtils;
  3. import com.liu.utils.WebUtils;
  4. import com.liu.vo.ResponseVo;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.aspectj.lang.ProceedingJoinPoint;
  7. import org.aspectj.lang.annotation.Around;
  8. import org.aspectj.lang.annotation.Aspect;
  9. import org.aspectj.lang.annotation.Pointcut;
  10. import org.springframework.stereotype.Component;
  11. import org.springframework.web.context.request.RequestContextHolder;
  12. import org.springframework.web.context.request.ServletRequestAttributes;
  13. import javax.annotation.Resource;
  14. import javax.servlet.http.HttpServletRequest;
  15. import javax.servlet.http.HttpServletResponse;
  16. import java.util.StringJoiner;
  17. @Slf4j
  18. @Aspect
  19. @Component
  20. public class RequestLimitAspect {
  21. @Resource
  22. private RedisUtils redisUtils;
  23. @Pointcut("@annotation(requestLimit)")
  24. public void doBefore(RequestLimit requestLimit){
  25. //切点定义
  26. }
  27. @Around(value = "doBefore(requestLimit)", argNames = "joinPoint,requestLimit")
  28. public Object doAround(ProceedingJoinPoint joinPoint, RequestLimit requestLimit) throws Throwable {
  29. ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  30. if(null == requestAttributes){
  31. log.info("限流失败,未获取到请求request");
  32. return joinPoint.proceed();
  33. }
  34. HttpServletRequest request = requestAttributes.getRequest();
  35. int time = requestLimit.time();
  36. int maxCount = requestLimit.count();
  37. StringJoiner key = new StringJoiner("");
  38. key.add(requestLimit.key());
  39. if(RequestLimitType.IP.code == requestLimit.limitType().code){
  40. key.add(WebUtils.getIP(request) + ":");
  41. }
  42. key.add(request.getRequestURI());
  43. String keyStr = key.toString();
  44. // 使用lua脚本执行,保证原子性,进行请求次数限制
  45. boolean isOutOfLimit = redisUtils.requestLimit(keyStr, time, maxCount);
  46. if(!isOutOfLimit){
  47. HttpServletResponse response = requestAttributes.getResponse();
  48. responseFailed(response, requestLimit.msg());
  49. return null;
  50. }
  51. return joinPoint.proceed();
  52. }
  53. private void responseFailed(HttpServletResponse response, String msg){
  54. ResponseVo result = new ResponseVo.Builder().error().message(msg).build();
  55. log.info(msg);
  56. WebUtils.responseOutJson(response, result);
  57. }
  58. }

7.在需要限流的接口上加自定义的注解

  1. package com.liu.controller;
  2. import com.liu.requestlimit.RequestLimit;
  3. import com.liu.requestlimit.RequestLimitType;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. @Slf4j
  9. @RestController
  10. @RequestMapping("/test")
  11. public class TestController {
  12. /**
  13. * 测试接口限流
  14. *
  15. * @author kevin
  16. * @date 2022/6/2 10:42
  17. */
  18. @RequestLimit(time = 10, count = 5, limitType = RequestLimitType.IP)
  19. @GetMapping("/testLimitRequest")
  20. public void testLimitRequest() throws InterruptedException {
  21. log.info("接口限流测试,开始处理被限流接口的业务......");
  22. Thread.sleep(2000);
  23. log.info("接口限流测试,处理被限流接口的业务结束!");
  24. }
  25. }

限流类型枚举类--RequestLimitType:

  1. package com.liu.requestlimit;
  2. @SuppressWarnings({"unused"})
  3. public enum RequestLimitType {
  4. DEFAULT(1, "全局限制"),
  5. IP(2, "通过IP限制");
  6. int code;
  7. String desc;
  8. RequestLimitType(int code, String desc){
  9. this.code = code;
  10. this.desc = desc;
  11. }
  12. /**
  13. * 通过代码值获取枚举
  14. * @author kevin
  15. * @param code :
  16. * @return com.cetccloud.base.enums.RequestLimitType
  17. * @date 2020/12/23 16:09
  18. */
  19. public static RequestLimitType getByCode(int code){
  20. for(RequestLimitType c : RequestLimitType.values()){
  21. if(code == c.getCode()){
  22. return c;
  23. }
  24. }
  25. return null;
  26. }
  27. /**
  28. * 通过描述获取枚举
  29. * @author kevin
  30. * @param desc :
  31. * @return com.cetccloud.base.enums.RequestLimitType
  32. * @date 2022/5/26
  33. */
  34. public static RequestLimitType getByDesc(String desc){
  35. for(RequestLimitType c : RequestLimitType.values()){
  36. if(desc.equals(c.getDesc())){
  37. return c;
  38. }
  39. }
  40. return null;
  41. }
  42. /**
  43. * 通过代码值获得代码描述
  44. * @author kevin
  45. * @param code :
  46. * @return java.lang.String
  47. * @date 2022/5/26
  48. */
  49. public static String getDesc(int code){
  50. for(RequestLimitType c : RequestLimitType.values()){
  51. if(code == c.getCode()){
  52. return c.desc;
  53. }
  54. }
  55. return null;
  56. }
  57. /**
  58. * 通过代码描述获得代码值
  59. * @author kevin
  60. * @param desc :
  61. * @return int
  62. * @date 2022/5/26
  63. */
  64. public static int getCode(String desc){
  65. for(RequestLimitType c : RequestLimitType.values()){
  66. if(desc.equals(c.getDesc())){
  67. return c.code;
  68. }
  69. }
  70. return -99;
  71. }
  72. public int getCode() {
  73. return code;
  74. }
  75. public String getDesc() {
  76. return desc;
  77. }
  78. }

8.直接jmeter测试并发

  1. 2022-06-06 14:55:53.430 INFO 13428 --- [nio-8081-exec-2] com.liu.controller.TestController : 接口限流测试,开始处理被限流接口的业务......
  2. 2022-06-06 14:55:53.430 INFO 13428 --- [nio-8081-exec-4] com.liu.controller.TestController : 接口限流测试,开始处理被限流接口的业务......
  3. 2022-06-06 14:55:53.430 INFO 13428 --- [nio-8081-exec-3] com.liu.controller.TestController : 接口限流测试,开始处理被限流接口的业务......
  4. 2022-06-06 14:55:53.430 INFO 13428 --- [nio-8081-exec-1] com.liu.controller.TestController : 接口限流测试,开始处理被限流接口的业务......
  5. 2022-06-06 14:55:53.430 INFO 13428 --- [nio-8081-exec-6] com.liu.controller.TestController : 接口限流测试,开始处理被限流接口的业务......
  6. 2022-06-06 14:55:53.555 INFO 13428 --- [nio-8081-exec-5] com.liu.requestlimit.RequestLimitAspect : 请求过于频繁,请稍后再试!
  7. 2022-06-06 14:55:53.555 INFO 13428 --- [nio-8081-exec-7] com.liu.requestlimit.RequestLimitAspect : 请求过于频繁,请稍后再试!
  8. 2022-06-06 14:55:53.555 INFO 13428 --- [nio-8081-exec-9] com.liu.requestlimit.RequestLimitAspect : 请求过于频繁,请稍后再试!
  9. 2022-06-06 14:55:53.555 INFO 13428 --- [nio-8081-exec-8] com.liu.requestlimit.RequestLimitAspect : 请求过于频繁,请稍后再试!
  10. 2022-06-06 14:55:53.648 INFO 13428 --- [io-8081-exec-10] com.liu.requestlimit.RequestLimitAspect : 请求过于频繁,请稍后再试!
  11. 2022-06-06 14:55:55.445 INFO 13428 --- [nio-8081-exec-6] com.liu.controller.TestController : 接口限流测试,处理被限流接口的业务结束!
  12. 2022-06-06 14:55:55.445 INFO 13428 --- [nio-8081-exec-2] com.liu.controller.TestController : 接口限流测试,处理被限流接口的业务结束!
  13. 2022-06-06 14:55:55.445 INFO 13428 --- [nio-8081-exec-1] com.liu.controller.TestController : 接口限流测试,处理被限流接口的业务结束!
  14. 2022-06-06 14:55:55.445 INFO 13428 --- [nio-8081-exec-4] com.liu.controller.TestController : 接口限流测试,处理被限流接口的业务结束!
  15. 2022-06-06 14:55:55.445 INFO 13428 --- [nio-8081-exec-3] com.liu.controller.TestController : 接口限流测试,处理被限流接口的业务结束!

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/382113
推荐阅读
相关标签
  

闽ICP备14008679号