我觉得这可以说是 rpc 最需要的一个功能。
因为他的实际意义非常重大,就是可以任意时间发布应用,而对业务无损。
以前发布应用都要等到半夜,很累也容易出问题。
结合 java 的 ShutdownHook 以及 linux 的 kill PID 来达到这个目的。
停止时,先标记为不接收新请求,新请求过来时直接报错,让客户端重试其它机器。
然后,检测线程池中的线程是否正在运行,如果有,等待所有线程执行完成,除非超时,则强制关闭。
停止时,不再发起新的调用请求,所有新的调用在客户端即报错。
然后,检测有没有请求的响应还没有返回,等待响应返回,除非超时,则强制关闭。
当然这里也结合了服务的状态管理和资源管理。
一个负责控制状态,一个负责关闭资源。
/**
* (1)设置 status 状态为等待关闭
* (2)查看是否 {@link InvokeManager#remainsRequest()} 是否包含请求
* (3)超时检测-可以不添加,如果难以关闭成功,直接强制关闭即可。
* (4)关闭所有线程池资源信息
* (5)设置状态为成功关闭
*/
@Override
protected void doHook() {
// 设置状态为等待关闭
statusManager.status(StatusEnum.WAIT_SHUTDOWN.code());
LOG.info("[Shutdown] set status to wait for shutdown.");
// 循环等待当前执行的请求执行完成
while (invokeManager.remainsRequest()) {
LOG.info("[Shutdown] still remains request, wait for a while.");
Waits.waits(10);
}
// 销毁所有资源
LOG.info("[Shutdown] resourceManager start destroy all resources.");
this.resourceManager.destroyAll();
LOG.info("[Shutdown] resourceManager finish destroy all resources.");
// 设置状态为关闭成功
statusManager.status(StatusEnum.SHUTDOWN_SUCCESS.code());
LOG.info("[Shutdown] set status to shutdown success.");
}
钩子函数的使用也比较简单,如下:
/**
* 添加 rpc shutdown hook
* @param rpcShutdownHook 钩子函数实现
* @since 0.1.3
*/
public static void rpcShutdownHook(final RpcShutdownHook rpcShutdownHook) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
rpcShutdownHook.hook();
}
});
}
正常启动
正常启动
正常启动
- 客户端
[INFO] [2019-11-01 23:10:51.709] [Thread-1] [c.g.h.r.c.s.h.AbstractShutdownHook.hook] - [Shutdown Hook] start
[INFO] [2019-11-01 23:10:51.710] [Thread-1] [c.g.h.r.c.s.h.DefaultShutdownHook.doHook] - [Shutdown] set status to wait for shutdown.
[INFO] [2019-11-01 23:10:51.711] [Thread-1] [c.g.h.r.c.s.h.DefaultShutdownHook.doHook] - [Shutdown] resourceManager start destroy all resources.
[INFO] [2019-11-01 23:10:51.711] [Thread-1] [c.g.h.r.c.s.r.i.DefaultResourceManager.destroyAll] - [Resource] destroyableList.size(): 1
[INFO] [2019-11-01 23:10:51.712] [Thread-1] [c.g.h.r.c.s.r.i.DefaultResourceManager.destroyAll] - [Resource] destroy destroyable: com.github.houbb.rpc.common.remote.netty.impl.DefaultNettyClient@10736d9
[INFO] [2019-11-01 23:10:51.713] [Thread-1] [c.g.h.r.c.r.n.i.DefaultNettyClient.destroy] - [Netty Client] start close future.
[INFO] [2019-11-01 23:10:51.715] [Thread-1] [c.g.h.r.c.r.n.i.DefaultNettyClient.destroy] - [Netty Client] 关闭完成
[INFO] [2019-11-01 23:10:51.727] [Thread-1] [c.g.h.r.c.r.n.i.DefaultNettyClient.destroy] - [Netty Client] 线程池关闭完成
[INFO] [2019-11-01 23:10:51.728] [Thread-1] [c.g.h.r.c.s.r.i.DefaultResourceManager.destroyAll] - [Resource] clear destroyableList
[INFO] [2019-11-01 23:10:51.729] [Thread-1] [c.g.h.r.c.s.h.DefaultShutdownHook.doHook] - [Shutdown] resourceManager finish destroy all resources.
[INFO] [2019-11-01 23:10:51.729] [Thread-1] [c.g.h.r.c.s.h.DefaultShutdownHook.doHook] - [Shutdown] set status to shutdown success.
[INFO] [2019-11-01 23:10:51.730] [Thread-1] [c.g.h.r.c.s.h.AbstractShutdownHook.hook] - [Shutdown Hook] end
- 服务端
[INFO] [2019-11-01 23:11:11.433] [Thread-1] [c.g.h.r.c.s.h.AbstractShutdownHook.hook] - [Shutdown Hook] start
[INFO] [2019-11-01 23:11:11.434] [Thread-1] [c.g.h.r.c.s.h.DefaultShutdownHook.doHook] - [Shutdown] set status to wait for shutdown.
[INFO] [2019-11-01 23:11:11.435] [Thread-1] [c.g.h.r.c.s.h.DefaultShutdownHook.doHook] - [Shutdown] resourceManager start destroy all resources.
[INFO] [2019-11-01 23:11:11.436] [Thread-1] [c.g.h.r.c.s.r.i.DefaultResourceManager.destroyAll] - [Resource] destroyableList.size(): 2
[INFO] [2019-11-01 23:11:11.437] [Thread-1] [c.g.h.r.c.s.r.i.DefaultResourceManager.destroyAll] - [Resource] destroy destroyable: com.github.houbb.rpc.common.remote.netty.impl.DefaultNettyServer@2c41f4
[INFO] [2019-11-01 23:11:11.437] [Thread-1] [c.g.h.r.c.r.n.i.DefaultNettyServer.destroy] - [Netty Server] 开始关闭
[INFO] [2019-11-01 23:11:11.439] [Thread-1] [c.g.h.r.c.r.n.i.DefaultNettyServer.destroy] - [Netty Server] 完成关闭
[INFO] [2019-11-01 23:11:11.469] [Thread-1] [c.g.h.r.c.s.r.i.DefaultResourceManager.destroyAll] - [Resource] destroy destroyable: com.github.houbb.rpc.common.remote.netty.impl.DefaultNettyClient@12cbfa
[INFO] [2019-11-01 23:11:11.470] [Thread-1] [c.g.h.r.c.r.n.i.DefaultNettyClient.destroy] - [Netty Client] start close future.
[INFO] [2019-11-01 23:11:11.471] [Thread-1] [c.g.h.r.c.r.n.i.DefaultNettyClient.destroy] - [Netty Client] 关闭完成
[INFO] [2019-11-01 23:11:11.481] [Thread-1] [c.g.h.r.c.r.n.i.DefaultNettyClient.destroy] - [Netty Client] 线程池关闭完成
[INFO] [2019-11-01 23:11:11.481] [Thread-1] [c.g.h.r.c.s.r.i.DefaultResourceManager.destroyAll] - [Resource] clear destroyableList
[INFO] [2019-11-01 23:11:11.481] [Thread-1] [c.g.h.r.c.s.h.DefaultShutdownHook.doHook] - [Shutdown] resourceManager finish destroy all resources.
[INFO] [2019-11-01 23:11:11.482] [Thread-1] [c.g.h.r.c.s.h.DefaultShutdownHook.doHook] - [Shutdown] set status to shutdown success.
[INFO] [2019-11-01 23:11:11.482] [Thread-1] [c.g.h.r.c.s.h.AbstractShutdownHook.hook] - [Shutdown Hook] end