在dubbo的用户指南里面提到优雅停机,配置以下能设置优雅停机超时时间。
<dubbo:application ...>
<dubbo:parameter key="shutdown.timeout" value="60000" />
</dubbo:application>
问题是:
1) 这个参数是不是假设一个线程或一个事务没执行完,执行kill pid 的最大等待超时时间?
2) 如果duubo与nutz集成,如何配置这个参数?
在dubbo的用户指南里面提到优雅停机,配置以下能设置优雅停机超时时间。
<dubbo:application ...>
<dubbo:parameter key="shutdown.timeout" value="60000" />
</dubbo:application>
问题是:
1) 这个参数是不是假设一个线程或一个事务没执行完,执行kill pid 的最大等待超时时间?
2) 如果duubo与nutz集成,如何配置这个参数?
没有使用插件,nutz用的是nutz-1.b.51 ,dubbo自己集成。集成用到您之前提到的nutz和dubbo集成project类似。
https://nutz.cn/yvr/t/q5o92h13niha9o02vr6jis1lsc
但是这个parameter配置比较特别,暂时没想到配置方式。
shutdown.timeout 在ProtocolConfig配置不起作用。10秒后强制关闭JVM。
配在ApplicationConfig 则会报错,没有这个属性可配。
以下是配置示例:
provider.application : {
type : 'com.alibaba.dubbo.config.ApplicationConfig',
fields : {
name : 'P2P',
logger: 'log4j'
}
},
protocol : {
type : 'com.alibaba.dubbo.config.ProtocolConfig',
fields : {
name : 'dubbo',
port:'8010',
contextpath:'FrameServer',
accesslog:'true',
parameters:{"shutdown.timeout" :"60000"}
},
},
reference :{
type :'com.alibaba.dubbo.config.ReferenceConfig',
singleton : false,
fields :{
cluster:"failfast",
loadbalance:"leastactive",
timeout:30000,
application : {
refer : 'provider.application'
},
registry : {
refer : 'registry',
}
}
},
http://dubbo.io/Graceful+Shutdown-zh.htm
优雅停机
(+) (#)
Dubbo是通过JDK的ShutdownHook来完成优雅停机的,所以如果用户使用"kill -9 PID"等强制关闭指令,是不会执行优雅停机的,只有通过"kill PID"时,才会执行。
原理:
服务提供方
停止时,先标记为不接收新请求,新请求过来时直接报错,让客户端重试其它机器。
然后,检测线程池中的线程是否正在运行,如果有,等待所有线程执行完成,除非超时,则强制关闭。
服务消费方
停止时,不再发起新的调用请求,所有新的调用在客户端即报错。
然后,检测有没有请求的响应还没有返回,等待响应返回,除非超时,则强制关闭。
设置优雅停机超时时间,缺省超时时间是10秒:(超时则强制关闭)
<dubbo:application ...>
<dubbo:parameter key="shutdown.timeout" value="60000" />
</dubbo:application>
如果ShutdownHook不能生效,可以自行调用:
ProtocolConfig.destroyAll();
dubbo-spring 调这个方法来解析<dubbo*>标签。
com.alibaba.dubbo.config.spring.schema.DubboBeanDefinitionParser.parse()
解析出parameter后调用。
其中beanDefinition.getPropertyValues().addPropertyValue("parameters", parameters);
但是nutz应该怎么处理呢?
ApplicationConfig 没有paramters。看了属性和方法也没有可以设置的。
ApplicationConfig继承AbstractConfig,AbstractConfig也没有合适的配置。
在com.alibaba.dubbo.common.Constants 定义:
public static final String SHUTDOWN_TIMEOUT_KEY = "shutdown.timeout";
public static final int DEFAULT_SHUTDOWN_TIMEOUT = 1000 * 60 * 15;
( ps: 我很好奇这里的默认时间并不是10秒,实际优雅停机超时时间确实是10秒。)
在com.alibaba.dubbo.remoting.transport.AbstractClient 中使用到:
public AbstractClient(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
send_reconnect = url.getParameter(Constants.SEND_RECONNECT_KEY, false);
shutdown_timeout = url.getParameter(Constants.SHUTDOWN_TIMEOUT_KEY, Constants.DEFAULT_SHUTDOWN_TIMEOUT);
//默认重连间隔2s,1800表示1小时warning一次.
reconnect_warning_period = url.getParameter("reconnect.waring.period", 1800);
try {
doOpen();
} catch (Throwable t) {
close();
throw new RemotingException(url.toInetSocketAddress(), null,
"Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress()
+ " connect to the server " + getRemoteAddress() + ", cause: " + t.getMessage(), t);
}
try {
// connect.
connect();
if (logger.isInfoEnabled()) {
logger.info("Start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress() + " connect to the server " + getRemoteAddress());
}
} catch (RemotingException t) {
if (url.getParameter(Constants.CHECK_KEY, true)) {
close();
throw t;
} else {
logger.warn("Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress()
+ " connect to the server " + getRemoteAddress() + " (check == false, ignore and retry later!), cause: " + t.getMessage(), t);
}
} catch (Throwable t){
close();
throw new RemotingException(url.toInetSocketAddress(), null,
"Failed to start " + getClass().getSimpleName() + " " + NetUtils.getLocalAddress()
+ " connect to the server " + getRemoteAddress() + ", cause: " + t.getMessage(), t);
}
executor = (ExecutorService) ExtensionLoader.getExtensionLoader(DataStore.class)
.getDefaultExtension().get(Constants.CONSUMER_SIDE, Integer.toString(url.getPort()));
ExtensionLoader.getExtensionLoader(DataStore.class)
.getDefaultExtension().remove(Constants.CONSUMER_SIDE, Integer.toString(url.getPort()));
}
查看源码,该构造方法有三处调用。
com.alibaba.dubbo.remoting.transport.AbstractClient.AbstractClient(URL, ChannelHandler)
--> com.alibaba.dubbo.remoting.transport.grizzly.GrizzlyClient.GrizzlyClient(URL, ChannelHandler)
--> com.alibaba.dubbo.remoting.transport.mina.MinaClient.MinaClient(URL, ChannelHandler)
--> com.alibaba.dubbo.remoting.transport.netty.NettyClient.NettyClient(URL, ChannelHandler)
我在这个构造方法打了断点,正常启动--》kill pid。
整个过程并没有进到此断点,是否证明这个参数根本没用到?
那么跳出这个配置来考虑,还有别的方法可以设置优雅停止超时时间吗?
最重要我是想在我执行停机的时候,保证我的数据库事务或者线程执行结束。这个时间起码要需要60秒才合适,10秒确实太短。
AbstractConfig中这段代码设置了优雅停机hook。但是没有地方超时设置,而且证明了这个destroyAll是释放dubbo资源,向zookeeper和客户端推送下机通知。一般只需要1,2秒。
。。。
综上,还是没有找到可以设置优雅超时的方法?
static {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() {
if (logger.isInfoEnabled()) {
logger.info("Run shutdown hook now.");
}
ProtocolConfig.destroyAll();
}
}, "DubboShutdownHook"));
}