синхронная связь MDB, проблема с максимальным размером пула - PullRequest
0 голосов
/ 10 мая 2011

Я использую Java Enterprise (3.1) с Glassfish.У меня есть два отдельных EAR, которые общаются синхронно через JMS.Более конкретно:

EAR1 использует обмен сообщениями JMS, чтобы сообщить EAR2, что делать.EAR1 начинает прослушивать ответ от EAR2 (QueueReceiver.receive).EAR2 получает сообщение и, соответственно, выполняет некоторую обработку, а затем отправляет сообщение JMS обратно в EAR1 с выводом.

Все это работает нормально.Пока я не получу это исключение:

[#|2011-05-10T15:05:27.382+0200|WARNING|glassfish3.1|javax.enterprise.resource.resourceadapter.com.sun.enterprise.connectors|_ThreadID=90;_ThreadName=Thread-1;|RAR5117 : Failed to obtain/create connection from connection pool [ jms/QueueConnectionFactory ]. Reason : com.sun.appserv.connectors.internal.api.PoolingException: In-use connections equal max-pool-size and expired max-wait-time. Cannot allocate more connections.|#]

Так что похоже, что контейнер не использует MDB.Вместо этого он создает новые, пока я не достигну предела.Я знаю, что причина этого в том, что MDB в EAR2 используют JMS для отправки результатов.Я предполагаю, что в экземпляре MDB все еще есть некоторые ресурсы, которые вызывают поведение.

Если я просто использую MDB для распечатки полученного сообщения, я могу продолжать отправлять сообщения весь день, так что это определенносвязан с соединением JMS.

Я занимаюсь этим уже два дня, поэтому, если кто-то захочет предложить какую-либо помощь, это будет с благодарностью.

этот код работает весь день:

package xxx;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;

@MessageDriven(activationConfig = {
    @ActivationConfigProperty(
        propertyName="destinationType",
        propertyValue="javax.jms.Queue")
}, mappedName = "AssociationQueue1")
public class AssociationMDB implements MessageListener {

    @Override
    public void onMessage(Message arg0) {
        MapMessage msg = (MapMessage)arg0;
        String source = null;
        String target = null;
        try {
            source = msg.getString("source");
             target = msg.getString("target");
        } catch (JMSException e) {
            e.printStackTrace();
        }       
        System.out.println(source + " " + target);
    }
}

Пока этого нет:

package xxx;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageListener;

@MessageDriven(activationConfig = {
    @ActivationConfigProperty(
        propertyName="destinationType",
        propertyValue="javax.jms.Queue")
}, mappedName = "AssociationQueue1")
public class AssociationMDB implements MessageListener {

    @Override
    public void onMessage(Message arg0) {
        Logger logger = Logger.getLogger(this.getClass().getSimpleName());
        QueueConnection qConnect = null;
        QueueSession qSession = null;
        QueueSender qSender = null;
        try {
            InitialContext context = new InitialContext();
            Queue responseQ = (Queue)context.lookup("AssociationQueue2");
            QueueConnectionFactory factory = (QueueConnectionFactory) context.lookup("jms/QueueConnectionFactory");
            qConnect = factory.createQueueConnection();
            qSession = qConnect.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
            qConnect.start();
            qSender = qSession.createSender(responseQ);

            TextMessage answer = qSession.createTextMessage();
            answer.setText("hey");
            qSender.send(answer);
            logger.info("message sent");
        }
        catch (JMSException jmse) {
            jmse.printStackTrace();
        } catch (NamingException e) {
            e.printStackTrace();
        }
        finally {
            try {
                if(qSender != null) {
                    qSender.close();
                    logger.info("cleaning qSender");
                }
                if(qSession != null) {
                    qSession.close();
                    logger.info("cleaning qSession");
                }
                if(qConnect != null) {
                    qConnect.close();
                    logger.info("cleaning qConnect");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }   
        }

    } 

(я также пытался использовать более новые, более причудливые вещи EJB, такие как нотации и т. Д., Но тоже не работал ...)

Себастьян

Ответы [ 2 ]

2 голосов
/ 22 мая 2011

Сам шаблон не кажется проблематичным. MDB наверняка разрешено отправлять сообщения в другую очередь. Является ли это ответом на что-то или просто делегирует работу, это не имеет отношения к рассматриваемой проблеме.

Проблема, по-видимому, заключается в том, что вы звоните qConnect.start(). Это подготавливает соединение для прослушивания входящего трафика. Для отправки сообщения это, таким образом, не нужно. Вам также не нужно закрывать сеанс и отправителя явно. И хотя вы закрываете соединение в блоке finally, оно легко уязвимо для утечек соединения, если какой-либо из приведенных выше кодов выдает его.

Кстати, ваша формулировка "не повторно использовать MDB" также не совсем верна. Я думаю, что вы имели в виду "не использовать соединения"?

Также кажется, что вы запускаете оба EAR на одном и том же экземпляре Glassfish. Это означает, что EAR1 использует тот же пул соединений, что и EAR2, и, следовательно, утечка соединения также может быть вызвана кодом, вызывающим QueueReceiver.receive.

Наконец, обратите внимание, что прослушивание напрямую подключений JMS для входящего трафика на самом деле вообще не разрешено в Java EE, особенно в EJB *. Это одна из раздражающих частей, где JMS API ведет себя по-разному автономно и используется внутри продукта Java EE. Некоторые серверы (например, JBoss AS) имеют разные фабрики соединений, где некоторые могут использоваться для прослушивания, а другие - нет. Я не знаю точных подробностей о Glassfish, но тот факт, что вы запускаете соединение для прослушивания, молча нарушает спецификацию и может быть основной причиной вашей проблемы.

*) Кроме того, есть некоторая неясность, касается ли это только EJB или сервлетов. Например. JBoss AS 6 запрещает как EJB, так и сервлетам прослушивать соединения с использованием Java EE-совместимого java: / JmsXA, но только для EJB Weblogic 8, а не сервлетов.

0 голосов
/ 25 сентября 2013

Арджан дал очень хорошее объяснение. Спасибо за это. Я просто хотел бы добавить кое-что.

В моем случае я получал фабрику соединений, используя @Resource вместо поиска по контексту, который, я думаю, очень похож. Теперь важно отметить, что QueueConnection.close () обрабатывается контейнером. Таким образом, закрытие не обязательно означает, что ваше соединение закрывается при срабатывании этого вызова. Это важно помнить на всех @ Ресурсах. При создании MDB ресурсы будут инициализироваться с помощью DI. После уничтожения MDB они снова будут освобождены. Это для фиксации и отката.

Таким образом, если вы, возможно, отправляете несколько сообщений из сеанса вашего MDB, эти соединения будут освобождены только после завершения сеанса вашего MDB. Да, я знаю, обычно один запрос = один ответ, но в моем случае я получил CSV-запрос, чтобы сгенерировать несколько XML-сообщений и направить каждую строку в очередь, если вы понимаете. Поэтому, как правило, вызов ConnectionFactory.createQueueConnection из вашего метода sendMessage может быть рискованной идеей. Вместо этого передайте экземпляр QueueConnection в качестве параметра.

...