我目前正在查看springboot,并且对我来说还不是很清楚如何将传入的HTTP请求分派给工作线程以阻止操作处理。
查看类 UndertowEmbeddedServletContainer.class ,由于唯一的HttpHandler是一个ServletHandler,它允许@Controller配置,因此看起来没有这种行为。
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); }
默认情况下,在undertow中,所有请求均由IO- Thread处理以进行非阻塞操作。这是否意味着每个@Controller执行将由非阻塞线程处理?还是有从IO-THREAD或WORKER- THREAD中选择的解决方案?
我尝试编写一种解决方法,但是此代码非常笨拙,也许有人有更好的解决方案:
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 } }
覆盖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; } } ...
覆盖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); } }
将初始化程序设置为应用程序上下文
public static void main(String[] args) { new SpringApplicationBuilder(Application.class).initializers(new UndertowInitializer()).run(args); }
最后创建一个分派给工作线程的HttpHandler
@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"); }
}
如您所见,我的“解决方案”实在太繁重了,我非常感谢您提供的许多简化方面的帮助。
谢谢
您什么都不需要做。
Spring Boot的默认Undertow配置ServletInitialHandler在Spring MVC的前面使用Undertow 的DispatcherServlet。该处理程序执行exchange.isInIoThread()检查并dispatch()在必要时调用。
ServletInitialHandler
DispatcherServlet
exchange.isInIoThread()
dispatch()
如果在中放置一个断点@Controller,则会看到在名为XNIO-1 task-nworker线程的线程上调用了该断点(IO线程的名称为XNIO-1 I/O-n)。
@Controller
XNIO-1 task-n
XNIO-1 I/O-n