当前位置: 首页 > 知识库问答 >
问题:

使用 EJB 3 发送 JMS 消息时的事务划分

程墨竹
2023-03-14

在我的Java EE应用程序中,我实现了一个异步数据库记录器作为MDB,它通过JMS接收XML消息并将其写入数据库。

在另一个 MDB 中,我使用以下代码创建一条日志消息并将它们发送到记录器 MDB 的输入队列:

    public static void log(String correlId, String message, String data) throws Exception{

            SysLogEntry sysLogEntry = new SysLogEntry();
            sysLogEntry.setCorrelId(correlId);
            sysLogEntry.setDatetimeCreate(new Date());
            sysLogEntry.setMessage(message);
            sysLogEntry.setData(data);

            ConnectionFactory jmsConnectionFactory = (ConnectionFactory)initialContext.lookup(JMS_CONNECTION_FACTORY_JNDI_NAME);
            Destination logEventDestination = (Destination) initialContext.lookup(LOG_EVENT_DESTINATION_JNDI_NAME);

            JmsUtils.sendMsgToDestination(JaxbUtils.toString(sysLogEntry, jaxbContext), jmsConnectionFactory, logEventDestination, false, Session.AUTO_ACKNOWLEDGE);

        }

public static void sendMsgToDestination(String payload, ConnectionFactory connFactory, Destination destination, boolean sessionTransacted, int acknowledgeMode) throws JMSException{
        if(payload == null)
            throw new IllegalArgumentException("Message payload is null");
        if(connFactory == null)
            throw new IllegalArgumentException("Connection factory is null");
        if(destination == null)
            throw new IllegalArgumentException("Message destination is null");

        Connection connection = null;
        try{
            connection = connFactory.createConnection();
            Session session = connection.createSession(sessionTransacted, acknowledgeMode);
            MessageProducer messageProducer = session.createProducer(destination);
            TextMessage textMessage = session.createTextMessage();
            textMessage.setText(payload);
            messageProducer.send(textMessage);
        } finally {
            if(connection != null){
                try{
                    connection.close();
                } catch (JMSException ignore){

                }
            }
        }
    }

哪里

  • SysLogEntry 是一个带有 JAXB 注释的类,我用它来序列化
  • JMS_CONNECTION_FACTORY_JNDI_NAME是 XA 连接工厂的 JNDI 名称

但是,每次回滚创建日志消息的事务时,日志消息都不会放入记录器输入队列。

有人可以告诉我我的代码出了什么问题吗?我考虑过使用一个单独的无状态会话 bean,它将启动一个新的事务来生成日志消息,但这种方法有一个缺点:在 EJB 内部,我可以轻松地让容器注入记录器 bean,但我也有非 EJB 对象,我想在其中执行异步日志记录。

我的应用程序服务器是Weblogic 10.3.3

共有3个答案

漆雕育
2023-03-14

我将创建一个单独的 Bean 来记录消息,并注释 sendMessage 以启动自己的和独立的事务:

