2014年7月20日星期日

EAP_010:EJB事务超时设置

开发运行环境:JBoss EAP 6.2.0。

问题来自一个客户,需求很简单:当调用某个带事务的EJB的方法时,允许设置一个超时时间,当超过设定时间,没有得到响应时,可以捕捉到超时异常,然后进行异常处理。

1. 测试事务超时的EJB

import javax.ejb.Remote;
import javax.ejb.Stateless;


@Stateless
@Remote(RemoteTranTimeout.class)
public class TranTimeoutBean implements RemoteTranTimeout {

    @Override
    public String sayHello(String name) {
        System.out.println("======sleeping=====");
        long transBMillis = System.currentTimeMillis();
        long transEMillis = 0;
        while (true) {
            transEMillis = System.currentTimeMillis();
            if ((transEMillis - transBMillis) / 1000 >= 15) {
                break;
            }
        }
        System.out.println("======wake up=====");
        System.out.println("do someting else");
        System.out.println("end");
        return "Hello, " + name;
    }
}

实现很简单,就是sleep15秒。

2. 在META-INF目录下创建jboss-ejb3.xml,该文件和jboss.xml在同一目录。

<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
               xmlns="http://java.sun.com/xml/ns/javaee"
               xmlns:tx="urn:trans-timeout"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               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
urn:trans-timeout http://www.jboss.org/j2ee/schema/trans-timeout-1_0.xsd"
               version="3.1"
               impl-version="2.0">
    <assembly-descriptor>
        <container-transaction>
            <method>
                <ejb-name>TranTimeoutBean</ejb-name>
                <method-name>*</method-name>
                <method-intf>Remote</method-intf>
            </method>
            <tx:trans-timeout>
                <tx:timeout>5</tx:timeout>
                <tx:unit>Seconds</tx:unit>
            </tx:trans-timeout>
        </container-transaction>
    </assembly-descriptor>
</jboss:ejb-jar>

这里我设置的事务超时时间是5秒。

2. 重新编译、发布、运行EJB
服务器日志如下:可以看出5秒钟后,事务管理器发现事务超时,然后回滚了该事务。

