骆驼:无法动态添加路由

Camel: failure to add routes dynamically

我正在使用 Apache-Camel 2.15.2。

我正在尝试将路由动态添加到 CamelContext,但我遇到了一个令我困惑的问题。

据我所知,我确实将路由添加到正确的 CamelContext,并且它们的 configure() 似乎被调用而没有抛出异常。然而,当我尝试执行主路线时,我得到一个 运行 时间异常,告诉我我动态添加的路线不存在。

这是我的代码的简化版本:

public class MainRouteBuilder extends RouteBuilder
{
    public static CamelContext camelContext;

    public static boolean returnLabel = true;

    public static RouteBuilder nestedRouteBuilder;

    @Override
    public void configure() throws Exception
    {
        System.out.println("Building main route!");
        System.out.println("Context: " + getContext());

        camelContext = getContext();

        from("direct:mainRoute")
        //3. I do not actually want to instantiate RouteContainer like this each time I call this route.
        //I would simply want to reuse a reference to an instance I created outside of configure()...
        .to(new RouteContainer().getMyRoute(2))
        ;

        returnLabel = false;

        //configure direct:myRoute.2

        includeRoutes(nestedRouteBuilder);      
    }

}

public class RouteContainer extends RouteBuilder
{
    public Route route;

    RouteContainer() {
        super(MainRouteBuilder.camelContext);
    }

    String getMyRoute(final int n) {

        if (MainRouteBuilder.returnLabel && route == null) {
            route = new Route() {

                @Override
                public void configure()
                {

                    System.out.println("Building nested route!");
                    System.out.println("Context: " + getContext());

                    from("direct:myRoute." + n)
                    .transform()
                    .simple("number: " + n)
                    .to("stream:out")
                    .process(new Processor() {
                        @Override
                        public void process(Exchange exchange) throws Exception {
                            Response response = new Response();
                            response.setStatus(Status.SUCCESS);

                            exchange.getOut().setBody(response);
                        }
                    });
                }               
            };
        }

//1. works:
        MainRouteBuilder.nestedRouteBuilder = this;

//2. does not work:
//      RouteContainer routeContainer = new RouteContainer();
//      routeContainer.route = this.route;
//      MainRouteBuilder.nestedRouteBuilder = routeContainer;

        return "direct:myRoute." + n;
    }


    @Override
    public void configure() throws Exception {
        if (route != null) {
            route.configure();
        }
    }

    public abstract static class Route {        
        abstract public void configure();
    }

}

发送到 direct:mainRoute 的请求有效。 在 Camel 启动期间,我在控制台中看到:

Building main route!
Context: SpringCamelContext(camel-1) with spring id org.springframework.web.context.WebApplicationContext:/sample-route
Building nested route!
Context: SpringCamelContext(camel-1) with spring id org.springframework.web.context.WebApplicationContext:/sample-route

当我向 direct:mainRoute 发送请求时,输出是:

{"status":"SUCCESS"}

但是,如果我在上面注释掉 (1) 并取消注释 (2),Camel 启动时会向控制台输出相同的输出,但是当我向 direct:mainRoute 发送请求时,执行路由失败,异常:

org.apache.camel.component.direct.DirectConsumerNotAvailableException: No consumers available on endpoint: Endpoint[direct://myRoute.2].

澄清一下: 我的问题是因为我实际上不想在每次调用它的路由时实例化 RouteContainer,就像我在 (3) 中所做的那样。这就是为什么我在第 (2) 点实例化它们并将 Route 实例插入其中...

所以我希望 MainRouteBuilder 看起来像这样:

public class MainRouteBuilder extends RouteBuilder
{
    public static CamelContext camelContext;

    public static boolean returnLabel = true;

    public static RouteBuilder nestedRouteBuilder;

    RouteContainer routeContainer = new RouteContainer();

    @Override
    public void configure() throws Exception
    {
        System.out.println("Building main route!");
        System.out.println("Context: " + getContext());

        camelContext = getContext();

        from("direct:mainRoute")
        .to(routeContainer.getMyRoute(2))
        //I may want to call it again like this:
        //.to(routeContainer.getMyRoute(3))
        ;

        returnLabel = false;

        //configure direct:myRoute.2

        includeRoutes(nestedRouteBuilder);      
    }

}

我的假设是嵌套路由 direct:myRoute.2 可能是在错误的 CamelContext 中创建的,但控制台输出告诉我事实并非如此。

知道我做错了什么吗?

路由配置!=路由执行

看来您混淆了路由配置和路由执行。我们都去过那里 ;-)

当您在 MainRouteBuilder#configure() 中配置 RouteBuilder 时,该方法仅在您的 Camel 应用程序启动时 执行一次 ,以便设置路由逻辑。 DSL 为路由(处理器、拦截器等)创建管道,这就是路由运行时。

