当JVM达到某个堆大小时,如何停止Camel路由?

时间:2014-02-06 13:38:24

标签: java jvm heap apache-camel

我正在使用Apache Camel连接到各种端点,包括JMS主题,并写入数据库。有时数据库连接失败(无论出于何种原因,数据库问题,网络blip等),主题订阅者的消息开始备份。在某个时刻,有许多消息备份等待写入数据库,应用程序抛出内存不足错误。到目前为止,我理解这一切。

我遇到的问题如下:当应用程序在最终放弃并接受内存不足之前疯狂地尝试垃圾收集时,应用程序停止工作,但仍处于活动状态。这意味着主题订阅者仍被JMS提供程序视为活动状态,但不会读取该主题之外的任何内容,因此提供程序开始对消息进行排队。最终,当最大深度耗尽时,提供者也会倒下。

如何将应用程序配置为在达到某个堆使用时断开连接,或者在内存不足时更快地自行终止?我相信有一些JVM参数允许应用程序在内存不足时更快地自杀,但我想知道这是否是最佳解决方案或是否有其他方法?

2 个答案:

答案 0 :(得分:1)

首先,我认为您应该使用能够刷新失败连接的JDBC连接池。因此,您首先不要遇到所描述的场景。至少不是数据库/网络问题是短暂的。

接下来,我通过应用生产者流控制来保护消息代理(至少在ActiveMQ中调用它是如何)。即如果违反了某个内存阈值,则阻止消息生成者提交更多消息。如果阈值设置正确,那么这将阻止您的消息代理翻倒。

至于你原来的问题:我使用JMX来监控VM。如果是某个指标,例如内存,违反了一个阈值,然后您可以通过MBeans Camel公开的方式暂停或关闭路由或整个Camel上下文。

答案 1 :(得分:0)

您可以使用Camel上下文方法.stop().start().suspend().resume()控制(启动/停止和暂停/恢复)Camel路由。

您可以旋转一个监视当前VM内存的单独线程,并在满足特定条件时停止所需的路由。

new Thread() {
    @Override
    public void run() {
        while(true) {
            long free = Runtime.getRuntime().freeMemory();
            boolean routeRunning = camelContext.isRouteStarted("yourRoute");
            if (free < threshold && routeRunning) {
                camelContext.stopRoute("yourRoute");
            } else if (free > threshold && !routeRunning) {
                camelContext.startRoute("yourRoute");
            }
            // Check every 10 seconds
            Thread.sleep(10000);
        }
    }
}

正如在另一个答案中所评论的那样,依赖于此并不是特别健壮,但至少比获得OutOfMemoryException更强大。请注意,您需要.stop()路由,.suspend()不会释放资源,这意味着与队列提供程序的连接仍然是打开的,并且服务看起来像是为业务开放。

您也可以将路由作为路由本身错误处理的一部分来停止(这可能更强大,但是一旦错误被清除就需要手动干预以重新启动路由,或者定期检查错误是否有计划的路由条件仍然存在并重新启动路径(如果它已经消失)。要记住的是,您无法从当时为路径提供服务的同一线程停止路由,因此您需要旋转一个单独的线程来执行停止。例如:

route("sample").from("jms://myqueue")
    // Handle SQL Exceptions by shutting down the route
    .onException(SQLException.class)
        .process(new Processor() {
            // This processor spawns a new thread that stops the current route
            Thread stop;

            @Override
            public void process(final Exchange exchange) throws Exception {
                if (stop == null) {
                    stop = new Thread() {
                        @Override
                        public void run() {
                            try {
                                // Stop the current route
                                exchange.getContext().stopRoute("sample");
                            } catch (Exception e) {}
                        }
                    };
                }
                // start the thread in background
                stop.start();
            }
        })
    .end()
    // Standard route processors go here
    .to(...);