Spring Boot Undertow 在同一个应用程序中同时添加阻塞处理程序和 NIO 处理程序 [英] Spring Boot Undertow add both blocking handler and NIO handler in the same application

查看:49
本文介绍了Spring Boot Undertow 在同一个应用程序中同时添加阻塞处理程序和 NIO 处理程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的上一个问题(感谢@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.但它在启动阶段被 UndertowEmbeddedServletContainerServletInitializerHandler 覆盖:

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 处理程序由 Sp​​ring 管理.

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 添加一个新的prefixPathrootHandler.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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