SpringBoot Undertow:如何分派到工作线程 [英] SpringBoot Undertow : how to dispatch to worker thread

查看:34
本文介绍了SpringBoot Undertow:如何分派到工作线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在查看 springboot undertow,但(对我而言)如何将传入的 http 请求分派到工作线程以进行阻塞操作处理并不是很清楚.

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.

查看 UndertowEmbeddedServletContainer.class 类,看起来没有办法实现这种行为,因为唯一的 HttpHandler 是 ServletHandler,它允许 @Controller 配置

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

}

默认情况下,在 undertow 中,所有请求都由 IO-Thread 处理以进行非阻塞操作.这是否意味着每个 @Controller 执行都将由一个非阻塞线程处理?或者是否有解决方案可以从 IO-THREAD 或 WORKER-THREAD 中选择?

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

覆盖 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");
    }

}

如您所见,我的解决方案"非常繁重,如果您能帮助我大大简化它,我将不胜感激.

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

谢谢

推荐答案

你不需要做任何事情.

Spring Boot 的默认 Undertow 配置使用 Undertow 的 ServletInitialHandler 在 Spring MVC 的 DispatcherServlet 前面.这个处理程序 执行exchange.isInIoThread()检查并在必要时调用dispatch().

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.

如果您在 @Controller 中放置一个断点,您会看到它在名为 XNIO-1 task-n 的线程上调用,该线程是一个工作线程(IO 线程被命名为 XNIO-1 I/On).

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).

这篇关于SpringBoot Undertow:如何分派到工作线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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