java prettyprint-override">@Stateless
public class SenderBean  {

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public sendMsgToDestination(String payload, ConnectionFactory connFactory, Destination destination, boolean sessionTransacted, int acknowledgeMode) throws JMSException{
        ...
        Connection connection = null;
        try(
           connection = connFactory.createConnection();
           Session session = connection.createSession(sessionTransacted, acknowledgeMode);
        ){
            MessageProducer messageProducer = session.createProducer(destination);
            TextMessage textMessage = session.createTextMessage();
            textMessage.setText(payload);
            messageProducer.send(textMessage);
        }
    }

然后我会使用这个 BEAN 进行日志记录,即使您的调用方事务被回滚,它也会将消息写入队列

羊舌高明
2023-03-14

可以将包含静态日志方法的类的静态成员分配给无状态 EJB 代理的实例。代理本身只是将调用路由到实例池中的空闲 Bean,因此可以静态共享它。

但。。。启动新事务只是为了逃避当前事务听起来很浪费。特别是如果频繁记录此异步记录器,您可能希望以不同的方式执行此操作。

使用一个简单的Java SE执行器池和几个线程,然后向该池提交工作(一个可运行)。在这个可运行代码中,您仍然可以发送 JMS 消息,保持现有代码不变。当池中的线程接听工作时,事务上下文将丢失,但这正是您首先需要的。

孙熠彤
2023-03-14

如果发生系统异常(或 sessionContext.setRollbackOnly),则全局事务将由容器回滚(如果使用容器管理的事务)。这意味着对全局事务中登记的 XA 感知资源的任何操作都将回滚。

在您的情况下,这包括要发送到记录器的待处理消息。这个被删除,因为事务被回滚。

在您的情况下,如果您使用非 XA 连接工厂,这应该就足够了。这样,消息应该立即发送(在记录器甚至有帮助的情况下)。因此,发送仅在跨越 JMS 发送任务的本地事务中运行。

至于非 EJB 对象:您可以使用非 XA 连接工厂将发送功能提取到普通的 Java 类(= 不是 EJB)中,并从 EJB 和普通类中使用它。

 类似资料:
  • null 谁能给我一个向RabbitMQ发送消息的标准程序的例子。我正在使用Spring Boot,也可以使用它的特性。

  • 我想使用SpringBoot向ActiveMQ队列发送消息。应用程序应在发送后终止,但仍保持活动状态。 这是我的申请代码: 在没有任何父节点的情况下使用以下依赖项(Maven): 和一行

  • Azure服务总线可以发送计划的消息。使用此处描述的AMQP协议发送计划消息:https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-amqp-request-response#message-operations 调度消息。要求 请求消息必须包括以下应用程序属性: |键|值|类型|必需|值内容 |操作|字符

  • 问题内容: 我正在尝试创建一个Java Application Client项目,该项目将JMS消息发送到Glassfish服务器上的队列。 问题在于,应用发送完消息后,它在应退出时挂起。该消息已成功传输,但是由于某种原因该应用程序没有退出。我已经尝试调试该应用程序,并且可以将其一直走到的结尾,这就是它的挂起位置。 这是代码: 如何使其停止挂起? 问题答案: 长期以来,它一直是Glassfish中

  • 如何延迟JMS消息发送或在不确定的时间内继续? 我使用的是Weblogic,正如您所知,在JMS发送之后,接收方将异步处理消息,但是,此时或有时外部资源还没有为接收方做好准备,因此,我想使用一些检查逻辑来延迟发送或处理消息。我猜例如:我将消息放入挂起队列,然后频繁检查资源可用性,一旦发送或继续消息? 大家都知道Weblogic是否支持这一点,或者如何实现它吗?

  • 我们有一个Spring Boot应用程序,用于在另一个组件上执行负载测试。我们每分钟最多需要发送35000条JMS消息,因此我使用调度器每分钟运行一次任务。 问题是当我保持低强度时,它会设法在指定的时间间隔(一分钟)内发送消息。但是当强度很高时,发送消息块需要超过1分钟。对以下实现有任何建议吗? 调度程序类 用于发送消息的类

  • 我是Spring JMS的新手。我的应用程序是使用Spring Boot开发的,并部署在JBoss EAP7.2.0中。我有一个远程队列,它是一个活动的MQ Artemis队列,也嵌入在JBoss EAP7.2.0中。有人能建议我如何使用Spring Boot的JmsTemplate向远程JMS队列发送消息吗?基本上,我不知道应该如何定义远程connectionFactory来连接到远程队列。

  • 最近,我不得不支持一位同事验证为什么一些系统测试没有通过wildfly,即在weblogic和glass fish上一致通过的系统测试。 分析日志后,很明显,原因与受支持线程发送的JMS消息过早提交到队列有关,当时的期望是当MDB的切入点容器托管事务提交时消息将被提交。因此,在发送消息的MDB运行完成之前,消息会发出。 在weblogic中,为了实现预期的行为,需要确保在获取容器(已配置XA)提供