SpringBoot Undertow : how to dispatch to worker thread

匿名 (未验证) 提交于 2019-12-03 02:33:02

问题:

i'm currently have a look a springboot undertow and it's not really clear (for me) how to dispatch an incoming http request to a worker thread for blocking operation handling.

Looking at the class UndertowEmbeddedServletContainer.class, it look like there is no way to have this behaviour since the only HttpHandler is a ServletHandler, that allow @Controller configurations

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);     } }  private HttpHandler getContextHandler(HttpHandler servletHandler) {     if (StringUtils.isEmpty(this.contextPath)) {         return servletHandler;     }     return Handlers.path().addPrefixPath(this.contextPath, servletHandler);  } 

By default, in undertow all requests are handled by IO-Thread for non blocking operations. Does this mean that every @Controller executions will be processed by a non blocking thread ? or is there a solution to chose from IO-THREAD or WORKER-THREAD ?

I try to write a workaround, but this code is pretty uggly, and maybe someone has a better solution:

BlockingHandler.class

@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface BlockingHandler {      String contextPath() default "/";  } 

UndertowInitializer.class

public class UndertowInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {      @Override     public void initialize(ConfigurableApplicationContext configurableApplicationContext) {         configurableApplicationContext.addBeanFactoryPostProcessor(new UndertowHandlerPostProcessor());     }  } 

UndertowHandlerPostProcessor.class

public class UndertowHandlerPostProcessor implements BeanDefinitionRegistryPostProcessor {       @Override     public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {         ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);         scanner.addIncludeFilter(new AnnotationTypeFilter(BlockingHandler.class));         for (BeanDefinition beanDefinition : scanner.findCandidateComponents("org.me.lah")){              try{                 Class clazz = Class.forName(beanDefinition.getBeanClassName());                 beanDefinitionRegistry.registerBeanDefinition(clazz.getSimpleName(), beanDefinition);             } catch (ClassNotFoundException e) {                 throw new BeanCreationException(format("Unable to create bean %s", beanDefinition.getBeanClassName()), e);             }         }     }       @Override     public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {         //no need to post process defined bean     } } 

override UndertowEmbeddedServletContainerFactory.class

public class UndertowEmbeddedServletContainerFactory extends  AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware, ApplicationContextAware {      private ApplicationContext applicationContext;      @Override     public EmbeddedServletContainer getEmbeddedServletContainer(ServletContextInitializer... initializers) {         DeploymentManager manager = createDeploymentManager(initializers);         int port = getPort();         if (port == 0) {             port = SocketUtils.findAvailableTcpPort(40000);         }         Undertow.Builder builder = createBuilder(port);          Map<String, Object> handlers = applicationContext.getBeansWithAnnotation(BlockingHandler.class);         return new UndertowEmbeddedServletContainer(builder, manager, getContextPath(),             port, port >= 0, handlers);     }      @Override     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {         this.applicationContext = applicationContext;     } }  ... 

override UndertowEmbeddedServletContainer.class

public UndertowEmbeddedServletContainer(Builder builder, DeploymentManager manager,                                         String contextPath, int port, boolean autoStart, Map<String, Object> handlers) {     this.builder = builder;     this.manager = manager;     this.contextPath = contextPath;     this.port = port;     this.autoStart = autoStart;     this.handlers = handlers; }  private Undertow createUndertowServer() {     try {         HttpHandler servletHandler = this.manager.start();         String path = this.contextPath.isEmpty() ? "/" : this.contextPath;         PathHandler pathHandler = Handlers.path().addPrefixPath(path, servletHandler);         for(Entry<String, Object> entry : handlers.entrySet()){             Annotation annotation = entry.getValue().getClass().getDeclaredAnnotation(BlockingHandler.class);             System.out.println(((BlockingHandler) annotation).contextPath());             pathHandler.addPrefixPath(((BlockingHandler) annotation).contextPath(), (HttpHandler) entry.getValue());         }          this.builder.setHandler(pathHandler);         return this.builder.build();     }     catch (ServletException ex) {         throw new EmbeddedServletContainerException(                 "Unable to start embdedded Undertow", ex);     } } 

set initializer to the application context

public static void main(String[] args) {     new SpringApplicationBuilder(Application.class).initializers(new UndertowInitializer()).run(args); } 

finaly create a HttpHandler that dispatch to worker thread

@BlockingHandler(contextPath = "/blocking/test") public class DatabaseHandler implements HttpHandler {      @Autowired     private EchoService echoService;      @Override     public void handleRequest(HttpServerExchange httpServerExchange) throws Exception {         if(httpServerExchange.isInIoThread()){             httpServerExchange.dispatch();         }          echoService.getMessage("my message");     } 

}

As you can see, my "solution" is really heavy, and i would really appreciate any help to simplify it a lot.

Thank you

回答1:

You don't need to do anything.

Spring Boot's default Undertow configuration uses Undertow's ServletInitialHandler in front of Spring MVC's DispatcherServlet. This handler performs the exchange.isInIoThread() check and calls dispatch() if necessary.

If you place a breakpoint in your @Controller, you'll see that it's called on a thread named XNIO-1 task-n which is a worker thread (the IO threads are named XNIO-1 I/O-n).



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!