Заголовки HttpServletRequest исчезают при использовании CompletableFuture - PullRequest
1 голос
/ 23 февраля 2020

Во-первых, мои извинения за столь смутное название вопроса. Я новичок в асинхронном программировании в Java (и, следовательно, CompletableFuture ), и я пытаюсь поэкспериментировать с ним, используя функцию Spring * @Async.

У меня есть следующее AsyncConfig. java

@Configuration
public class AsyncConfiguration {

    private static final Logger LOG = LogManager.getLogger(AsyncConfiguration.class);

    @Bean("asyncExecutor")
    public Executor asyncExecutor() {

        LOG.info("Configuring ASYNC Executor");

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(1);
        executor.setMaxPoolSize(2);
        executor.setQueueCapacity(2);
        executor.setThreadNamePrefix("AsyncThread--");
        executor.initialize();

        LOG.info("ASYNC Executor Configuration Complete");

        return executor;
    }

}

Теперь у меня есть два фильтра, ApiUsageLimitFilter с @Order(1) и RequestResponseLoggingFilter с @Order(2)

В doFilter из ApiUsageLimitFilter.java у меня есть следующий код:

@Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        LOG.info("API Filter Request handled by Thread {} -> {}", Thread.currentThread().getName(),
                Thread.currentThread().getId());
        final HttpServletRequest httpServletRequest = HttpServletRequest.class.cast(request);
        HttpServletResponse httpServletResponse = HttpServletResponse.class.cast(response);
        try {
            LOG.info("Checking usgae limit for IP: {}", httpServletRequest.getHeader("X-Real-IP"));
            apiUsageMonitorService.isAllowed(httpServletRequest)  //Some method that returns a CompletableFuture<Boolean>
            .thenCompose((allowed) -> {
                if (allowed.equals(Boolean.TRUE)) {
                    LOG.info("IP: {} allowed", httpServletRequest.getHeader("X-Real-IP")); // Value is NULL
                    try {
                        chain.doFilter(httpServletRequest, httpServletResponse);
                    } catch (IOException | ServletException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    return null;
                } else {
                    LOG.warn("IP: {} reached its usage limit. Blocking any further calls",
                            httpServletRequest.getHeader("X-Real-IP"));
                    return apiUsageMonitorService.remainingTTL(httpServletRequest);
                }
            }).thenAccept((remainingTTL) -> {

                System.out.println("This gets executed");
                if (remainingTTL != null) {
                    System.out.println("SOme of this gets executed");
                    try {

                        ApiResponse errorResponse = new ApiResponse();
                        errorResponse.setStatus(ApiRequestStatus.FAILURE);
                        errorResponse.setMessage(
                                "You have reached the API usage limit. Only 10 requests allowed per hour. Please try after the time specified in Retry-After header");
                        errorResponse.setErrorCode(ApiRequestErrorCode.API_USAGE_LIMIT_REACHED);
                        String errorResponseString = getResponseAsString(errorResponse);
                        LOG.info("Error response as String:{}", errorResponseString);
                        httpServletResponse.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());

                        Long remainingSeconds = TimeUnit.MILLISECONDS.toSeconds(remainingTTL);
                        httpServletResponse.setHeader("Retry-After", remainingSeconds.toString() + " seconds");
                        httpServletResponse.getWriter().write(errorResponseString);
                    } catch (Exception e) {
                        // TODO: handle exception
                    }

                }
            });

Приведенный выше метод вызывает службу, которая в основном возвращает CompletableFuture из boolean и на основе этого значения я либо вызываю следующий фильтр в цепочке, либо возвращаю ответ обратно.

Что происходит, прежде чем вызов переходит на apiUsageMonitorService.isAllowed(httpServletRequest) строка, объект запроса существует, и я вижу заголовок. Однако, когда внутри thenCompose метода apiUsageMonitorService.isAllowed(httpServletRequest), который возвращает CompletableFuture, я не могу видеть это значение заголовка X-Real- IP больше. Он печатает ноль.

Я попробовал то же самое без асинхронного c кода и смог получить доступ к заголовку запроса даже после этого конкретного вызова. Более того, звонок шел и к следующему фильтру в цепочке. Но при использовании CompletableFuture заголовок просто не существует в этой точке после вызова, и он также не go до RequestResponseLoggingFilter, который является следующим фильтром в цепочке, потому что я не вижу любые журналы, сделанные этим фильтром.

Ниже приведены журналы:

2020-02-23 23: 12: 53.721 INFO 28774 --- [nio-8080-exe c -3]

i.turls.lib.filters.ApiUsageLimitFilter  : API Filter Request handled by Thread http-nio-8080-exec-3 -> 58
2020-02-23 23:12:53.721  INFO 28774 --- [nio-8080-exec-3] i.turls.lib.filters.ApiUsageLimitFilter  : Checking usgae limit for IP: 122.168.23.274
2020-02-23 23:12:53.721  INFO 28774 --- [ AsyncThread--1] i.t.l.s.i.ApiUsageMonitorServiceImpl     : Running Async Method on Thread AsyncThread--1 -> 70
2020-02-23 23:12:53.721  INFO 28774 --- [ AsyncThread--1] i.t.l.s.i.ApiUsageMonitorServiceImpl     : Checking API usage for IP: 122.168.23.274
2020-02-23 23:12:53.729  INFO 28774 --- [ AsyncThread--1] i.t.l.s.i.ApiUsageMonitorServiceImpl     : Creating new entry with IP 122.168.23.274
2020-02-23 23:12:53.738  INFO 28774 --- [ AsyncThread--1] i.turls.lib.filters.ApiUsageLimitFilter  : IP: null allowed

Что здесь не так? Неужели когда CompletableFuture заканчивается, объект запроса давно исчез, потому что Фильтры реализуют синхронный метод? Если да, то как мне решить эту проблему? Я должен использовать это asyn c way

...