programing

Spring Boot에서 각 사용자의 환율 제한을 설정하려면 어떻게 해야 합니까?

lastcode 2023. 2. 9. 21:58
반응형

Spring Boot에서 각 사용자의 환율 제한을 설정하려면 어떻게 해야 합니까?

많은 요청 콜을 처리하는 Spring Boot Rest API를 개발하고 있습니다.My Controller는 다음과 같습니다.

@RestController

public class ApiController {
    List<ApiObject>  apiDataList;   

    @RequestMapping(value="/data",produces={MediaType.APPLICATION_JSON_VALUE},method=RequestMethod.GET)
    public ResponseEntity<List<ApiObject>> getData(){                                       
        List<ApiObject> apiDataList=getApiData();
        return new ResponseEntity<List<ApiObject>>(apiDataList,HttpStatus.OK);
    }
    @ResponseBody 
    @Async  
    public List<ApiObject>  getApiData(){
        List<ApiObject>  apiDataList3=new List<ApiObject> ();
        //do the processing
        return apiDataList3;
    }
}

그래서 각 사용자의 환율 제한을 설정하려고 합니다.모든 사용자가 분당 5개의 요청만 요청할 수 있다고 가정해 보십시오.분당 5개의 API 콜만 발신하도록 각 사용자의 환율 제한을 설정하려면 어떻게 해야 합니까?또, 유저가 그 이상의 api 콜을 요구했을 경우, 429 응답을 반송할 수 있습니까?IP 주소가 필요합니까?

어떤 도움이라도 감사합니다.

각 유저(IP 주소)에 대해서, 초당 요구를 억제하려고 하는 유저를 위한 솔루션을 다음에 나타냅니다.이 솔루션에는 구글의 1.8+를 다시카페인 라이브러리가 필요합니다.Guava library 을 합니다.LoadingCache클래스: 요구 수 및 클라이언트 IP 주소를 저장합니다.,도합니다.javax.servlet-api는 ''의존관계'를입니다.servlet filter여기서 요구 카운트가 발생합니다.을 사용하다

import javax.servlet.Filter;


@Component
public class requestThrottleFilter implements Filter {

    private int MAX_REQUESTS_PER_SECOND = 5; //or whatever you want it to be

    private LoadingCache<String, Integer> requestCountsPerIpAddress;

    public requestThrottleFilter(){
      super();
      requestCountsPerIpAddress = Caffeine.newBuilder().
            expireAfterWrite(1, TimeUnit.SECONDS).build(new CacheLoader<String, Integer>() {
        public Integer load(String key) {
            return 0;
        }
    });
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        String clientIpAddress = getClientIP((HttpServletRequest) servletRequest);
        if(isMaximumRequestsPerSecondExceeded(clientIpAddress)){
          httpServletResponse.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
          httpServletResponse.getWriter().write("Too many requests");
          return;
         }

        filterChain.doFilter(servletRequest, servletResponse);
    }

    private boolean isMaximumRequestsPerSecondExceeded(String clientIpAddress){
      Integer requests = 0;
      requests = requestCountsPerIpAddress.get(clientIpAddress);
      if(requests != null){
          if(requests > MAX_REQUESTS_PER_SECOND) {
            requestCountsPerIpAddress.asMap().remove(clientIpAddress);
            requestCountsPerIpAddress.put(clientIpAddress, requests);
            return true;
        }

      } else {
        requests = 0;
      }
      requests++;
      requestCountsPerIpAddress.put(clientIpAddress, requests);
      return false;
      }

    public String getClientIP(HttpServletRequest request) {
        String xfHeader = request.getHeader("X-Forwarded-For");
        if (xfHeader == null){
            return request.getRemoteAddr();
        }
        return xfHeader.split(",")[0]; // voor als ie achter een proxy zit
    }

    @Override
    public void destroy() {

    }
}

으로는 IP 하는 모든 합니다.LoadingCache이것은 각 엔트리에 유효기간이 있는 특별한 맵과 같습니다.컨스트럭터에서는 유효기간이 1초로 설정되어 있습니다.「 」 、 「 IP 」 、 「 LoadingCache 」1 「 LoadingCache 」만료 시 지도에서 자동으로 삭제됩니다.는, 「1」의 「IP」가 .isMaximumRequestsPerSecondExceeded(String clientIpAddress)는 이러한 요구를 총 요구 수에 추가하지만 그 전에 초당 최대 요구량이 이미 초과되었는지 여부를 확인합니다.이 경우 true가 반환되고 필터는 Too many requests를 나타내는 상태 코드 429의 오류 응답을 반환합니다.

이렇게 하면 사용자당 초당 일정한 양의 요청만 수행할 수 있습니다.

.Caffeine: " " "에 "pom.xml

    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
        <exclusions>
            <exclusion>
                <artifactId>logback-classic</artifactId>
                <groupId>ch.qos.logback</groupId>
            </exclusion>
            <exclusion>
                <artifactId>log4j-over-slf4j</artifactId>
                <groupId>org.slf4j</groupId>
            </exclusion>
        </exclusions>
    </dependency>

★★★★★★★★★★★★★★★★★★★★★에 주의해 주세요.<exclusion> 을 사용하고 . 사용하고 있습니다.log4j2 Spring이 logback도서관.사용하시는 경우logback 나서 '나', '나', '나'를 삭제해 .<exclusion>이러한 POM 의존관계 또는 로깅은 이 라이브러리에서 활성화되지 않습니다.

