优雅地处理 WildFly 组件关闭
Handle WildFly component shutdown gracefully
如何在部署在 wildfly 上的 war 容器中实现能够正常关闭的服务?我有以下示例代码:
SomeService.java:
@Stateless
public class SomeService {
public void doThing() {
// this will fail once the component is shut down.
System.out.println("Doing some work...");
}
}
SomeWorker.java:
@Singleton
@Startup
public class SomeWorker {
@Inject
private SomeService service;
@Resource
private TimerService timerService;
@PostConstruct
public void doWork() {
System.out.println("Started startup bean...");
timerService.createSingleActionTimer(10, new TimerConfig());
}
@Timeout
public void doLater() {
while (!Thread.interrupted()) {
service.doThing();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
System.out.println("graceful shutdown");
}
}
此代码只是开始循环调用 EJB 的方法。如果这是一个纯 Java SE 应用程序(用相应的替换替换 EJB 注释),只需捕获 InterruptedException
就足以定期正常关闭(不会用 kill -9
杀死 VM,但这是无论如何都超出了我的范围)。
但是在这种情况下,我不知道如何检测正在取消部署的模块。如果我取消部署 war 文件(在我的例子中,通过将 test.war.deployed
文件重命名为 test.war.undeployed
),它只是开始最终在 EJB 访问上抛出 ComponentIsStoppedException
:
14:14:17,250 INFO [stdout] (EJB default - 1) Doing some work...
14:14:18,252 INFO [stdout] (EJB default - 1) Doing some work...
14:14:19,253 INFO [stdout] (EJB default - 1) Doing some work...
14:14:19,808 INFO [org.jboss.as.server.deployment.scanner] (DeploymentScanner-threads - 2) WFLYDS0033: Deployment test.war was previously undeployed by this scanner but has been redeployed by another management tool. Marker file D:\<path>\wildfly\standalone\deployments\test.war.undeployed is being removed to record this fact.
14:14:19,831 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 67) WFLYUT0022: Unregistered web context: '/test' from server 'default-server'
14:14:20,256 ERROR [org.jboss.as.ejb3.timer] (EJB default - 1) WFLYEJB0020: Error invoking timeout for timer: [id=4de4a666-5a6e-4830-aa89-21d2040d04f0 timedObjectId=test.test.SomeWorker auto-timer?:false persistent?:true timerService=org.jboss.as.ejb3.timerservice.TimerServiceImpl@526b0669 initialExpiration=Thu Oct 18 14:14:09 CEST 2018 intervalDuration(in milli sec)=0 nextExpiration=null timerState=IN_TIMEOUT info=null]: javax.ejb.EJBException: org.jboss.as.ee.component.ComponentIsStoppedException: WFLYEE0043: Component is stopped
...
Caused by: org.jboss.as.ee.component.ComponentIsStoppedException: WFLYEE0043: Component is stopped
at org.jboss.as.ee.component.BasicComponent.waitForComponentStart(BasicComponent.java:110)
at org.jboss.as.ee.component.ViewService$View.invoke(ViewService.java:194)
at org.jboss.as.ee.component.ViewDescription.processInvocation(ViewDescription.java:185)
at org.jboss.as.ee.component.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:81)
at test.SomeService$$$view1.doThing(Unknown Source)
...
at test.SomeService$Proxy$_$$_Weld$EnterpriseProxy$.doThing(Unknown Source)
at test.SomeWorker.doLater(SomeWorker.java:31)
...
检查正在取消部署的模块是否正在通过 @PreDestroy
注释方法:
@PreDestroy
public void doShutdown() {
System.out.println("Shutting down. Please wait...");
for (Timer timer : timerService.getTimers() {
timer.cancel();
}
}
如何在部署在 wildfly 上的 war 容器中实现能够正常关闭的服务?我有以下示例代码:
SomeService.java:
@Stateless
public class SomeService {
public void doThing() {
// this will fail once the component is shut down.
System.out.println("Doing some work...");
}
}
SomeWorker.java:
@Singleton
@Startup
public class SomeWorker {
@Inject
private SomeService service;
@Resource
private TimerService timerService;
@PostConstruct
public void doWork() {
System.out.println("Started startup bean...");
timerService.createSingleActionTimer(10, new TimerConfig());
}
@Timeout
public void doLater() {
while (!Thread.interrupted()) {
service.doThing();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
System.out.println("graceful shutdown");
}
}
此代码只是开始循环调用 EJB 的方法。如果这是一个纯 Java SE 应用程序(用相应的替换替换 EJB 注释),只需捕获 InterruptedException
就足以定期正常关闭(不会用 kill -9
杀死 VM,但这是无论如何都超出了我的范围)。
但是在这种情况下,我不知道如何检测正在取消部署的模块。如果我取消部署 war 文件(在我的例子中,通过将 test.war.deployed
文件重命名为 test.war.undeployed
),它只是开始最终在 EJB 访问上抛出 ComponentIsStoppedException
:
14:14:17,250 INFO [stdout] (EJB default - 1) Doing some work...
14:14:18,252 INFO [stdout] (EJB default - 1) Doing some work...
14:14:19,253 INFO [stdout] (EJB default - 1) Doing some work...
14:14:19,808 INFO [org.jboss.as.server.deployment.scanner] (DeploymentScanner-threads - 2) WFLYDS0033: Deployment test.war was previously undeployed by this scanner but has been redeployed by another management tool. Marker file D:\<path>\wildfly\standalone\deployments\test.war.undeployed is being removed to record this fact.
14:14:19,831 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 67) WFLYUT0022: Unregistered web context: '/test' from server 'default-server'
14:14:20,256 ERROR [org.jboss.as.ejb3.timer] (EJB default - 1) WFLYEJB0020: Error invoking timeout for timer: [id=4de4a666-5a6e-4830-aa89-21d2040d04f0 timedObjectId=test.test.SomeWorker auto-timer?:false persistent?:true timerService=org.jboss.as.ejb3.timerservice.TimerServiceImpl@526b0669 initialExpiration=Thu Oct 18 14:14:09 CEST 2018 intervalDuration(in milli sec)=0 nextExpiration=null timerState=IN_TIMEOUT info=null]: javax.ejb.EJBException: org.jboss.as.ee.component.ComponentIsStoppedException: WFLYEE0043: Component is stopped
...
Caused by: org.jboss.as.ee.component.ComponentIsStoppedException: WFLYEE0043: Component is stopped
at org.jboss.as.ee.component.BasicComponent.waitForComponentStart(BasicComponent.java:110)
at org.jboss.as.ee.component.ViewService$View.invoke(ViewService.java:194)
at org.jboss.as.ee.component.ViewDescription.processInvocation(ViewDescription.java:185)
at org.jboss.as.ee.component.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:81)
at test.SomeService$$$view1.doThing(Unknown Source)
...
at test.SomeService$Proxy$_$$_Weld$EnterpriseProxy$.doThing(Unknown Source)
at test.SomeWorker.doLater(SomeWorker.java:31)
...
检查正在取消部署的模块是否正在通过 @PreDestroy
注释方法:
@PreDestroy
public void doShutdown() {
System.out.println("Shutting down. Please wait...");
for (Timer timer : timerService.getTimers() {
timer.cancel();
}
}