Java “什么是正确的方法?”;暂停“;使用camel和activemq的路由?

Java “什么是正确的方法?”;暂停“;使用camel和activemq的路由?,java,apache-camel,activemq,Java,Apache Camel,Activemq,我的基本问题是,在任何一个1小时的时间段内,我只能处理来自其中一个队列(所有机器)的7000条消息。我看不到用camel或activemq实现这一点的方法,所以我求助于实现自己的路由停止/启动逻辑。我看到了很多方法可以做到这一点,我也尝试了其中的一些(只是遇到了问题) camelContext.stopRoute(route):这是因为消息停止处理,但当我调用camelContext.startRoute(route)时,它会泄漏tcp连接,最终导致activemq服务器达到极限并死亡 came

我的基本问题是,在任何一个1小时的时间段内,我只能处理来自其中一个队列(所有机器)的7000条消息。我看不到用camel或activemq实现这一点的方法,所以我求助于实现自己的路由停止/启动逻辑。我看到了很多方法可以做到这一点,我也尝试了其中的一些(只是遇到了问题)

  • camelContext.stopRoute(route)
    :这是因为消息停止处理,但当我调用
    camelContext.startRoute(route)
    时,它会泄漏tcp连接,最终导致activemq服务器达到极限并死亡
  • camelContext.suspendRoute(route)
    :这也会停止处理消息,并且不会泄漏连接,但它似乎会杀死在调用
    camelContext.resumeRoute(route)
    时不会重新激活的活动使用者(在管理面板中可见)。我认为这最终可能会导致该队列中根本没有消息被处理,即使我继续
  • 实现自定义
    路由策略
    。公平地说,我还没有尝试过这个方法,但根据我上面选择的暂停方法,它似乎会成为我遇到的相同问题的牺牲品

  • 有没有一种方法可以解决我还没有遇到的这个问题?

    我建议使用

    上面的示例将在发送到
    mock:result
    之前限制在
    jms:queue:inbox
    上接收的消息,以确保在任何1小时窗口中最多发送7000条消息

    或者,为了实现更细粒度的控制,您可以定义一个节流路由策略,如Camel的:

    
    
    节流警察的定义如下:

    <bean id="myPolicy" class="org.apache.camel.impl.ThrottlingInflightRoutePolicy">
        <property name="scope" value="Context"/>
        <!-- when we hit > 20 inflight exchanges then kick in and suspend the routes -->
        <property name="maxInflightExchanges" value="20"/>
        <!-- when we hit lower than 10% of the max = 2 then kick in and resume the routes the default percentage is 70% but in this demo we want a low value -->
        <property name="resumePercentOfMax" value="10"/>
        <!-- output throttling activity at WARN level -->
        <property name="loggingLevel" value="WARN"/>
    </bean>
    
    
    
    编辑1:

    如果需要全局限制,则可以首先让一个使用者读取消息,如上所述限制所有消息,然后将它们重新发送到另一个队列,并让=1个分布式使用者重新读取和处理它们

    编辑2:


    或者,您可以实现自己的
    限制FlightRoutePolicy
    访问包含处理信息的中央数据库。这样,您就不需要“单节点主节流器”。然而,DB也可能是一个单点故障。

    Peter得到了最好的答案,但我最终扩展了
    限制飞行路线策略
    ,并且没有很好的解释如何工作,所以我想我应该对这个问题稍加注释,并展示我是如何实际解决这个问题的

    public class MyRoutePolicy extends RoutePolicySupport implements CamelContextAware {
    
        private CamelContext camelContext;
        private final Lock lock = new ReentrantLock();
        private ContextScopedEventNotifier eventNotifier;
    
        @Override
        public final void setCamelContext(final CamelContext camelContext) {
            this.camelContext = camelContext;
        }
    
        @Override
        public final CamelContext getCamelContext() {
            return this.camelContext;
        }
    
        @Override
        public final void onExchangeDone(final Route route, final Exchange exchange) {
            throttle(route);
        }
    
        private void throttle(final Route route) {
            // this works the best when this logic is executed when the exchange is done
            Consumer consumer = route.getConsumer();
    
            boolean stop = isRouteMarkedForSuspension(route.getId()) && ((JmsConsumer) route.getConsumer()).isStarted();
            if (stop) {
                try {
                    lock.lock();
                    stopConsumer(consumer);
                } catch (Exception e) {
                    handleException(e);
                } finally {
                    lock.unlock();
                }
            }
    
            // reload size in case a race condition with too many at once being invoked
            // so we need to ensure that we read the most current size and start the consumer if we are already to low
            boolean start = !isRouteMarkedForSuspension(route.getId()) && ((JmsConsumer) route.getConsumer()).isSuspended();
            if (start) {
                try {
                    lock.lock();
                    startConsumer(consumer);
                } catch (Exception e) {
                    handleException(e);
                } finally {
                    lock.unlock();
                }
            }
        }
    
        @Override
        protected final void doStart() throws Exception {
            ObjectHelper.notNull(camelContext, "CamelContext", this);
            eventNotifier = new ContextScopedEventNotifier();
            // must start the notifier before it can be used
            ServiceHelper.startService(eventNotifier);
            // we are in context scope, so we need to use an event notifier to keep track
            // when any exchanges is done on the camel context.
            // This ensures we can trigger accordingly to context scope
            camelContext.getManagementStrategy().addEventNotifier(eventNotifier);
        }
    
        @Override
        protected final void doStop() throws Exception {
            ObjectHelper.notNull(camelContext, "CamelContext", this);
            camelContext.getManagementStrategy().removeEventNotifier(eventNotifier);
        }
    
        private class ContextScopedEventNotifier extends EventNotifierSupport {
    
            @Override
            public void notify(final EventObject event) throws Exception {
                for (Route route : camelContext.getRoutes()) {
                    throttle(route);
                }
            }
    
            @Override
            public boolean isEnabled(final EventObject event) {
                return event instanceof ExchangeCompletedEvent;
            }
    
            @Override
            protected void doStart() throws Exception {
                // noop
            }
    
            @Override
            protected void doStop() throws Exception {
                // noop
            }
    
            @Override
            public String toString() {
                return "ContextScopedEventNotifier";
            }
        }
    }
    
    因此,我将上面的RoutePolicy添加到我的所有路线中,如下所示:

    from(uri).routePolicy(routePolicy).process(runner);
    
    MyRoutePolicy
    是一个内部类,
    isRouteMarkedForSuspension
    在主类中定义

    节气门
    在两点处被击中:

    • 在处理交换(消息)之后。这对于确定是否应该暂停使用者非常有用
    • 通过
      ContextScopedEventNotifier
      通知事件。这有助于确定是否应恢复消费者

    我会采用客户路线政策。当你说“根据我上面选择的暂停方法”时,我想对于一个路由策略,你刚才调用了stopConsumer()和startConsumer(),就像throttlingFlightRoutePolicy一样。“throller”会有帮助吗@vikingsteve我需要在队列级别对所有正在处理该队列的机器进行节流。您提到的节流仅用于限制单个计算机的处理。@matthelliwell我在RoutePolicy之外尝试了startConsumer()和stopConsumer(),这导致了类似于(1)的连接泄漏。我想我应该尝试RoutePolicy,只是为了完整性。节流策略可以跨上下文/机器应用吗?否则就有可能出现单点故障。@vikingsteve谢谢你的提问。请参阅我的编辑2如何处理它。@Peter有趣。我没有考虑让其中一个消费者进行节流,但这会引入大量开销来管理不对称的消费者。我不确定ThrottlingFlightRoutePolicy如何实现不同于我在问题中提到的camelContext.startRoute()或camelContext.resumeRoute()的停止/启动路由,但既然每个人都非常支持它,我就试试看!
    public class MyRoutePolicy extends RoutePolicySupport implements CamelContextAware {
    
        private CamelContext camelContext;
        private final Lock lock = new ReentrantLock();
        private ContextScopedEventNotifier eventNotifier;
    
        @Override
        public final void setCamelContext(final CamelContext camelContext) {
            this.camelContext = camelContext;
        }
    
        @Override
        public final CamelContext getCamelContext() {
            return this.camelContext;
        }
    
        @Override
        public final void onExchangeDone(final Route route, final Exchange exchange) {
            throttle(route);
        }
    
        private void throttle(final Route route) {
            // this works the best when this logic is executed when the exchange is done
            Consumer consumer = route.getConsumer();
    
            boolean stop = isRouteMarkedForSuspension(route.getId()) && ((JmsConsumer) route.getConsumer()).isStarted();
            if (stop) {
                try {
                    lock.lock();
                    stopConsumer(consumer);
                } catch (Exception e) {
                    handleException(e);
                } finally {
                    lock.unlock();
                }
            }
    
            // reload size in case a race condition with too many at once being invoked
            // so we need to ensure that we read the most current size and start the consumer if we are already to low
            boolean start = !isRouteMarkedForSuspension(route.getId()) && ((JmsConsumer) route.getConsumer()).isSuspended();
            if (start) {
                try {
                    lock.lock();
                    startConsumer(consumer);
                } catch (Exception e) {
                    handleException(e);
                } finally {
                    lock.unlock();
                }
            }
        }
    
        @Override
        protected final void doStart() throws Exception {
            ObjectHelper.notNull(camelContext, "CamelContext", this);
            eventNotifier = new ContextScopedEventNotifier();
            // must start the notifier before it can be used
            ServiceHelper.startService(eventNotifier);
            // we are in context scope, so we need to use an event notifier to keep track
            // when any exchanges is done on the camel context.
            // This ensures we can trigger accordingly to context scope
            camelContext.getManagementStrategy().addEventNotifier(eventNotifier);
        }
    
        @Override
        protected final void doStop() throws Exception {
            ObjectHelper.notNull(camelContext, "CamelContext", this);
            camelContext.getManagementStrategy().removeEventNotifier(eventNotifier);
        }
    
        private class ContextScopedEventNotifier extends EventNotifierSupport {
    
            @Override
            public void notify(final EventObject event) throws Exception {
                for (Route route : camelContext.getRoutes()) {
                    throttle(route);
                }
            }
    
            @Override
            public boolean isEnabled(final EventObject event) {
                return event instanceof ExchangeCompletedEvent;
            }
    
            @Override
            protected void doStart() throws Exception {
                // noop
            }
    
            @Override
            protected void doStop() throws Exception {
                // noop
            }
    
            @Override
            public String toString() {
                return "ContextScopedEventNotifier";
            }
        }
    }
    
    from(uri).routePolicy(routePolicy).process(runner);