【问题标题】:Catch Exceptions inside a Message Driven Bean (MDB)在消息驱动 Bean (MDB) 中捕获异常
【发布时间】:2012-12-12 12:40:34
【问题描述】:

我必须如何处理 mdb 中的异常?我有一种有趣的感觉,即异常发生在 try catch 块之后,所以我无法捕获并记录它。 Glassfish v3 决定重复整个消息。它运行到一个无限循环并在硬盘驱动器上写入大量的日志文件。

我正在使用 Glassfishv3.01 + Eclipselink 2.0.1

public class SaveAdMessageDrivenBean implements MessageListener {

    @PersistenceContext(unitName="QIS") 
    private EntityManager em;

    @Resource
    private MessageDrivenContext mdc;

    public void onMessage(Message message) {
        try {
            if (message instanceof ObjectMessage) {
                ObjectMessage obj = (ObjectMessage)message;
                AnalyzerResult alyzres = (AnalyzerResult)obj.getObject();
                save(alyzres);
            }
        } catch (Throwable e) { 
            mdc.setRollbackOnly();
            log.log(Level.SEVERE, e);
        }
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    private void save(AnalyzerResult alyzres) throws PrdItemNotFoundException {

       Some s = em.find(Some.class, somepk);
       s.setSomeField("newvalue");

       // SQL Exception happens after leaving this method because of missing field for ex.
    }
}    

【问题讨论】:

    标签: java jpa eclipselink glassfish-3


    【解决方案1】:

    您遇到了严重的消息中毒案例...

    我看到的主要问题是:

    • 您在onMessage() 中直接调用save() 方法:这意味着容器无法在save 方法周围注入适当的事务处理代理
    • 无论如何save() 方法应该有@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 以便在单独的事务中提交,否则它将加入onMessage 事务(默认为REQUIRED)并绕过您的异常处理代码,beign成功执行onMessage后提交

    我要做的是:

    save 方法移动到新的无状态会话bean:

    @Stateless
    public class AnalyzerResultSaver
    {
        @PersistenceContext(unitName="QIS") 
        private EntityManager em;
    
        @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
        private void save(AnalyzerResult alyzres) throws PrdItemNotFoundException {
            Some s = em.find(Some.class, somepk);
            s.setSomeField("newvalue");
            // SQL Exception happens after leaving this method
        }
    }
    

    在你的 MDB 中注入这个 bean:

    public class SaveAdMessageDrivenBean implements MessageListener {
    
        @Inject  
        private AnalyzerResultSaver saver;
    
        @Resource
        private MessageDrivenContext mdc;
    
        public void onMessage(Message message) {
            try {
                if (message instanceof ObjectMessage) {
                    ObjectMessage obj = (ObjectMessage)message;
                    AnalyzerResult alyzres = (AnalyzerResult)obj.getObject();
                    saver.save(alyzres);
                }
            } catch (Throwable e) { 
                mdc.setRollbackOnly();
                log.log(Level.SEVERE, e);
            }
        }
    }
    

    另一个提示:在此代码中,消息中毒仍然存在。现在它派生自调用mdc.setRollbackOnly(); 的行。

    我建议在这里记录异常并将消息传输到毒队列,从而防止容器无限地重新提交消息。

    更新:

    “毒药队列”或“错误队列”只是保证您(希望可恢复)丢弃的消息不会完全丢失的一种手段。它在无法保证消息数据正确性的集成场景中大量使用。

    设置有害队列意味着定义目标队列或主题并将“坏”消息重新传递到该目标。

    操作员应定期检查此队列(通过专用应用程序)并修改消息并重新提交到“良好”队列,或者丢弃消息并要求重新提交。

    【讨论】:

    • 非常感谢。我如何“将消息转移到毒队列”?
    • @HasanTuncay 澄清了“毒药队列部分”。我不确定在您的情况下是否有必要,因为中毒是由“编程”错误引起的。这里的日志应该足够了,所以直接省略mdc.setRollbackOnly();
    【解决方案2】:

    我相信您发布的代码大部分都可以。

    你的使用

        @TransactionAttribute(TransactionAttributeType.REQUIRED)
    

    被完全忽略,因为这个(以及大多数其他)注释只能应用于业务方法(包括 onMessage)。不过这并不重要,因为您的 onMessage 方法会免费获得一个隐式方法。

    这导致消息处理在 Java EE 容器中是事务性的。如果交易因任何原因失败,容器需要再次尝试传递消息。

    现在,您的代码正在从 save 方法中捕获异常,这很好。但是随后您将明确标记事务以进行回滚。这具有告诉容器消息传递失败并且它应该重试的效果。

    因此,如果您删除:

        mdc.setRollbackOnly();
    

    容器将停止尝试重新传递消息。

    【讨论】:

      【解决方案3】:

      如果我没记错的话,您是在让容器处理交易。这样,实体管理器将在方法完成后将要刷新的操作排队,这就是方法完成后出现异常的原因。

      直接使用em.flush()作为方法的最后一步将执行事务的所有相关查询,在提交事务时容器生成flush()时抛出异常,而不是稍后抛出。

      【讨论】:

      • flush() 是只提交我在这个 mdb 中所做的更改,还是刷新同时运行的其他 mdb 中的各种事务。那么使用flush时entitymanager的重点是什么?
      • 我该如何停止无限循环?
      • flush() 方法将对象的实际持久化带到方法(和当前事务)的范围内。通过用try-catch 块包围它,您应该能够捕获异常并处理它们。
      • 消息一次又一次地从队列中传来。不知何故,回滚似乎告诉队列该消息未正确处理。我想将异常放入日志并从队列中删除消息或用“好的。它完成了”来确认它
      • 您是否尝试在 catch 块中这样做?
      猜你喜欢
      • 2017-06-13
      • 1970-01-01
      • 1970-01-01
      • 2014-07-24
      • 1970-01-01
      • 2011-08-05
      • 2019-10-03
      • 2015-09-27
      • 2011-06-09
      相关资源
      最近更新 更多