Spring Boot Undertow 在同一个应用程序中同时添加阻塞处理程序和 NIO 处理程序 [英] Spring Boot Undertow add both blocking handler and NIO handler in the same application
问题描述
在我的上一个问题(感谢@Andy Wilkinson) 我发现对 undertowEmbeddedServletContainer
的所有传入请求都由工作线程处理(阻塞操作).
In my previous question (thank @Andy Wilkinson) I figured out that all incoming requests to an undertowEmbeddedServletContainer
are handled by a worker thread (blocking operation).
根据 Andy 的说法,我尝试添加一个 UndertowBuilderCustomizer
以覆盖 ServletInitializerHandler
以使用非阻塞处理程序处理传入请求.
According to Andy, I try to add a UndertowBuilderCustomizer
in order to override the ServletInitializerHandler
to handle incoming requests with a non-blocking handler.
@Bean
public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory(){
UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory = new UndertowEmbeddedServletContainerFactory();
undertowEmbeddedServletContainerFactory.addBuilderCustomizers(new UndertowBuilderCustomizer() {
@Override
public void customize(Undertow.Builder builder) {
builder.setHandler(new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
exchange.getResponseSender().send("test");
}
});
}
});
return undertowEmbeddedServletContainerFactory;
}
在这个定制器中,我为 NIO 处理程序设置了构建器 rootHandler
.但它在启动阶段被 UndertowEmbeddedServletContainer
用 ServletInitializerHandler
覆盖:
In this customizer I set the builder rootHandler
for a NIO handler.
But it is overriden by UndertowEmbeddedServletContainer
at startup phase with a ServletInitializerHandler
:
private Undertow createUndertowServer() {
try {
HttpHandler servletHandler = this.manager.start();
this.builder.setHandler(getContextHandler(servletHandler));
return this.builder.build();
}
catch (ServletException ex) {
throw new EmbeddedServletContainerException(
"Unable to start embdedded Undertow", ex);
}
}
正如这个问题的标题所说:我试图同时拥有阻塞和非阻塞处理程序,其中阻塞处理程序通过 @Controller
注释管理,而 NIO 处理程序由 Spring 管理.
As the title of this question says: I'm trying to have both blocking and non-blocking handlers, where blocking handlers are managed through @Controller
annotation, and where NIO handlers are managed by Spring.
我找到了一个解决方案,但作为初学者,我不知道它是否是一个好方法.
I found a solution, but as a beginner, I don't know if it's a good one.
HandlerPath 注释
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.TYPE})
public @interface HandlerPath {
public String path() default "";
}
创建实现HttpHandler的bean
@Component
@HandlerPath(path = "/hello-nio")
public class HelloHandler implements HttpHandler{
@Autowired
HelloService helloService;
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
exchange.getResponseSender().send(helloService.sayHello("Josselin"));
}
}
创建一个简单的控制器
@Controller
public class HelloController {
@RequestMapping("/hello")
@ResponseBody
public String sayHello(){
return "hello";
}
}
创建一个实现 ServletExtension 的类
public class NonBlockingHandlerExtension implements ServletExtension{
@Override
public void handleDeployment(DeploymentInfo deploymentInfo, final ServletContext servletContext) {
deploymentInfo.addInitialHandlerChainWrapper(new HandlerWrapper() {
@Override
public HttpHandler wrap(final HttpHandler handler) {
WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
Map<String, Object> handlers = ctx.getBeansWithAnnotation(HandlerPath.class);
PathHandler rootHandler = new PathHandler();
rootHandler.addPrefixPath("/", handler);
for(Map.Entry<String, Object> handlerEntry : handlers.entrySet()){
if(handlerEntry.getValue() instanceof HttpHandler){
HttpHandler httpHandler = (HttpHandler) handlerEntry.getValue();
String path = httpHandler.getClass().getAnnotation(HandlerPath.class).path();
rootHandler.addPrefixPath(path, httpHandler);
}
}
return rootHandler;
}
});
}
}
在这个方法中,默认的 ServletInitializer
处理程序绑定到/"上下文,并由 spring 管理,因此所有阻塞请求都可以由 @Controller
(s).然后我尝试发现所有用@HandlerPath
注释的bean,然后根据@HandlerPath 添加一个新的
属性.prefixPath
到rootHandler
.path
In this method, the default ServletInitializer
handler is bound to "/" context, and managed by spring, so all blocking requests could be handled by @Controller
(s).
Then I try to discover all beans that are annotated with @HandlerPath
, then add a new prefixPath
to the rootHandler
based on @HandlerPath.path
property.
终于
创建目录 META-INF.services
Create a directory META-INF.services
创建一个文件 io.undertow.servlet.ServletExtension 并添加一行:
Create a file io.undertow.servlet.ServletExtension and add line:
org.me.undertow.NonBlockingHandlerExtension
结果
一切都像魅力一样工作,当绑定 URL 被命中时会调用 NIO 处理程序,阻塞处理程序也是如此.
All is working like a charm, NIO handlers are called when binding URL are hit, so do blocking handlers.
谁能告诉我这个解决方案是否可以以任何方式改进?此外,由于 NIO 处理程序 URL 不是由 Spring 管理的,我想我必须使用 globaleMethodSecurity
并设置 @PreAuthorize
来保护 NIO 处理程序?
Could anyone please let me know if this solution could be improved in any way?
Moreover, as NIO handler URLs are not managed by Spring, I guess I have to use globaleMethodSecurity
and set @PreAuthorize
to secure the NIO handler?
推荐答案
我最近遇到了类似的问题,我发现 UndertowEmbeddedServletContainerFactory
提供了 addDeploymentInfoCustomizers()
可用于将自定义 HttpHandler
放在处理程序链的开头.
I had similar problem lately and I discovered that UndertowEmbeddedServletContainerFactory
provides addDeploymentInfoCustomizers()
which can be used to put custom HttpHandler
at the beginning of handlers chain.
@Bean
public UndertowEmbeddedServletContainerFactory embeddedServletContainerFactory(RootHandler rootHandler) {
UndertowEmbeddedServletContainerFactory factory = new UndertowEmbeddedServletContainerFactory();
factory.addDeploymentInfoCustomizers(deploymentInfo ->
deploymentInfo.addInitialHandlerChainWrapper(rootHandler::setNext));
return factory;
}
示例 RootHandler
@Component
public class RootHandler implements HttpHandler {
private HttpHandler next;
@Autowired
public RootHandler(...) {
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
if (exchange.getRelativePath().startsWith("/service")) {
handleServiceRequest(exchange);
} else {
next.handleRequest(exchange);
}
}
private void handleServiceRequest(HttpServerExchange exchange) {
// ...
exchange.getResponseSender().send("OK");
}
public HttpHandler setNext(HttpHandler next) {
this.next = next;
return this;
}
}
这篇关于Spring Boot Undertow 在同一个应用程序中同时添加阻塞处理程序和 NIO 处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!