Ограничить количество обработанных запросов на запрос - PullRequest
0 голосов
/ 07 января 2019

у нас есть служба, которая имеет одну конечную точку, которую нужно ограничить обработкой 2 запросов одновременно. Эти 2 запроса могут занять некоторое время.

В настоящее время мы используем для этого свойства tomcat. Проблема, с которой мы сейчас сталкиваемся, заключается в том, что, когда эти 2 потока используются для этой конечной точки, наша проверка работоспособности больше не работает.

Итак, мы хотели бы ограничить количество запросов для этой конкретной конечной точки. Мы немного подумали об этом, и одна идея заключалась в том, чтобы сделать это с помощью фильтра, но мне это кажется очень глупым ...

Значит, я надеялся, что у кого-то есть другая идея?

Ответы [ 2 ]

0 голосов
/ 07 января 2019

Вот пример того, как реализовать асинхронный контроллер REST, который будет обрабатывать не более 2 одновременных запросов одновременно. Эта реализация не будет блокировать ни один из потоков сервлетов Tomcat во время обработки запросов.

Если во время выполнения двух поступает еще один, вызывающий абонент получит HTTP 429 (слишком много запросов).

В этом примере немедленно отклоняются запросы, которые нельзя обработать с помощью 429. Если вместо этого вы хотите поставить в очередь ожидающие запросы до тех пор, пока не станет доступен один из 2 потоков обработки, замените SynchronousQueue другой реализацией BlockingQueue.

Возможно, вы захотите привести этот пример в порядок, я намеренно включил здесь все классы, используемые для его подстановки:

@Configuration
@RestController
public class TestRestController {

  static class MyRunnable implements Runnable {

    DeferredResult<ResponseEntity<String>> deferredResult;

    MyRunnable(DeferredResult<ResponseEntity<String>> dr) {
      this.deferredResult = dr;
    }

    @Override
    public void run() {
      // do your work here and adjust the following
      // line to set your own result for the caller...

      this.deferredResult.setResult(ResponseEntity.ok("it worked"));
    }
  }

  @SuppressWarnings("serial")
  @ResponseStatus(HttpStatus.TOO_MANY_REQUESTS)
  static class TooManyRequests extends RuntimeException {
  }

  private final ExecutorService executorService = new ThreadPoolExecutor(2, 2,
      0L, TimeUnit.MILLISECONDS,
      new SynchronousQueue<Runnable>(),
      (runnable, executor) -> {
        ((MyRunnable) runnable).deferredResult.setErrorResult(new TooManyRequests());
      });


  @GetMapping(value = "/blah", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  public DeferredResult<ResponseEntity<String>> yourRestService() {

    final DeferredResult<ResponseEntity<String>> deferredResult = new DeferredResult<>();
    this.executorService.execute(new MyRunnable(deferredResult));

    return deferredResult;
  }
}
0 голосов
/ 07 января 2019

По умолчанию RequestMappingHandlerAdapter обрабатывает @Controller @RequestMapping методы. Поэтому самый простой способ - создать свой собственный RequestMappingHandlerAdapter и переопределить его handleInternal, чтобы добавить свою логику управления.

Ниже псевдокод:

public static class MyRequestMappingHandlerAdapter extends RequestMappingHandlerAdapter {

   //counter to keep track number of concurrent request for each HandlerMethod
   //HandlerMethod represent a @RequestMapping method
   private Map<HandlerMethod, Integer> requestCounterMap = new ConcurrentHashMap<>();

    @Override
    protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response,
            HandlerMethod handlerMethod) throws Exception {

        //Increase the counter for this handlerMethod by 1.
        //Throw exception if the counter is more than 2 request

        ModelAndView mv = super.handleInternal(request, response, handlerMethod);

        //Method finish , decrease the counter by 1 

        return mv;
    }

}

Предположим, что вы используете автоконфигурацию MVC с пружинной загрузкой, вы можете заменить RequestMappingHandlerAdapter на свой, настроив бин WebMvcRegistrations и переопределить его getRequestMappingHandlerAdapter() методы:

@Bean
public WebMvcRegistrations webMvcRegistrations() {
    return new WebMvcRegistrations() {

        @Override
        public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
            return new MyRequestMappingHandlerAdapter();
        }
    };
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...