8.12. jakarta Enterprise Beans-clustered 数据库计时器
JBoss EAP 支持群集数据库支持定时器,用于在群集环境中持久保留 Jakarta Enterprise Beans 计时器。因为集群是通过数据库提供的,如果计时器在较短的时间内停机的数量增加,则性能会降低。您可以使用 ejb3/service=timer 属性来优化性能。
-service/database execution-data-store 组件的 refresh-interval 和 allow-
您还可以在非集群模式下使用数据库计时器,如下所示:
-
将
refresh-interval设置为0。 - 为每个节点提供唯一的分区名称,或者对每个节点使用不同的数据库。
Jakarta Enterprise Beans-clustered 数据库计时器如下:
- 允许执行计时器的每个节点都会为每个计时器调度一个超时时间。
当这个超时过期时,每个节点都会尝试通过将其状态更新为 running 来锁定计时器。
更新其状态的查询类似以下查询:
UPDATE JBOSS_EJB_TIMER SET TIMER_STATE=? WHERE ID=? AND TIMER_STATE<>? AND NEXT_DATE=?;
由于使用了事务处理和 READ_COMMITTED 或 SERIALIZABLE 隔离模式,只有一个节点成功更新该行,这是计时器执行的节点。
8.12.1. 设置 Jakarta Enterpise Beans-clustered 计时器 复制链接链接已复制到粘贴板!
您可以通过添加数据库支持的计时器存储来设置 Jakarta Enterprise Beans-clustered 计时器。
先决条件
- 数据库必须支持 READ_COMMITTED 或 SERIALIZABLE 隔离模式。
流程
创建数据库支持的计时器存储:
/subsystem=ejb3/service=timer-service/database-data-store=my-clustered-store:add(allow-execution=true, datasource-jndi-name="java:/MyDatasource", refresh-interval=60000, database="postgresql", partition="mypartition")根据以下内容设置参数:
-
allow-execution: 设置为true,以允许此节点执行计时器。如果将它设置为false,JBoss EAP 会将此节点上的计时器添加到数据库,供另一个节点执行。当您将定时器执行限制为集群中几个节点时,可以减少总体数据库负载。 -
datasource-jndi-name:要使用的数据源。 refresh-interval:在此节点检查数据库是否由其他节点添加的新计时器前,设置必须调整的时间间隔。该值以毫秒为单位。重要设置较小的值意味着 JBoss EAP 加快使用定时器,但会增加数据库的负载。如果添加计时器的节点因为失败或
allow-execution为 false,则此计时器可能无法运行,直到节点刷新后为止。Database:定义正在使用的数据库的类型。些 SQL 语句由数据库自定义。如果未定义此属性,服务器将尝试自动检测该类型。目前支持的类型有
postgresql、mysql或acle、db2、hsql和h2。SQL 存在于以下文件中:module
/system/layers/base/org/jboss/as/ejb3/main/timers/timer-sql.properties您可以通过向此文件中添加新的数据库特定 SQL 语句来修改已执行的 SQL 或添加对新数据库的支持。
-
分区:将值设置为您希望此节点所属的分区的名称。只有同一分区的节点的计时器才对此节点可见。使用此属性将大型集群分成几个较小的集群,以提高性能。
-
要将此数据库数据存储用于非集群计时器,请将 refresh-interval 设置为零,并确保每个节点都有唯一的分区名称,或者对每个节点使用不同的数据库。
8.12.2. 在部署中使用 Jakarta Enterprise Beans-clustered 计时器 复制链接链接已复制到粘贴板!
您可以使用单个数据存储作为所有应用程序的默认数据存储,或者为每个应用程序使用特定的数据存储。
先决条件
- 您已设置了 Jakarta Enterprise Beans-clustered 数据库支持的定时器存储。
流程
要将单个数据存储用作所有应用程序的默认值,请按如下所示更新
ejb3子系统中的default-data- 存储:<timer-service thread-pool-name="timer" default-data-store="clustered-store"> <data-stores> <database-data-store name="clustered-store" datasource-jndi-name="java:jboss/datasources/ExampleDS" partition="timer"/> </data-stores> </timer-service>要将单独的数据存储用于特定应用程序,请在
jboss-ejb3.xml文件中设置定时器数据存储名称:<?xml version="1.1" encoding="UTF-8"?> <jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:timer="urn:timer-service:1.0" xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd" version="3.1" impl-version="2.0"> <assembly-descriptor> <timer:timer> <ejb-name>*</ejb-name> <timer:persistence-store-name>my-clustered-store</timer:persistence-store-name> </timer:timer> </assembly-descriptor> </jboss:ejb-jar>
您可以通过对业务方法配置 Jakarta Interceptors 以编程方式刷新计时器,从而强制计时器在 刷新过期前进行 刷新。
在集群部署中,如果多个节点在较短的时间内更新其数据存储,内存中计时器状态可能会临时变得不同步。
先决条件
- 您已配置了数据库支持的集群 Jakarta Enterprise Beans。
流程
实施 Jakarta Interceptors,使
wildfly.ejb.timer.refresh.enabled设为true。import javax.interceptor.AroundInvoke; import javax.interceptor.Interceptor; import javax.interceptor.InvocationContext; /** * An interceptor to enable programmatic timer refresh across multiple nodes. */ @Interceptor public class RefreshInterceptor { @AroundInvoke public Object intercept(InvocationContext context) throws Exception { context.getContextData().put("wildfly.ejb.timer.refresh.enabled", Boolean.TRUE); return context.proceed(); } }配置 Jakarta Interceptors。
您可以将 Jakarta Interceptors 配置为目标无状态或单例 bean 商业方法。当
wildfly.ejb.timer.refresh.enabled设为true时,调用TimerService.getAllTimers()会在返回计时器前刷新计时器数据存储。@Singleton public class RefreshBean1 ... { @Interceptors(RefreshInterceptor.class) public void businessMethod1() { ... // since wildfly.ejb.timer.refresh.enabled is set to true in interceptor for this business method, // calling timerService.getAllTimers() will first refresh from timer datastore before returning timers. final Collection<Timer> allTimers = timerService.getAllTimers(); ... } }或者,您也可以实施一种专用的业务方法,以便在需要时以编程方式刷新应用的其他部分调用的计时器。
@Interceptors(RefreshInterceptor.class) public List<Timer> getAllTimerInfoWithRefresh() { return timerService.getAllTimers(); } public void businessMethod1() { final LocalBusinessInterface businessObject = sessionContext.getBusinessObject(LocalBusinessInterface.class); businessObject.getAllTimerInfoWithRefresh(); // timer has been programmatically refreshed from datastore. // continue with other business logic... }