我有一个带有Netty端点的camel实例,它整合了许多传入请求以发送到单个接收器。更具体地说,这是一个Web服务,每个传入的SOAP请求都会导致Producer.sendBody()
进入camel子系统。每个请求的处理涉及不同的路由,但它们都将在单个Netty端点中发送到下一级服务器。一切都很好,只要我在任何时候只有少数传入请求。但是,如果我开始有超过100个同时请求,我会遇到这个例外:
java.lang.IllegalStateException: Queue full
at java.util.AbstractQueue.add(AbstractQueue.java:71) ~[na:1.6.0_24]
at java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:209) [na:1.6.0_24]
at org.apache.camel.impl.DefaultServicePool.release(DefaultServicePool.java:95) [camel-core-2.9.2.jar:2.9.2]
at org.apache.camel.impl.ProducerCache$1.done(ProducerCache.java:297) ~[camel-core-2.9.2.jar:2.9.2]
at org.apache.camel.processor.SendProcessor$2$1.done(SendProcessor.java:120) ~[camel-core-2.9.2.jar:2.9.2]
at org.apache.camel.component.netty.handlers.ClientChannelHandler.messageReceived(ClientChannelHandler.java:162) ~[camel-netty-2.9.2.jar:2.9.2]
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:296) ~[netty-3.3.1.Final.jar:na]
这来自Netty组件使用的DefaultServicePool
。 DefaultServicePool
使用ArrayBlockingQueue
作为队列的后端,并将其设置为默认容量100 Producers。它出于性能原因使用服务池,以避免不得不继续创建和销毁经常重用的生产者。很公平。不幸的是,我没有弄清楚它是如何实现的。
这一切都从ProducerCache::doInAsyncProducer
开始,通过调用doGetProducer
开始。所述方法尝试从池中acquire
生成器,如果失败,则使用endpoint.getProducer()
创建新的生产者。然后使用pool.addAndAcquire
确保服务池存在。完成后,它返回到调用函数。 doInAsyncProducer
会完成它,直到它完成,在这种情况下它会调用done
处理器。此时,我们完全处理了交换,因此它使用pool.release
这是橡胶在路上行驶的地方。 DefaultServicePool::release
方法使用ArrayBlockingQueue
将Producer插入add
后端。这是我的java.lan.IllegalStateException
来自的地方。
为什么呢?好吧,让我们看一下用例。我有101个同时传入的请求。它们中的每一个都在大致相同的时间点击Netty端点。第一个创建服务池,容量为100,但启动时为空。事实上,101个请求中的每一个都将从endpoint.getProducer
创建一个新的制作人;每个都将验证它们是否超过服务池的容量(为空);并且每个将继续发送到服务器。每次完成后,它会尝试执行pool.release
。由于尚未达到池容量,前100个将成功。第101个请求将尝试添加到队列并且将失败,因为队列已满!
是吗?如果我正确读取,那么每当有超过100个同时请求时,此代码将始终失败。我的服务需要支持超过10,000个同时请求,因此不会飞。
似乎更稳定的解决方案可能是:
acquire
期间阻止,直到制作人可用与此同时,我正在考虑限制传入的请求。
我希望这个问题是要了解我是否正确地阅读了这个逻辑,看看它是否可以改变。或者,我使用它错了吗?有没有更好的方法来处理这类事情?
答案 0 :(得分:0)
是的,应该改进IMHO的逻辑。我已经记录了一张票以改善这一点。 https://issues.apache.org/jira/browse/CAMEL-5703