How To Shutdown Java Service Gracefully [Tutorial]
FuqiangWang
I will just talk about in my way, I don’t care your tricky ways like kill
something.
First of all, Let’s define Service
, U can refer to google guava’s one, but Let’s make it simple, A Service can be started and stopped, sometimes, we would like to see the status of a service, so a running
status will be exposed too.
So A simple service looks like:
class MyService{
private volatile boolean running = false;
public void start(){
....
running = true;
while(running){
...
}
}
public boolean isRunning() {return running;}
public void shutdown(){
= false;
running }
}
If we run this service in main thread, we don’t have a chance to call shutdown(), so we expose this Service via jmx, say , with springframework:
@ManagedResource(...)
class MyService{
...
@ManagedOperation
public void shutdown(){
= false;
running }
}
and configure spring to expose it:
<?xml version="1.0" encoding="UTF-8"?>
beans xmlns="http://www.springframework.org/schema/beans"
< xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
context:mbean-export/>
<
beans> </
then, when we would like to shut it down, we call the shutdown() method via jmx.
To make things simple, we usually will give a stop.sh under bin directory for ops, such a stop.sh can call this shutdown() method via jmx with the help of attach API and management-agent.jar, as long as we wrap these functions into a helper main class:
import com.sun.tools.attach.AgentInitializationException;
import com.sun.tools.attach.AgentLoadException;
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.File;
import java.io.IOException;
/**
* A Tool you can use to stop your service process gracefully via JMX locally and safely.
*
* Shutdown needs tools.jar in classpath, so to run this class, assign the path of tools.jar in your system in the shell script, it's a must prerequisite!
*
* @since 2014-10-24
*/
public class Shutdown {
public static final String LOCAL_CONNECTOR_ADDRESS_URL = "com.sun.management.jmxremote.localConnectorAddress";
/**
* current process's pid
*/
private String pid;
/**
* the managed bean's name of the top service that we will stop
*/
private String mbeanName;
/**
* the operation name of the managed bean, usually named "stop", "shutdown", "destroy" without any parameters.
*/
private String mbeanMethodName;
protected JMXServiceURL getConnectorAddressAsPerPid(String pid) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
= VirtualMachine.attach(pid);
VirtualMachine vm String connectorAddress = vm.getAgentProperties().getProperty(LOCAL_CONNECTOR_ADDRESS_URL);
if (connectorAddress == null) {
String agent = vm.getSystemProperties().getProperty("java.home") + File.separator + "lib" + File.separator + "management-agent.jar";
.loadAgent(agent);
vm= vm.getAgentProperties().getProperty(LOCAL_CONNECTOR_ADDRESS_URL);
connectorAddress }
return new JMXServiceURL(connectorAddress);
}
public Object execute() throws Throwable {
validate(pid, "pid");
validate(mbeanName, "mbeanName");
validate(mbeanMethodName, "mbeanMethodName");
JMXConnector jmxConnector = JMXConnectorFactory.newJMXConnector(getConnectorAddressAsPerPid(getPid()), null);
.connect();
jmxConnectortry {
MBeanServerConnection connection = jmxConnector.getMBeanServerConnection();
return connection.invoke(ObjectName.getInstance(getMbeanName()), getMbeanMethodName(), null, null);
} finally {
.close();
jmxConnector}
}
protected void validate(String property, String propertyName) {
if (property == null || property.trim().isEmpty())
throw new IllegalStateException("[" + propertyName + "] must be set");
}
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
public String getMbeanName() {
return mbeanName;
}
public void setMbeanName(String mbeanName) {
this.mbeanName = mbeanName;
}
public String getMbeanMethodName() {
return mbeanMethodName;
}
public void setMbeanMethodName(String mbeanMethodName) {
this.mbeanMethodName = mbeanMethodName;
}
/**
* Shutdown shutdown = new Shutdown();
* shutdown.setPid("7198");
* shutdown.setMbeanName("com.sun.management:type=DiagnosticCommand");
* shutdown.setMbeanMethodName("vmSystemProperties");
* System.out.println(shutdown.execute());
*/
public static void main(String[] args) throws Throwable {
if (args.length != 3) {
throw new Exception("usage: java ... Shutdown [pid] [mbean name] [mbean operation]");
}
= new Shutdown();
Shutdown shutdown .setPid(args[0]);
shutdown.setMbeanName(args[1]);
shutdown.setMbeanMethodName(args[2]);
shutdown.execute();
shutdown}
}
then in the stop.sh, you can simple write:
#! /usr/bin/env bash
java -cp tools.jar:... com.wacai.csw.scheduler.controls.Shutdown [pid] [service mbean name] [shutdown method]
since the tools.jar is platform dependent, just point it to the location under your platform in the shell.
OK, now everything works perfectly and seamlessly.
「为AI疯狂」星球上,扶墙老师正在和朋友们讨论有趣的AI话题,你要不要⼀起来呀?^-^
这里
- 不但有及时新鲜的AI资讯和深度探讨
- 还分享AI工具、产品方法和商业机会
- 更有体系化精品付费内容等着你,加入星球(https://t.zsxq.com/0dI3ZA0sL) 即可免费领取。(加入之后一定记得看置顶消息呀!)

存量的时代,省钱就是赚钱。
在增量的时代,省钱其实是亏钱。
避坑儿是省钱的一种形式,更是真正聪明人的选择!
弯路虽然也是路,但还是能少走就少走,背后都是高昂的试错成本。
订阅「福报」,少踩坑,少走弯路,多走一步,就是不一样的胜率!
