Java计时器服务多次运行

12

我创建了一个名为doWork()的函数,计划每天在凌晨1点运行,该函数如下:

@Schedule(hour = "1", persistent = false)
    public void doWork()
    {
        System.out.println("Starting .....\nTIME: " + System.currentTimeMillis());
        System.out.println("this : " + this);

        //Some code here, if-conditions and try/catch blocks. No loops

        System.out.println("Exiting .....\nTIME: " + System.currentTimeMillis());
        System.out.println("this : " + this);
    }
问题在于这个函数运行的次数比预定的要多。
一开始创建时,它按预期每天准确地在凌晨1:00:00运行。几天后,它开始在1:03:00运行(这毫无道理,因为它是非持久性的,服务器也没有停机)。之后,该函数开始以非常短的时间间隔运行多次(相差几秒钟)。
有人知道可能是什么原因引起的吗?或者告诉我可以做些什么来解决它?
[编辑]: 环境详细信息
应用程序服务器:WebSphere Application Server 8.5.5 IDE: Rational Application Developer 9.1 数据库管理系统:IBM DB2 10.1

3
你使用哪个应用服务器(及版本)?这可能有助于检查与该应用服务器相关的问题。 - Olivier Grégoire
你是如何记录服务运行时间的?日志输出?系统/服务器时间? - kolossus
@OlivierGrégoire 我正在使用WebSphere应用服务器8.5.5。 - Ahmed Anwar
@kolossus 两个时间都是基于服务器时间记录的。该方法计划在凌晨1点运行,但由于某些原因它会在随机时间运行,并且会运行多次。 - Ahmed Anwar
1个回答

1
我相信您正在观察容器尝试重试失败的doWork()调用。
EJB计时器服务是事务性的。
如果超时方法的执行抛出任何运行时异常,那么事务将被回滚,并且容器将尝试再次执行超时方法。请参见EJB 3.1规范的§18.2.8事务。
此外,如果事务超时到期,某些实现将只标记当前事务以进行回滚并继续处理。这最终将导致定时器调用失败,并将尝试重试。
该重试机制未得到很好的说明,实际行为因实现而异。有些会永远重试,而其他一些则在若干次尝试后放弃。例如,WebSphere提供了一种指定重试策略的方法。请参见使用EJB计时器服务创建企业Bean的计时器
最后,如果您的应用程序跨多个节点运行,则可能每个服务器实例都有一个计时器正在运行。根据规范的§18.2.3非持久性计时器:
对于自动非持久化定时器,容器会在应用程序初始化期间为分布式容器中的每个JVM创建一个新的非持久化定时器。
如果它们都执行相同的任务,那么很可能会引起足够的混乱,从而生成错误、回滚和随后的重试。

在任何人可以进一步推测之前,您仍需要提供有关您的环境的更多详细信息。此外,在doWork()退出后,您如何确定初始事务已成功完成? - Steve C
我确定第一次成功完成是因为有日志记录。通过检查数据库日志,我可以看到从解析的 .csv 文件中的记录已经成功持久化到了数据库中。然后它继续重试,删除数据库中的所有内容,最终导致我得到一个空的数据库。 请检查环境详细信息的编辑。 - Ahmed Anwar
你有多少个服务器实例?EJB是@Singleton还是@Stateless? - Steve C
该方法执行需要多长时间? - Steve C
它是@Stateless的。并且在2个节点上有2个服务器实例。 该方法需要大约一个小时,因为它需要一个巨大的csv文件(超过100,000行数据),将其解析并持久化到数据库中。 - Ahmed Anwar
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接