15:36:58,165 INFO  [stdout] (EJB default - 2) ======sleeping=====
15:37:03,164 WARN  [com.arjuna.ats.arjuna] (Transaction Reaper) ARJUNA012117: TransactionReaper::check timeout for TX 0:ffffc0a80067:6a0b2067:53cb6ff0:18 in state  RUN
15:37:03,166 WARN  [com.arjuna.ats.arjuna] (Transaction Reaper Worker 0) ARJUNA012095: Abort of action id 0:ffffc0a80067:6a0b2067:53cb6ff0:18 invoked while multiple threads active within it.
15:37:03,166 WARN  [com.arjuna.ats.arjuna] (Transaction Reaper Worker 0) ARJUNA012108: CheckedAction::check - atomic action 0:ffffc0a80067:6a0b2067:53cb6ff0:18 aborting with 1 threads active!
15:37:03,166 WARN  [com.arjuna.ats.arjuna] (Transaction Reaper Worker 0) ARJUNA012121: TransactionReaper::doCancellations worker Thread[Transaction Reaper Worker 0,5,main] successfully canceled TX 0:ffffc0a80067:6a0b2067:53cb6ff0:18
15:37:13,165 INFO  [stdout] (EJB default - 2) ======wake up=====
15:37:13,165 INFO  [stdout] (EJB default - 2) do someting else
15:37:13,165 INFO  [stdout] (EJB default - 2) end
15:37:13,166 ERROR [org.jboss.as.ejb3.invocation] (EJB default - 2) JBAS014134: 对组件 TranTimeoutBean 的方法 public abstract java.lang.String org.jboss.as.quickstarts.ejb.remote.stateless.RemoteTranTimeout.sayHello(java.lang.String) 的 EJB 调用失败: javax.ejb.EJBTransactionRolledbackException: Transaction rolled back
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.handleEndTransactionException(CMTTxInterceptor.java:138) [jboss-as-ejb3-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.endTransaction(CMTTxInterceptor.java:118) [jboss-as-ejb3-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInOurTx(CMTTxInterceptor.java:276) [jboss-as-ejb3-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.required(CMTTxInterceptor.java:339) [jboss-as-ejb3-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.processInvocation(CMTTxInterceptor.java:238) [jboss-as-ejb3-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ejb3.remote.EJBRemoteTransactionPropagatingInterceptor.processInvocation(EJBRemoteTransactionPropagatingInterceptor.java:79) [jboss-as-ejb3-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ejb3.component.interceptors.CurrentInvocationContextInterceptor.processInvocation(CurrentInvocationContextInterceptor.java:41) [jboss-as-ejb3-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ejb3.component.interceptors.ShutDownInterceptorFactory$1.processInvocation(ShutDownInterceptorFactory.java:64) [jboss-as-ejb3-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ejb3.component.interceptors.LoggingInterceptor.processInvocation(LoggingInterceptor.java:59) [jboss-as-ejb3-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ee.component.NamespaceContextInterceptor.processInvocation(NamespaceContextInterceptor.java:50) [jboss-as-ee-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ejb3.component.interceptors.AdditionalSetupInterceptor.processInvocation(AdditionalSetupInterceptor.java:55) [jboss-as-ejb3-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ee.component.TCCLInterceptor.processInvocation(TCCLInterceptor.java:45) [jboss-as-ee-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:61) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ee.component.ViewService$View.invoke(ViewService.java:165) [jboss-as-ee-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.as.ejb3.remote.protocol.versionone.MethodInvocationMessageHandler.invokeMethod(MethodInvocationMessageHandler.java:329) [jboss-as-ejb3-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.as.ejb3.remote.protocol.versionone.MethodInvocationMessageHandler.access$100(MethodInvocationMessageHandler.java:70) [jboss-as-ejb3-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.as.ejb3.remote.protocol.versionone.MethodInvocationMessageHandler$1.run(MethodInvocationMessageHandler.java:203) [jboss-as-ejb3-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) [rt.jar:1.7.0_55]
    at java.util.concurrent.FutureTask.run(FutureTask.java:262) [rt.jar:1.7.0_55]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [rt.jar:1.7.0_55]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_55]
    at java.lang.Thread.run(Thread.java:745) [rt.jar:1.7.0_55]
    at org.jboss.threads.JBossThread.run(JBossThread.java:122)
Caused by: javax.transaction.RollbackException: JBAS014585: 事务 'TransactionImple < ac, BasicAction: 0:ffffc0a80067:6a0b2067:53cb6ff0:18 status: ActionStatus.ABORTED >' 已被回滚
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.endTransaction(CMTTxInterceptor.java:99) [jboss-as-ejb3-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    ... 29 more

3. 注意,在standalone.xml中还有一个全局的事务超时时间,默认是300秒,即5分钟。
所有应用的事务调用时长都不能超过此设定时间。
<subsystem xmlns="urn:jboss:domain:transactions:1.4">
            <core-environment>
                <process-id>
                    <uuid/>
                </process-id>
            </core-environment>
            <recovery-environment socket-binding="txn-recovery-environment" status-socket-binding="txn-status-manager"/>
            <coordinator-environment default-timeout="300"/>
        </subsystem>

4. 除了写在jboss-ejb3.xml中,还可以用批注的方式写在EJB的方法上

import javax.ejb.Remote;
import javax.ejb.Stateless;
import java.util.concurrent.TimeUnit;
import org.jboss.ejb3.annotation.TransactionTimeout;

@Stateless
@Remote(RemoteTranTimeout.class)
public class TranTimeoutBean implements RemoteTranTimeout {

    @Override
    @TransactionTimeout(value = 10, unit = TimeUnit.SECONDS)
    public String sayHello(String name) {
        System.out.println("======sleeping=====");
        long transBMillis = System.currentTimeMillis();
        long transEMillis = 0;
        while (true) {
            transEMillis = System.currentTimeMillis();
            if ((transEMillis - transBMillis) / 1000 >= 15) {
                break;
            }
        }
        System.out.println("======wake up=====");
        System.out.println("do someting else");
        System.out.println("end");
        return "Hello, " + name;
    }
}


参考文献:
1. http://stackoverflow.com/questions/13015280/jboss-7-1-ejb-2-1-custom-transaction-timeout-configuration
2. https://docs.jboss.org/author/display/AS7/EJB+3+Reference+Guide#EJB3ReferenceGuide-TransactionTimeout
3. https://access.redhat.com/solutions/90553

没有评论: