您如何确保 EJB 计时器仅在 Java EE 集群环境中创建一次?

How do you make sure that an EJB timer is only created once in a Java EE clustered environment?

我正在开发一个在 IBM WebSphere Application Server 上的集群 Java EE 环境中运行的应用程序。我们有一个启动单例 bean,它创建一个持久的 EJB 计时器,我们配置了 EJB 计时器服务,以便每个集群节点使用相同的数据库表。如何确保 2 个节点不会在启动时同时创建持久计时器?我知道如何确保实际上只有一个节点 运行 超时方法,但不知道如何确保计时器不会被创建两次。

我们目前正在取消并重新创建启动时具有相同 "name" 的所有计时器,这意味着(如果我理解正确的话),只要集群节点不同时初始化这个 bean,那么最后一个要启动的节点将清除所有现有计时器并重新创建单个计时器实例。但是,我如何确保我们避免 2 个节点同时退出 cancelTimer() 方法和 运行 TimerService.createTimer() 方法的罕见情况,为集群创建 2 个相同的计时器?

下面是一些示例代码:

@Startup
@Singleton
public class Timer{
    String timerName = "myTimer";

    @Resource
    private SessionContext sessionCtx;

    @PostConstruct
    public void start() {
        cancelTimer();
        TimerService ts = sessionCtx.getTimerService();
        ts.createTimer(new Date(), 60000, timerName);
    }

    @PreDestroy
    public void stop() {
        cancelTimer();
    }

    public void cancelTimer() {
        TimerService ts = sessionCtx.getTimerService();
        Collection<Timer> timers = ts.getTimers();
        for(Timer timer : timers){
            if (timer.getInfo().equals(timerName)) {
                timer.cancel();
            }
        } 
    }

    @Timeout
    public void timeout() {
        System.out.println("timeout!");
    }
}

在传统的 WebSphere Application Server 中,如果您切换到 自动 持久计时器(例如,通过使用 @Schedule 注释)并且所有集群成员都具有 EJB 容器配置指向同一集群范围内的调度程序,然后应用程序服务器将负责确保在整个集群中恰好调度一个自动计时器实例。如果您无法切换到自动持久计时器并且需要继续手动创建它们的方法,一种常见的解决方案是使用数据库作为同步点(这实际上是应用程序服务器所做的)。例如,您的 start 方法可以尝试对数据库条目进行特定更新,表明它已保留安排计时器的权利,并且只有在成功完成后,才会继续安排计时器。