私の環境はGlassfish 3.1.2 b23で、JDK 1.7.0_45を搭載したWindows Server 2008上で組み込みモードのOpenMQがバンドルされています(FWIWも同じ結果でLOCALモードを試しました)。
非常に単純な JMS コンポーネントを含むアプリケーションがあります。JMS コンポーネントは、キューに送信され、MDB によって消費されるメッセージを生成するステートレス セッション Bean で構成されます。JMS 接続プールとキューは、Glassfish 管理 UI を使用して作成された管理オブジェクトです。
問題は、メッセージを通常どおりに作成して消費した後、次のエラーが表示されることです。
com.sun.messaging.jms.JMSException: MQRA:DCF:allocation failure:createConnection:Error in allocating a connection. Cause: In-use connections equal max-pool-size and expired max-wait-time. Cannot allocate more connections.
at com.sun.messaging.jms.ra.DirectConnectionFactory._allocateConnection(DirectConnectionFactory.java:548)
at com.sun.messaging.jms.ra.DirectConnectionFactory.createConnection(DirectConnectionFactory.java:265)
at com.sun.messaging.jms.ra.DirectConnectionFactory.createConnection(DirectConnectionFactory.java:244)
at
[SNIP]
Caused by: javax.resource.spi.ResourceAllocationException: Error in allocating a connection. Cause: In-use connections equal max-pool-size and expired max-wait-time. Cannot allocate more connections.
at com.sun.enterprise.connectors.ConnectionManagerImpl.internalGetConnection(ConnectionManagerImpl.java:307)
at com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:236)
at com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:165)
at com.sun.enterprise.connectors.ConnectionManagerImpl.allocateConnection(ConnectionManagerImpl.java:160)
at com.sun.messaging.jms.ra.DirectConnectionFactory._allocateConnection(DirectConnectionFactory.java:543)
... 99 more
Caused by: com.sun.appserv.connectors.internal.api.PoolingException: In-use connections equal max-pool-size and expired max-wait-time. Cannot allocate more connections.
at com.sun.enterprise.resource.pool.ConnectionPool.getResource(ConnectionPool.java:418)
at com.sun.enterprise.resource.pool.PoolManagerImpl.getResourceFromPool(PoolManagerImpl.java:245)
at com.sun.enterprise.resource.pool.PoolManagerImpl.getResource(PoolManagerImpl.java:170)
at com.sun.enterprise.connectors.ConnectionManagerImpl.getResource(ConnectionManagerImpl.java:332)
at com.sun.enterprise.connectors.ConnectionManagerImpl.internalGetConnection(ConnectionManagerImpl.java:301)
... 103 more
これは@Asynchronous
、ステートレス EJB のメソッドによって開始された自動化されたプロセスが項目のリストをリッピングし、項目ごとにコード パスを呼び出し、それが最終的にこのメッセージ生成に影響を与える場合に発生するようです。一度に 1 つのメッセージしか生成しないわずかに異なるコード パスからメッセージ生成が呼び出される場合、この問題は見られません。
メッセージはミリ秒かかります。生産し、ミリ秒。消費する。接続の最大待機時間は 1 分です。また、最大。同時にメッセージを生成できるスレッドの数は、JMS 接続ファクトリに関連付けられた接続の最大数より (約 2 倍) 少なくなります。
セッションが非 jms-transactional で AUTO-ACK であるプロデューサーは、どのくらいの速さで連続してコールできますか? Producer.send(..) が返されるとすぐに、jms 接続が返されて次のプロデューサで使用できるようになるか、次の .send 呼び出しのために接続が解放されるまでに非常にわずかな遅延時間があると想定します()?
コンシューマーは MDB であり、プロデューサーが使用しているのと同じプールからの JMS 接続も使用していると想定しています (それが唯一の JMS 接続プールであるため)。以下の imqcmd の出力は、アクティブなコンシューマが 1 つあることを示していますが、MDB がプールされているため、コンシューマ側も X # (MDB インスタンスの数) の接続を使用している可能性があることを意味します。プールされた MDB インスタンス?
MDB の例外処理にも興味があります。このパターンは、JavaEE6 サンプルおよび catches/logs Throwable から取得されました。Throwable で mdc.setRollback への呼び出しがないと、JMS 接続がリークしますか?
この問題のデバッグでは、imqcmdコマンドを使用しています。具体的には:
imqcmd query dst -tq -n MyQueue -u admin
---------------------------------------
Destination Name Destination Type
---------------------------------------
MyQueue Queue
On the broker specified by:
-------------------------
Host Primary Port
-------------------------
localhost 7676
Destination Name MyQueue
Destination Type Queue
Destination State RUNNING
Created Administratively false
Current Number of Messages
Actual 0
Remote 0
Held in Transaction 0
Current Message Bytes
Actual 0
Remote 0
Held in Transaction 8928
Current Number of Producers 0
Current Number of Active Consumers 1
Current Number of Backup Consumers 0
Max Number of Messages 100000
Max Total Message Bytes 10737418240
Max Bytes per Message 10485760
Max Number of Producers 100
Max Number of Active Consumers unlimited (-1)
Max Number of Backup Consumers 0
Limit Behavior REJECT_NEWEST
Consumer Flow Limit 1000
Is Local Destination false
Local Delivery is Preferred false
Use Dead Message Queue true
XML schema validation enabled false
XML schema URI List -
Reload XML schema on failure false
Successfully queried the destination.
プロデューサ: ADetailBean.java
@Stateless
public class ADetailBean.java {
private transient static final Logger log = Logger.getLogger(ADetailBean.java);
@Resource(mappedName = "jms/MyConnectionFactory")
private ConnectionFactory jmsConnectionFactory;
@Resource(mappedName = "jms/myApp/MyQueue")
private javax.jms.Queue myQueue;
@PersistenceContext(unitName = "MyPC")
private EntityManager entityManager;
@EJB
AnotherBean anotherBean;
public void createAMessage(@NotNull AFile file, AnAction action) {
ACollection aCol = aCollectionBean.findReqColForTaskColId(file.getCollectionId());
if (aCol != null) {
ADetail aDetail = new ADetail(file.getFileNumber(), file.getCollectionId(), file.getMd5(), reqCol.getCollectionId(), action);
entityManager.persist(aDetail);
Connection jmsConnection = null;
try {
jmsConnection = jmsConnectionFactory.createConnection();
Session session = jmsConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(myQueue);
AMessage aMessage = new AMessage(file.getName());
ObjectMessage myMessage = session.createObjectMessage(aMessage);
producer.send(myMessage);
} catch (JMSException e) {
log.error("Error sending message for file number: " +
file.getFileNumber() + ", md5: " + file.getMd5() +
", action: " + action + " : " + e.getMessage(), e);
} finally {
if (jmsConnection != null) {
try {
jmsConnection.close();
} catch (Exception ex) {
log.warn("Unable to close JMS connection: " + ex.getMessage());
}
}
}
}
//Other methods in this EJB
}
MDB: MyMessageListener.java
@MessageDriven(mappedName = "jms/myApp/MyQueue", activationConfig = {
@ActivationConfigProperty(propertyName = "acknowledgeMode",
propertyValue = "Auto-acknowledge"),
@ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "subscriptionDurability",
propertyValue = "Durable"),
@ActivationConfigProperty(propertyName = "clientId",
propertyValue = "myClient"),
@ActivationConfigProperty(propertyName = "subscriptionName",
propertyValue = "mySub")
})
public class MyMessageListener implements MessageListener {
private static final Logger log = Logger.getLogger(MyMessageListener.class);
@Resource
private MessageDrivenContext mdc;
@PersistenceContext(unitName = "MyPC")
private EntityManager entityManager;
@EJB
ADetailManager aDetailManager;
@EJB
ADetailBean aDetailBean;
@EJB
AFileBean aFileBean;
@Override
public void onMessage(Message message) {
log.debug("MESSAGE received from the MyQueue queue");
try {
if (message instanceof ObjectMessage) {
if (((ObjectMessage) message).getObject() != null &&
((ObjectMessage) message).getObject() instanceof ACalcMessage) {
ACalcMessage msg = (ACalcMessage) ((ObjectMessage) message).getObject();
log.debug("MESSAGE BEAN: Received aCalc msg for name: " + msg.getName());
//Wait for opportunity to process dupe recalculation for name
try {
while (!aDetailManager.obtainNameLock(msg.getName())) {
log.debug("Waiting to obtain name lock for name: " + msg.getName());
Thread.sleep(1000);
}
aDetail aDetail = aDetailBean.findNext(msg.getName());
//Do some processing including JPA queries and entity merges
entityManager.remove(aDetail);
}
} finally {
aDetailManager.releaseNameLock(msg.getName());
}
}
}
} catch (JMSException e) {
log.error("Error processing message: " + e.getMessage());
mdc.setRollbackOnly();
} catch (Throwable te) {
log.error("Error processing message: ", te);
}
}
}
MyConnectionFactory 管理対象オブジェクトの設定
Pool Name: jms/MyConnectionFactory
JNDI Name: jms/MyConnectionFactory
Resource Type: javax.jms.ConnectionFactory
Status: [X] Enabled
Initial Minimum Pool Size: 8
Maximum Pool Size: 64
Pool Resize Quantity: 2
Idle Timeout: 300 seconds
Max Wait Time: 60000 Milliseconds
Transaction Support: EMPTY (I assume this means the default level, which is XA?)
Connection Validation: [X] Required
No Additional Properties
接続プールの詳細タブで:
Validate At Most Once: 0 Seconds
Leak Timeout: 0 Seconds
Leak Reclaim: [ ]
Creation Retry Attempts: 0
Retry Interval: 10 Seconds
Pooling: [X] Enabled
Lazy Association: [ ] Enabled
Lazy Connection Enlistment: [ ] Enabled
Associate with Thread: [ ] Enabled
Match Connections: [X] Enabled
Max Connection Usage: 0
Queue 管理対象オブジェクトの設定は次のようになります。
JNDI Name: jms/myApp/MyQueue
Resource Adapter: jmsra
Resource Type: javax.jms.Queue
Class Name: com.sun.messaging.Queue
Status: [X] Enabled
Additional Properties:
Name | Value
---- -------
Name MyQueue