好吧,在玩了太久之后,我终于找到了一个适合我的解决方案。希望这是做我需要做的事情的正确方式。
现在,执行正常的CXF RESTful路由会显示使用阻塞任务的拖航,执行反应式路由会显示直接使用NIO的拖航。当我尝试使用ServletHttpHandler时,它似乎只是作为Servlet3异步调用调用服务。
处理程序完全独立运行,允许我在响应式服务之外运行REST服务。
1) 创建注释,用于将RouterFunction映射到下拖处理程序
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface ReactiveHandler
{
String value();
}
2) 创建一个UndertowReactiveHandler“Provider”,以便在配置Undertow时,可以懒洋洋地获取注入的RouterFunction并返回UndertowHttpHandler。
final class UndertowReactiveHandlerProvider implements Provider<UndertowHttpHandlerAdapter>
{
@Inject
private ApplicationContext context;
private String path;
private String beanName;
@Override
public UndertowHttpHandlerAdapter get()
{
final RouterFunction router = context.getBean(beanName, RouterFunction.class);
return new UndertowHttpHandlerAdapter(toHttpHandler(router));
}
public String getPath()
{
return path;
}
public void setPath(final String path)
{
this.path = path;
}
public void setBeanName(final String beanName)
{
this.beanName = beanName;
}
}
3) 创建NonBLockingHandlerFactory(实现BeanFactoryPostProcessor)。这将查找任何已用“ReactiveHandler”注释的@Bean方法,然后为每个注释的路由器函数动态创建一个“UndertowReactiveHandlerProvider”Bean,该Bean稍后用于向Undertow提供处理程序。
@Override
public void postProcessBeanFactory(final ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException
{
final BeanDefinitionRegistry registry = (BeanDefinitionRegistry)configurableListableBeanFactory;
final String[] beanDefinitions = registry.getBeanDefinitionNames();
for (String name : beanDefinitions)
{
final BeanDefinition beanDefinition = registry.getBeanDefinition(name);
if (beanDefinition instanceof AnnotatedBeanDefinition
&& beanDefinition.getSource() instanceof MethodMetadata)
{
final MethodMetadata beanMethod = (MethodMetadata)beanDefinition.getSource();
final String annotationType = ReactiveHandler.class.getName();
if (beanMethod.isAnnotated(annotationType))
{
//Get the current bean details
final String beanName = beanMethod.getMethodName();
final Map<String, Object> attributes = beanMethod.getAnnotationAttributes(annotationType);
//Create the new bean definition
final GenericBeanDefinition rxHandler = new GenericBeanDefinition();
rxHandler.setBeanClass(UndertowReactiveHandlerProvider.class);
//Set the new bean properties
MutablePropertyValues mpv = new MutablePropertyValues();
mpv.add("beanName", beanName);
mpv.add("path", attributes.get("value"));
rxHandler.setPropertyValues(mpv);
//Register the new bean (Undertow handler) with a matching route suffix
registry.registerBeanDefinition(beanName + "RxHandler", rxHandler);
}
}
}
}
4) 创建Undertow ServletExtension。这将查找任何UndertowReactiveHandlerProviders,并将其添加为UndertowHttpHandler。
public class NonBlockingHandlerExtension implements ServletExtension
{
@Override
public void handleDeployment(DeploymentInfo deploymentInfo, final ServletContext servletContext)
{
deploymentInfo.addInitialHandlerChainWrapper(handler -> {
final WebApplicationContext ctx = getWebApplicationContext(servletContext);
//Get all of the reactive handler providers
final Map<String, UndertowReactiveHandlerProvider> providers =
ctx.getBeansOfType(UndertowReactiveHandlerProvider.class);
//Create the root handler
final PathHandler rootHandler = new PathHandler();
rootHandler.addPrefixPath("/", handler);
//Iterate the providers and add to the root handler
for (Map.Entry<String, UndertowReactiveHandlerProvider> p : providers.entrySet())
{
final UndertowReactiveHandlerProvider provider = p.getValue();
//Append the HttpHandler to the root
rootHandler.addPrefixPath(
provider.getPath(),
provider.get());
}
//Return the root handler
return rootHandler;
});
}
}
5) 在META-INF/services下创建一个“io.undertow.servlet.ServletExtension”文件。
com.mypackage.NonBlockingHandlerExtension
6) 创建SpringBoot自动配置,如果类路径上有Undertow,则加载后处理器。
@Configuration
@ConditionalOnClass(Undertow.class)
public class UndertowAutoConfiguration
{
@Bean
BeanFactoryPostProcessor nonBlockingHandlerFactoryPostProcessor()
{
return new NonBlockingHandlerFactoryPostProcessor();
}
}
7) 注释我要映射到UndertowHandler的任何RouterFunction。
@Bean
@ReactiveHandler("/api/rx/service")
RouterFunction<ServerResponse> routeTracking(TrackingHandler handler)
{
RouterFunction<ServerResponse> route = RouterFunctions
.nest(path("/api/rx/service"), route(
GET("/{cid}.gif"), handler::trackGif).andRoute(
GET("/{cid}"), handler::trackAll));
return route;
}
有了它,我可以调用我的REST服务(Shiro与之合作),将Swagger2与我的REST服务一起使用,并在同一个SpringBoot应用程序中调用我的反应式服务(他们不使用Shiro)。
在我的日志中,REST调用使用阻塞(task-#)处理程序显示Undertow。被动调用使用非阻塞(I/O-#和nioEventLoopGroup)处理程序显示Undertow