AsyncHandlerInterceptor - запрос диспетчера ASYNC не вызывается при закрытии клиента
Ну, это исключительное поведение, необходимое для нашего варианта использования. Мы используем обработчик-перехватчик для "увеличения количества активных запросов" в методе "preHandle". В методе afterCompletion мы уменьшаем счетчик активных запросов. Все идет нормально. Синхронизация вызовов прямолинейна. Но в случае асинхронности существует второй запрос (DispatcherType: ASYNC), который используется для уменьшения счетчика, в то время как основной запрос (DispatcherType: REQUEST) используется для увеличения счетчика. Мы проверяем тип диспетчера, чтобы избежать двойного приращения. Пока все хорошо.
Проблема возникает в случае некоторых проблемных клиентов, которые отключаются после запуска запроса. В этом случае, когда основной запрос поступает на сервер, но перед тем, как асинхронный поток запускается, клиент отключается (например, закрывает браузер). В этом случае второй запрос (DispatcherType: ASYNC) вообще не создается. Эта ситуация с выходом из прилавка усилилась (по основному запросу).Для нашего варианта использования мы должны уменьшить счетчик независимо от того, что если он увеличивается для запроса. С нетерпением ждем вашей помощи / предложения здесь. Заранее спасибо.
Другие детали: Spring Boot application Spring Framework: 4.3.4.RELEASE Tomcat: 7 Использование перехватчика RestController Перехватчик: AsyncHandlerInterceptor В асинхронном режиме мы используем ResponseBodyEmitter для отправки данных клиенту
Войти на сервер:
Exception in thread "pool-87-thread-1" java.lang.IllegalStateException: The request associated with the **AsyncContext has already completed processing**.
at org.apache.catalina.core.AsyncContextImpl.check(AsyncContextImpl.java:497)
at org.apache.catalina.core.AsyncContextImpl.getRequest(AsyncContextImpl.java:209)
at org.apache.catalina.core.AsyncContextImpl.dispatch(AsyncContextImpl.java:198)
at org.apache.catalina.core.AsyncContextImpl.dispatch(AsyncContextImpl.java:170)
at org.apache.catalina.core.AsyncContextImpl.dispatch(AsyncContextImpl.java:164)
at org.springframework.web.context.request.async.StandardServletAsyncWebRequest.dispatch(StandardServletAsyncWebRequest.java:123)
at org.springframework.web.context.request.async.WebAsyncManager.setConcurrentResultAndDispatch(WebAsyncManager.java:353)
at org.springframework.web.context.request.async.WebAsyncManager.access$200(WebAsyncManager.java:58)
at org.springframework.web.context.request.async.WebAsyncManager$7.handleResult(WebAsyncManager.java:416)
at org.springframework.web.context.request.async.DeferredResult.setResultInternal(DeferredResult.java:199)
at org.springframework.web.context.request.async.DeferredResult.setErrorResult(DeferredResult.java:214)
at org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler$HttpMessageConvertingHandler.completeWithError(ResponseBodyEmitterReturnValueHandler.java:219)
at org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter.completeWithError(ResponseBodyEmitter.java:204)
at org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter.sendInternal(ResponseBodyEmitter.java:169)
at org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter.send(ResponseBodyEmitter.java:159)
at net.atpco.pipeline.common.post.KryoResponseEmitterPostBox.send(KryoResponseEmitterPostBox.java:48)
at net.atpco.pipeline.common.post.KryoResponseEmitterPostBox.lambda$0(KryoResponseEmitterPostBox.java:37)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Обновления: В ходе дальнейших исследований я обнаружил это в весенней документации... "Обратите внимание, что реализациям HandlerInterceptor может потребоваться работа, когда асинхронный запрос заканчивается или завершается с сетевой ошибкой. В таких случаях контейнер сервлетов не отправляет и, следовательно, postHandle и методы afterCompletion не будут вызываться. Вместо этого перехватчики могут регистрироваться для отслеживания асинхронного запроса через методы registerCallbackInterceptor и registerDeferredResultInterceptor в WebAsyncManager. Это можно делать с упреждением при каждом запросе из preHandle независимо от того, начнется ли обработка асинхронного запроса. " Этот deferredResultInterceptor выглядит как решение проблемы.