SpringBoot整合RateLimiter实现限流

简介: 写作缘由 在和某学长炫耀在自己会用Redis+Lua实现滑动窗口限流时,他说现在都用RateLimiter,所以就我就想搞个Demo,但是度娘了一下,感觉我搜索到的博客有几个个人认为不太完善的地方,比如只贴了部分代码,没贴依赖。尤其是你用AOP实现的时候,其实依赖哪个还有有讲究的;还有一个问题就是大多都是基于AOP实现,拦截器实现也是一个不错的方式,所以此处用拦截器HandlerInterceptorAdapter实现。

源码下载


https://github.com/cbeann/Demooo/tree/master/springboot-ratelimiter


部分代码


pom


 <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>28.1-jre</version>
        </dependency>


自定义注解ExtRateLimiter


package com.example.annotation;
import java.lang.annotation.*;
/**
 * @author chaird
 * @create 2021-03-20 17:57
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExtRateLimiter {
  // 以每秒为单位固定的速率值往令牌桶中添加令牌
  double permitsPerSecond();
  // 在规定的毫秒数中,如果没有获取到令牌的话,则直接走服务器降级处理
  long timeout();
}


拦截器


package com.example.Interceptor;
import com.example.annotation.ExtRateLimiter;
import com.google.common.util.concurrent.RateLimiter;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
 * @author CBeann
 * @create 2020-07-04 18:06
 */
@Component
public class RateLimiterInceptor extends HandlerInterceptorAdapter {
  private Map<String, RateLimiter> rateHashMap = new ConcurrentHashMap<>();
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
    if (!(handler instanceof HandlerMethod)) {
      return true;
    }
    final HandlerMethod handlerMethod = (HandlerMethod) handler;
    final Method method = handlerMethod.getMethod();
    // 有这个注解
    boolean methodAnn = method.isAnnotationPresent(ExtRateLimiter.class);
    if (methodAnn) {
      // 获取注解
      ExtRateLimiter extRateLimiter = method.getDeclaredAnnotation(ExtRateLimiter.class);
      //获取注解属性
      double permitsPerSecond = extRateLimiter.permitsPerSecond();
      long timeout = extRateLimiter.timeout();
      String key = method.getDeclaringClass().getName() + method.getName();
      RateLimiter rateLimiter = null;
      if (rateHashMap.get(key) == null) {
        rateLimiter = RateLimiter.create(permitsPerSecond);
        rateHashMap.put(key, rateLimiter);
      } else {
        rateLimiter = rateHashMap.get(key);
      }
      boolean tryAcquire = rateLimiter.tryAcquire(timeout, TimeUnit.MILLISECONDS);
      if (!tryAcquire) {
        response.setContentType("application/json; charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print("限流");
        writer.close();
        response.flushBuffer();
        return false;
      }
      return super.preHandle(request, response, handler);
    }
    return true;
  }
  @Override
  public void postHandle(
      HttpServletRequest request,
      HttpServletResponse response,
      Object handler,
      ModelAndView modelAndView)
      throws Exception {
    super.postHandle(request, response, handler, modelAndView);
  }
}


拦截器配置


package com.example.config;
import com.example.Interceptor.RateLimiterInceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
 * @author chaird
 * @create 2020-09-23 16:13
 */
@Configuration
public class MVCConfig extends WebMvcConfigurerAdapter {
  @Autowired private RateLimiterInceptor rateLimiterInceptor;
  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    // 获取http请求拦截器
    registry.addInterceptor(rateLimiterInceptor).addPathPatterns("/*");
  }
}


controller


package com.example.controller;
import com.example.annotation.ExtRateLimiter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class StockController {
  @GetMapping("/getStock")
  @ExtRateLimiter(permitsPerSecond = 2, timeout = 500)
  public Object getStock() {
    String s = "ok";
    return s;
  }
}


测试


大约每秒允许2个请求


http://localhost:8080/getStock

目录
相关文章
|
6月前
|
缓存 Java Sentinel
Springboot 中使用 Redisson+AOP+自定义注解 实现访问限流与黑名单拦截
Springboot 中使用 Redisson+AOP+自定义注解 实现访问限流与黑名单拦截
|
Java
springboot实现自定义注解限流
springboot实现自定义注解限流
137 1
|
3月前
|
算法 Java UED
你的Spring Boot应用是否足够健壮?揭秘限流功能的实现秘诀
【8月更文挑战第29天】限流是保障服务稳定性的关键策略,通过限制单位时间内的请求数量防止服务过载。本文基于理论介绍,结合Spring Boot应用实例,展示了使用`@RateLimiter`注解和集成`Resilience4j`库实现限流的方法。无论采用哪种方式,都能有效控制请求速率,增强应用的健壮性和用户体验。通过这些示例,读者可以灵活选择适合自身需求的限流方案。
84 2
|
4月前
|
算法 Java 数据库连接
Spring Boot中的限流策略实现
Spring Boot中的限流策略实现
|
5月前
|
NoSQL 算法 Java
使用 Spring Boot 实现限流功能:从理论到实践
【6月更文挑战第18天】在微服务和高并发系统中,限流(Rate Limiting)是一种非常重要的技术手段,用于保护系统免受过载,确保服务的稳定性。限流可以控制请求的速率,防止单个客户端或恶意用户消耗过多的资源,从而影响其他用户。
400 5
|
6月前
|
算法 NoSQL Java
springboot整合redis及lua脚本实现接口限流
springboot整合redis及lua脚本实现接口限流
245 0
|
6月前
|
前端开发 NoSQL Java
【SpringBoot】秒杀业务:redis+拦截器+自定义注解+验证码简单实现限流
【SpringBoot】秒杀业务:redis+拦截器+自定义注解+验证码简单实现限流
159 0
|
6月前
|
存储 算法 Java
Spring Boot 通用限流方案
Spring Boot 通用限流方案
511 0
|
6月前
|
算法 NoSQL Java
8. 「Java大师」教你如何用Spring Boot轻松实现高效「限流」!
8. 「Java大师」教你如何用Spring Boot轻松实现高效「限流」!
279 0
|
NoSQL Java Redis
springboot高级教程基于 redis 通过注解实现限流
springboot高级教程基于 redis 通过注解实现限流
158 0