带回家的要点: DSL 不是 在每个 Exchange 中一遍又一遍地执行。

换句话说,Camel 并没有按照您在 (3) 中指出的那样去做。它不会为每个 Exchange 执行 new RouteContainer().getMyRoute(2)。想一想: configure() 的字节码仅在配置 Camel 时执行,字节码实例化 class RouteContainer 的对象并调用 getMyRoute 参数 2.生成的对象被送入 to() DSL 生成的 SendProcessor

分析你的代码

现在,关于为什么您的代码没有产生您期望的结果。

RouteContainer 的状态保持有问题。每次调用 getMyRoute 都会覆盖实例变量 route。因此,您当前的代码不可能多次调用 getMyRoute(使用不同的 n),然后最后只调用一次 includeRoutes,因为只会添加最近生成的路由.

我也不喜欢用你自己的 class 掩盖 Camel Route class,只是为了充当占位符,但这会引发不同的讨论没要求。

更简单的解决方案

而不是你的 RouteContainer class,这里是 RouteGenerator class 创建路由和 returns direct: 端点到调用者.它跟踪内部 Set.

中的所有路由
public class RouteGenerator {

    private Set<RouteBuilder> routeBuilders = new HashSet<>();

    public String generateRoute(final int n) {

        routeBuilders.add(new RouteBuilder() {
            @Override public void configure() throws Exception {
                System.out.println("Building nested route!");
                System.out.println("Context: " + getContext());

                from("direct:myRoute." + n)
                    .transform() .simple("number: " + n)
                    .to("stream:out")
                    .process(new Processor() {
                        @Override
                        public void process(Exchange exchange) throws Exception {
                            Response response = new Response();
                            response.setStatus(Status.SUCCESS);
                            exchange.getOut().setBody(response);
                        }
                    });
            }
        });

        return "direct:myRoute." + n;

    }

    public Set<RouteBuilder> getRouteBuilders() {
        return routeBuilders;
    }

}

这是您的 MainRouteBuilder,它只实例化 RouteGenerator 一次,并且可以根据需要生成任意数量的路由。

完成路由配置后,只需遍历累积的 RouteBuilders 并包含它们:

public class MainRouteBuilder extends RouteBuilder {

    public static CamelContext camelContext;

    public static RouteGenerator routeGenerator = new RouteGenerator();

    @Override
    public void configure() throws Exception {
        System.out.println("Building main route!");
        System.out.println("Context: " + getContext());

        camelContext = getContext();

        from("direct:mainRoute")
            .to(routeGenerator.generateRoute(2));


        for (RouteBuilder routeBuilder : routeGenerator.getRouteBuilders()) {
            includeRoutes(routeBuilder);
        }
    }

}

编辑:为什么您的选项 (2) 不起作用?

经过一段时间的调试,我明白了为什么你会看到这种效果。

摘自the Java Tutorial

As with instance methods and variables, an inner class is associated with an instance of its enclosing class and has direct access to that object's methods and fields.

在 (2) 中,您在初始 RouteContainer 对象的范围内创建了一个 Route 的实例,作为外部对象。 Route 对象保留外部 RouteContainer 作为其外部对象。因此,from() 和后续方法是在 初始 RouteContainer (RouteBuilder) 对象上调用的,而不是在您稍后创建的新 RouteContainer 对象上调用的 [=116] =],也就是你提供给上层RouteBuilder(关联CamelContext)的那个。

这就是为什么您的 direct:myRoute.2 没有被添加到 Camel 上下文中的原因,因为它是在不同的路由构建器中创建的。

另请注意 (2) 的控制台输出:

Building main route!
Context: CamelContext(camel-1)
Building nested route!
Context: CamelContext(camel-2)
Added!

正在将第二个路由添加到不同的上下文 camel-2。这个新的上下文是由 Camel 在将路由添加到旧的 RouteBuilder 时懒惰地创建的,它还没有关联到任何 Camel 上下文。

注意初始的RouteContainer(在实例变量初始化时创建)的Camel Context是null,因为你稍后赋值MainRouteBuilder.camelContext 属性

您可以通过添加以下 println 语句来查看如何使用两个不同的路由构建器:

内部路由#configure:

System.out.println("RouteContainer to which route is added: " + RouteContainer.this.hashCode());

在 MainRouteBuilder#configure 内部,就在 includeRoutes:

之前
System.out.println("RouteContainer loaded into Camel: " + nestedRouteBuilder.hashCode());

与(1)的hashcode相同。对于 (2),哈希码 不同 ,清楚地表明有两个不同的 RouteBuilder 在起作用(一个包含路由,另一个加载到上下文中,不包含路线)。


资料来源:我是 Apache Camel PMC 成员和提交者。