Какие этапы прохождения HTTP запроса в Spring WebFlux до контроллера
Что проверяют
Знаете ли вы внутреннюю цепочку WebFlux, а не только аннотации, и видите ли отличия от Spring MVC. Следом обычно спрашивают, где подключается Spring Security или чем опасен блокирующий вызов в фильтре.
Как отвечать
Путь запроса в приложении на Spring Boot с Reactor Netty:
- сервер и адаптер: Netty принимает соединение,
ReactorHttpHandlerAdapterоборачивает его вServerHttpRequestиServerHttpResponseи вызываетHttpHandler; HttpWebHandlerAdapter: создаётServerWebExchange, объект с запросом, ответом, атрибутами и сессией;- обработка исключений: цепочка обёрнута в
ExceptionHandlingWebHandler, ошибки уходят кWebExceptionHandler, в Boot этоDefaultErrorWebExceptionHandler; WebFilter: фильтры по порядку@Order, здесь работаютWebFilterChainProxyиз Spring Security, CORS, логирование. Дальше управление передаётchain.filter(exchange);DispatcherHandler: перебираетHandlerMapping.RouterFunctionMappingобслуживает функциональные маршруты и проверяется раньше,RequestMappingHandlerMappingотвечает за@RequestMapping;HandlerAdapter: для контроллераRequestMappingHandlerAdapterразрешает аргументы, тело читаетHttpMessageReader(для JSON на базе Jackson), затем валидация и вызов метода;- результат: возвращённый
MonoилиFluxобрабатываетHandlerResultHandler, напримерResponseBodyResultHandlerпишет тело черезHttpMessageWriter.
Оговорка: всё это сначала собирается в реактивный конвейер, а выполняется, когда сервер подписывается на итоговый Mono<Void>. Потоков event loop мало, поэтому код на пути запроса не должен блокировать.
Где спотыкаются
Описывают Spring MVC: DispatcherServlet, сервлетные Filter, HandlerInterceptor. В WebFlux интерсепторов нет, их роль выполняет WebFilter.
Ставят RouterFunction и контроллер в одну цепочку. Это два альтернативных способа объявить обработчик, между ними выбирает DispatcherHandler.
Ходят в фильтре через JDBC за правами пользователя. Поток event loop встаёт, и под нагрузкой задержка растёт у всех запросов сразу. Такой вызов уводят в subscribeOn(Schedulers.boundedElastic()) или переводят на R2DBC.