2

以前の質問(@Andy Wilkinson に感謝) で、への着信要求はすべてワーカーundertowEmbeddedServletContainerスレッド (ブロック操作) によって処理されることがわかりました。

Andy によると、着信リクエストをノンブロッキング ハンドラで処理するUndertowBuilderCustomizerために をオーバーライドするために、 を追加しようとしています。ServletInitializerHandler

@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;
}

このカスタマイザーrootHandlerでは、NIO ハンドラーのビルダーを設定します。ただしUndertowEmbeddedServletContainer、起動段階で次のようにオーバーライドされます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 によって管理されます。

解決策を見つけましたが、初心者として、それが良いものかどうかわかりません。

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;
            }
        });
    }
}

このメソッドでは、デフォルトハンドラーは "/" コンテキストにバインドされ、Spring によって管理されるため、すべてのブロッキング リクエストは(s)ServletInitializerによって処理できます。@Controller次に、 で注釈が付けられたすべての Bean を検出してから、based onプロパティに@HandlerPath新しい を追加しようとします。prefixPathrootHandler@HandlerPath.path

ついに

ディレクトリ META-INF.services を作成します

ファイル io.undertow.servlet.ServletExtension を作成し、次の行を追加します。

org.me.undertow.NonBlockingHandlerExtension

結果

すべてが魅力のように機能し、バインディング URL がヒットすると NIO ハンドラーが呼び出され、ブロッキング ハンドラーも呼び出されます。

このソリューションを何らかの方法で改善できるかどうか、誰かに教えてもらえますか? さらに、NIO ハンドラーの URL は Spring によって管理されていないため、NIO ハンドラーを保護するために使用globaleMethodSecurityおよび設定する必要があると思いますか?@PreAuthorize

4

1 に答える 1

0

私は最近同様の問題を抱えており、ハンドラーチェーンの先頭にカスタムを配置するために使用できるUndertowEmbeddedServletContainerFactory提供を発見しました。addDeploymentInfoCustomizers()HttpHandler

@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;
    }
}
于 2016-04-27T21:35:32.863 に答える