Netty中为什么要优雅关闭线程池?以及异步关闭通道的时候什么时候会得到异步结果关闭通道?
为什么BossGroup和WorkerGroup线程池最终需要优雅的关闭?
先看喜爱具体的代码 如下图:
可以看到我们最终会给两个线程池执行一个优雅关闭方法shutdownGracefully,那么为什么要优雅的关闭呢?优雅的关闭的时候是怎么个优雅法,它内部是怎么执行?都进行了哪些相关操作?
所谓的优雅的关闭线程池就是说,如果线程池中的任何一个线程的任务队列中还有任务,那么这个时候我就不会关闭线程池,只有当线程池中的所有线程的任务队列的任务都执行完毕了,那么我才会关闭线程池。其实也就是说,我现在启动了一个服务器,然后有很多的客户端都来连接我的服务器,会牵涉到很多的客户端任务,可能有10000个客户端,最终通过线程池中的100个线程进行执行,会创建10000个Channel,一个Channel对应一个客户端连接。
如果有任何一个客户端还存在内部任务,那么这个时候我们就不会关闭线程池,这个时候不优雅。只有当所有客户端的任务都执行完毕了,也就是我们的线程池的所有线程的任务队列里面都没有任务了,这个时候我们才会去关闭线程池,这个时候关闭的时候比较优雅,因此叫做优雅的关闭线程池。
那么优雅的关闭线程池有什么好处呢?
- 保证数据一致性。比如说如果没有优雅的关闭,可能你这个时候程序里面正在执行一个事务,正在更新数据库的多张表的信息,如果任务还没执行完的时候,线程池强行关闭了,那么我们程序里面的信息就不能成功的更新到我们的mysql数据库表中,这个时候就会出现数据库表信息不一致问题了。而如果我们优雅的关闭线程池,等到所有的任务都执行完毕之后再关闭任务,那么就不会出现数据库更新不完毕的问题了;再比如可能有时候我们正在打开一个文件,然后往文件里面写入信息,那么如果这个时候线程池关闭了,我们程序就不能继续给文件写入最新的信息了,那么这个时候也就不能确保文件数据的一致性了,而优雅的关闭线程池可以避免这个问题。
异步关闭应用程序的结果是什么时候返回的?
不管是在客户端应用程序,还是在服务端应用程序,都可以发现有一个异步关闭连接的代码 如下图:
可以发现不管是启动服务器的时候,还是在客户端连接服务器的时候,都会有个异步关闭连接的代码。
我们就拿服务端的异步关闭连接的代码来说吧。如下图:
这里其实服务器仅仅是关闭了与一个客户端的连接,因为future.channel()就是获取的某一个客户端对应的Channel,然后调用closeFuture().sync()异步的去关闭这个Channel;那么这个时候你肯定会有一个疑惑对不对?就是,一个服务器启动之后,会有好多个客户端连接,我这里代码里面只能关闭一个,关闭这个这句代码就不能被调用了,那么问题来了,其他客户端对应的Channel如何关闭呢?如果但从我们这里的代码来看确实是有问题的,我们这里仅仅是只能关闭一个客户端的,但是在实际开发中,每当客户端连接上服务器之后,我们可以把客户端对应的Channel存储到一个Set集合中,最后我们遍历这个集合,然后给每个客户端对应的Channel对象都调用异步关闭连接的方法closeFuture().sync()就可以了。
既然是异步关闭服务器与一个客户端的连接,那么肯定是有一个异步结果的通知时机,就是我什么时候能获取到别的地方的异步的结果呢?就是什么时候我的future.channel().closeFuture().sync()方法可以不再阻塞了呢?那就是当我们对应的Channel的客户端里面的所有的任务都执行完毕之后,这个时候会去通知主线程,会给future.channel().closeFuture().sync()发送一个异步结果,然后这句代码就不再阻塞了。
那么问题又来了,为什么我们要异步关闭连接呢?同步关闭不行吗?答案是不行,同步关闭会有一些问题:
- 同步关闭的话,会导致我们再处理客户端任务的时候,主线程什么也不能干,只能干等待客户端任务执行完成;但是如果是异步操作,那么我们在执行客户端任务的时候,主线程里面还可以执行一些其他操作,比如说我们可以进行一些IO操作,去访问一些数据库数据。这样效率就会比较高。
不关闭客户端与服务端的这个连接行不行?
不行。因为不关闭这个连接的话会造成内存泄露,就是我们的客户端任务已经执行完毕了,按道理来说已经可以把服务端和这个客户端连接相关的内存都释放掉了,比如说把Channel,Socket,ByteBuf占用的相关内存都释放掉,如果不关闭连接的话,这些对象依然会存在,它们仍然会占用内存,既便它们已经不会再被使用,因此这就会造成内存泄露问题了。
什么是内存泄露呢?就好像是小偷偷油一样,本来一箱油总量10L,现在被小偷偷走了3L,虽然油的总量没有变,但现在你能够从油箱中使用的油只剩下了7L。而内存泄露相关的小偷就是哪些不能使用但是仍然占用内存资源的对象,这些对象我们不能正常使用相当于是一些废弃的东西,但是它自身又拿着一些内存,我们不能让其主动释放,这些内存我们也不能正常使用。关闭连接可以把这个客户端相关的Channel对象给清理掉,从而主动的释放它所占有的内存资源;关闭连接也会把Socket对象清理掉,从而释放掉服务器中的Socket占用的一些内存资源,占用的是哪些内存资源呢?比如Scoket它其实包含了客户端通过网络传输的数据吗,那么这些数据被服务端接收之后肯定是保存在内存中的;关闭连接还会服务端与每个客户端连接的时候不仅有Channel,还有一个ByteBuf用来管理处理客户端请求需要用到的缓存的,当我们关闭连接的时候,也会清理掉ByteBuf对象,从而释放掉这个对象相关的内存资源。
其实内存泄露也就是一些无用的对象还占用着计算机系统有限的内存。