2014-12-19 60 views
1

在我的previous question(謝謝@Andy Wilkinson)中,我發現所有到undertowEmbeddedServletContainer的傳入請求都由工作線程(阻塞操作)處理。Spring Boot Undertow在同一應用程序中添加阻塞處理程序和NIO處理程序

根據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管理註釋,以及Spring處理NIO處理程序的位置。

我找到了一個解決方案,但作爲初學者,我不知道它是否是一個好的解決方案。

HandlerPath註釋

@Retention(RetentionPolicy.RUNTIME) 
@Documented 
@Target({ElementType.TYPE}) 
public @interface HandlerPath { 

    public String path() default ""; 

} 

創建豆實現的HttpHandler

@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處理程序綁定到「/」上下文,並且管理由彈簧,因此,所有阻止請求可以通過@Controller(多個)進行處理。 然後,我嘗試發現所有註釋爲@HandlerPath的豆,然後根據@HandlerPath.path屬性向rootHandler添加新的prefixPath

最後

創建一個目錄META-INF.services

創建一個文件io.undertow.servlet.ServletExtension,並添加一行:

org.me.undertow.NonBlockingHandlerExtension 

結果

所有工作都像一個魅力,NIO處理程序被稱爲bi時找到URL,所以阻止處理程序。

任何人都可以請讓我知道,如果這種解決方案可以改善任何方式? 此外,由於NIO處理程序的URL不是由Spring管理的,我想我必須使用globaleMethodSecurity並設置來保護NIO處理程序?

回答

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