Netty优雅退出机制和原理

1. 引言

2. 进程的优雅退出

2.1 Kill -9 PID带来的问题

在Linux上一般会经过kill -9 pid的方式强制将某个进程杀掉,这种方式简单高效,所以不少程序的中止脚本常常会选择使用kill -9 pid的方式。数据库

不管是Linux的Kill -9 pid仍是windows的taskkill /f /pid强制进程退出,都会带来一些反作用:对应用软件而言其效果等同于忽然掉电,可能会致使以下一些问题:windows

  1. 缓存中的数据还没有持久化到磁盘中,致使数据丢失;
  2. 正在进行文件的write操做,没有更新完成,忽然退出,致使文件损坏;
  3. 线程的消息队列中尚有接收到的请求消息还没来得及处理,致使请求消息丢失;
  4. 数据库操做已经完成,例如帐户余额更新,准备返回应答消息给客户端时,消息尚在通讯线程的发送队列5. 中排队等待发送,进程强制退出致使应答消息没有返回给客户端,客户端发起超时重试,会带来重复更新问题;
  5. 其它问题等...

2.2 JAVA优雅退出

Java的优雅停机一般经过注册JDK的ShutdownHook来实现,当系统接收到退出指令后,首先标记系统处于退出状态,再也不接收新的消息,而后将积压的消息处理完,最后调用资源回收接口将资源销毁,最后各线程退出执行。缓存

一般优雅退出须要有超时控制机制,例如30S,若是到达超时时间仍然没有完成退出前的资源回收等操做,则由停机脚本直接调用kill -9 pid,强制退出。异步

3. 如何实现Netty优雅退出

要实现Netty的优雅退出,首先须要了解通用Java进程的优雅退出如何实现。下面咱们先讲解下优雅退出的实现原理,并结合实际代码进行讲解。最后看下如何实现Netty的优雅退出。ide

信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求能够说是同样的,它oop

3.1 信号简介

是进程间一种异步通讯的机制。以Linux的kill命令为例,kill -s SIGKILL pid (即kill -9 pid) 当即杀死指定pid的进程,SIGKILL就是发送给pid进程的信号。测试

信号具备平台相关性,Linux平台支持的一些终止进程信号以下所示:操作系统

输入图片说明

Windows平台存在一些差别,它的一些信号举例以下:SIGINT(Ctrl+C中断)、SIGILL、SIGTERM (kill发出的软件终止)、SIGBREAK (Ctrl+Break中断)。线程

信号选择:为了避免干扰正常信号的运做,又能模拟Java异步通知,在Linux上咱们须要先选定一种特殊的信号。经过查看信号列表上的描述,发现 SIGUSR1 和 SIGUSR2 是容许用户自定义的信号,咱们能够选择SIGUSR2,为了测试方便,在Windows上咱们能够选择SIGINT。code

3.2 Java程序的优雅退出

首先看下通用的Java进程优雅退出的流程图:

输入图片说明

  • 第一步,应用进程启动的时候,初始化Signal实例,它的代码示例以下:
Signal sig = new Signal(getOSSignalType());
  • 第二步,根据操做系统的名称来获取对应的信号名称,代码以下:
private String getOSSignalType()
{
       return System.getProperties().getProperty("os.name").
		 toLowerCase().startsWith("win") ? "INT" : "USR2";
}

判断是不是windows操做系统,若是是则选择SIGINT,接收Ctrl+C中断的指令;不然选择USR2信号,接收SIGUSR2(等价于kill -12 pid)指令。

  • 第三步,将实例化以后的SignalHandler注册到JDK的Signal,一旦Java进程接收到kill -12 或者 Ctrl+C则回调handle接口,代码示例以下:
Signal.handle(sig, shutdownHandler);

其中shutdownHandler实现了SignalHandler接口的handle(Signal sgin)方法,代码示例以下:

  • 第四步,在接收到信号回调的handle接口中,初始化JDK的ShutdownHook线程,并将其注册到Runtime中,示例代码以下:
private void invokeShutdownHook()
{
	Thread t = new Thread(new ShutdownHook(), "ShutdownHook-Thread");
	Runtime.getRuntime().addShutdownHook(t);
}
  • 第五步,接收到进程退出信号后,在回调的handle接口中执行虚拟机的退出操做,示例代码以下:
Runtime.getRuntime().exit(0);

虚拟机退出时,底层会自动检测用户是否注册了ShutdownHook任务,若是有,则会自动将ShutdownHook线程拉起,执行它的Run方法,用户只须要在ShutdownHook中执行资源释放操做便可,示例代码以下:

class ShutdownHook implements Runnable
{
	@Override
	public void run() {
		System.out.println("ShutdownHook execute start...");
		System.out.print("Netty NioEventLoopGroup shutdownGracefully...");
		try {
			TimeUnit.SECONDS.sleep(10);//模拟应用进程退出前的处理操做
		} catch (InterruptedException e) {
				e.printStackTrace();
		}
		System.out.println("ShutdownHook execute end...");
	System.out.println("Sytem shutdown over, the cost time is 10000MS");
		}
}

下面咱们在Windows环境中对通用的Java优雅退出程序进行测试,打开CMD控制台,拉起待测试程序,以下所示:

启动进程:

查看线程信息,发现注册的ShutdownHook线程没有启动,符合预期:

相关文章
相关标签/搜索