4

在集群 JEE6 环境(Glassfish 3.1.2)中,@Singleton 可以/可以在每个集群节点上创建一个 bean。如果这个 Singleton Bean 在其上注册了一个程序计时器@PostConstruct- 该@Timeout方法多久执行一次?- 仅在其中一个单例上(每个滴答声),或者对于每个注册了计时器的 Singeton 一次(每个滴答声)?

下面的代码是一个例子,这个问题对这段代码意味着什么。

@Singleton
public class CachedService {

@Resource
private TimerService timerService;

    private static final long CACHE_TIMEOUT_DURATION_MS = 60 * 60 * 1000;

    @PostConstruct
    void initResetTimer() {
        this.timerService.createIntervalTimer(CACHE_TIMEOUT_DURATION_MS,
            CACHE_TIMEOUT_DURATION_MS,
            new TimerConfig("current user cache timeout", false));
    }

    @Timeout
    public void executeResetTimer() {
        this.clearCache();
    }
}

示例:应用程序在集群中的 3 个节点上运行。假设 Singleton 在每个节点上都实例化,所以initResetTimer总共执行 3 次(每个节点一次)。那么问题是:是否每小时清除一次所有节点上的缓存(被调用)?executeResetTimer

(我知道计时器不会在所有节点上同时计时,因为 Singleton 是在不同时间实例化的,但这不是问题/问题。)

4

3 回答 3

4

首先,确保您已按照此处所述为外部共享 XA 数据源设置计时器服务。

在过去深入研究过您的问题后,我记得开发人员在邮件列表中的一些解释,Glassfish 的实现如下:

假设您在集群中有节点 A、B 和 C。在节点 A 创建的持久定时器由节点 A“拥有”(即定时器事件被传递到节点 A)。如果节点 A 发生故障,则可以将其计时器迁移到另一个活动节点。

由于 Glassfish不支持cluster-wide @Singletons,您最终会得到与调用一样多的计时器initResetTimer()。此外,每个服务器重新启动/重新部署可能会为每个集群节点创建一个新的计时器实例,除了旧的未取消的计时器,所以不要忘记取消您以编程方式创建的计时器:) 为避免这种情况,请一起使用声明性@Schedule(...)方法,Glassfish 将创建计时器跨集群一次,并希望在失败时自动迁移它们。

希望这可以帮助。

更新:

以编程方式创建的计时器(持久性或非持久性)将在它创建的 JVM/节点中被触发,而不管集群设置与否。大致可以总结一下:独立定时器实例数等于调用次数timer.createXxxTimer()

于 2012-07-13T06:56:07.337 回答
2

我查看了 EJB 3.1 规范的第 18 章“定时器服务”。该应用程序应根据规范独立于集群运行。

我的理解是,如果createIntervalTimer在集群中调用一次,则计时器应该根据集群中的节点数独立触发一次。由于每个单例 bean(根据您的问题)调用createIntervalTimer,它将被执行n次。它类似于在 ServletContextListener 中创建计时器

不过,这是理论。我会仔细检查您定位的特定应用服务器。在 glassfish 中,集群范围的计时器需要使用外部数据库配置计时器池。

于 2012-07-04T11:53:53.807 回答
1

即使它不是一个直接的回答,这无论如何都会有所帮助:每个集群环境只配置一个实例的一种方法是将单例 ejb 公开为 MXbean。您应该必须公开一个托管的 imterface,甚至可以是 empty ,然后在 @PostCostruct 标记的方法中将您的 ejb 注册到 jmx 服务。最后,您必须提供一个 @PreDestroy 挂钩才能从 jmx 服务中注销。这是 Java Champion Adam Bien 建议的方式。

于 2012-07-09T09:06:13.403 回答