我有一個帶有Netty端點的駱駝實例,它將許多傳入請求合併到一個接收器中。更具體地說,這是一個Web服務,每個傳入的SOAP請求都會在駝峯子系統中生成一個Producer.sendBody()
。每個請求的處理都涉及不同的路由,但它們都將在單個Netty端點中發送到下一級服務器。一切都很好,只要我一次只有少量的傳入請求。如果我開始有超過100個併發請求,不過,我得到這個異常:駱駝ServicePool與Netty一起使用時的邏輯
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個生產者的默認容量。它出於性能原因使用服務池,以避免不斷創建和銷燬經常重用的生產者。很公平。不幸的是,我沒有得到如何實施的邏輯。
這一切都始於ProducerCache::doInAsyncProducer
,開頭爲doGetProducer
。所述方法嘗試從池中生產者acquire
,如果失敗,則使用endpoint.getProducer()
創建新的生產者。然後確保服務池使用pool.addAndAcquire
。完成後,它返回到調用函數。 doInAsyncProducer
在完成之前完成它,在這種情況下它會調用done
處理器。此時,我們完成了交易處理,因此它使用pool.release
將生產者釋放回池中。這裏是橡膠撞擊道路的地方。 DefaultServicePool::release
方法使用add
將生產者插入到ArrayBlockingQueue
後端。這是我的java.lan.IllegalStateException
來自的地方。
爲什麼?那麼,讓我們看看一個用例。我有101個同時傳入的請求。它們中的每一個大致在同一時間點擊Netty端點。第一個創建容量爲100的服務池,但是它開始時是空的。實際上,101個請求中的每一個都會從endpoint.getProducer
創建一個新的Producer;每個人將驗證他們沒有超過服務池的容量(這是空的);並且每個將繼續發送到服務器。完成後,它會嘗試執行pool.release
。前100名將成功,因爲池容量尚未達到。第101個請求將嘗試添加到隊列中並失敗,因爲隊列已滿!
是嗎?如果我正確閱讀,那麼只要有超過100個同時請求,此代碼就會失敗。我的服務需要支持多達10,000個同時請求,所以這只是不會飛。
這似乎是一個更穩定的解決方案可能是:
- 預分配期間
acquire
所有100名製片人於初始化 - 阻塞,直到生產者可用
- 絕對不要創建自己的非-pool生產者如果使用ServicePool
在此期間,我想節流傳入的請求。
我對這個問題的期望是學習如果我正確地閱讀這個邏輯,看看它是否可以改變。或者,我用錯了嗎?有沒有更好的方法來處理這種類型的事情?