편집: 필터가 저장된 패키지의 컴포넌트 스캔을 Spring에 맡겨야 합니다.그렇지 않으면 필터가 작동하지 않습니다.또, @Component로 주석이 붙어 있기 때문에, 필터는 디폴트(/*)로 모든 엔드 포인트에 대해서 동작합니다.

스프링이 필터를 검출했을 경우는, 기동시에 로그에 다음과 같은 것이 표시됩니다.

o.s.b.w.servlet.FilterRegistrationBean : Mapping filter:'requestThrottleFilter' to: [/*]

편집 19-01-2022:

초기 솔루션에는 너무 많은 요청을 차단하는 데 한 가지 단점이 있으며 이로 인해 코드를 변경했습니다.먼저 이유를 설명하겠습니다.

사용자가 초당 3개의 요청을 할 수 있다고 가정합니다.사용자가 1초 이내에 첫 번째 요구를 그 1초의 첫 번째 200밀리초 동안 실행한다고 가정합니다.의 엔트리가 ""에 됩니다.requestCountsPerIpAddress1번이 사용자는 두 번째가 경과하고 엔트리가 삭제될 때까지 마지막 100밀리초 동안만 4개의 연속된 요청을 수행한다고 가정합니다.즉, 사용자가 효과적으로 차단되는 것은 네 번째 요청 시도 시 최대 100밀리초뿐입니다.100밀리초가 지나면 즉시 3개의 새로운 요청을 할 수 있습니다.

를 할 수 되었습니다.는 첫 번째 요구 때 할 수 에 의해, 「」의 가 「500」으로됩니다).LoadingCache개개 2 개둘 ( 2 ) 둘밀밀500 )를밀밀 500를를를 ) 나 then 。 내에 을 할 수 반해 됩니다.사용자가 엔트리가 만료된 직후에 3개의 요청을 하면 1초 이내에 5개의 요청을 효과적으로 작성할 수 있습니다.단, 3개의 요청만 허용됩니다(이전 엔트리가 만료되기 전 마지막 500밀리초 동안 2개 + 새로운 엔트리의 처음 500밀리초 동안 3개).따라서 이는 요청을 억제하는 매우 효율적인 방법이 아닙니다.

구아바 라이브러리와 관련하여 교착상태에 빠진 문제가 있어서 도서관을 카페인으로 변경했습니다.를 계속 이 guava 행을 .requestCountsPerIpAddress.asMap().remove(clientIpAddress); 밑에if(requests > MAX_REQUESTS_PER_SECOND) {암호에 입력되어 있습니다.기본적으로는 IP 주소의 현재 엔트리를 삭제합니다.그 후 다음 줄에 다시 추가되어 해당 엔트리의 유효기간이 1초로 리셋됩니다.

이로 인해 사용자가 마지막 요청 후 1초 동안 요청 송신을 중지할 때까지 REST 엔드포인트에 계속 스팸을 보내는 것만으로 409 응답이 무기한 반환됩니다.

봄에는 그 부품이 없습니다.

  • 솔루션의 일부로 구축할 수 있습니다.필터를 생성하여 스프링 컨텍스트에 등록합니다.필터는, 착신 콜을 체크해, 시간대에 유저 마다의 착신 요구를 카운트 합니다.토큰 버킷알고리즘이 가장 유연하기 때문에 사용합니다.
  • 현재 솔루션과 독립적인 구성 요소를 구축할 수 있습니다.작업을 수행하는 API 게이트웨이를 만듭니다.Zuul 게이트웨이를 확장하고 토큰버킷 알고리즘을 사용할 수 있습니다.
  • API 게이트웨이로 동작할 수 있고 환율 제한 및 슬롯링을 지원하는 Mulesoft ESB와 같은 이미 내장된 컴포넌트를 사용할 수 있습니다.내가 직접 써본 적은 없어.
  • 마지막으로 환율 제한 및 조절 등을 지원하는 API Manager를 사용할 수 있습니다.Mull Soft, WSO2, 3Scale, Kong 등 체크 아웃...(대부분 비용이 들며 일부는 오픈 소스이며 커뮤니티 에디션도 있습니다).

스프링에는 개봉 즉시 환율 제한이 없습니다.

bucket4j-spring-boot-starter 프로젝트는 토큰 버킷알고리즘과 함께 bucket4j 라이브러리를 사용하여 REST api에 대한 액세스를 환율 제한합니다.응용 프로그램 속성 파일을 통해 구성할 수 있습니다.IP 주소 또는 유저명에 근거해 액세스를 제한하는 옵션이 있습니다.

사용자와는 독립적으로 10초 이내에 최대 5개의 요청을 허용하는 간단한 설정 예로서 다음과 같습니다.

bucket4j:
  enabled: true
  filters:
  - cache-name: buckets
    url: .*
    rate-limits:
    - bandwidths:
      - capacity: 5
    time: 10
    unit: seconds

Netflix Zuul을 사용하는 경우 다음과 같은 스토리지 옵션을 사용하는 Spring Cloud Zuul Rate Limit을 사용할 수 있습니다.영사, 레디스, 스프링 데이터 및 버킷4j.

언급URL : https://stackoverflow.com/questions/44042412/how-to-set-rate-limit-for-each-user-in-spring-boot

반응형