1.9. 关闭你的应用
如果你运行了TimeClient,你肯定可以注意到,这个应用并没有自动退出而只是在那里保持着无意义的运行。跟踪堆栈记录你可以发现, 这里有一些运行状态的I/O线程。为了关闭这些I/O线程并让应用优雅的退出,你需要释放这些由ChannelFactory分配的资源。
一个典型的网络应用的关闭过程由以下三步组成:
关闭负责接收所有请求的server socket。
关闭所有客户端socket或服务端为响应某个请求而创建的socket。
释放ChannelFactory使用的所有资源。
为了让TimeClient执行这三步,你需要在TimeClient.main()方法内关闭唯一的客户连接以及ChannelFactory使用的所有资源,这样做便可以优雅的关闭这个应用。
package org.jboss.netty.example.time; public class TimeClient { public static void main(String[] args) throws Exception { ... ChannelFactory factory = ...; ClientBootstrap bootstrap = ...; ... ChannelFuture future = bootstrap.connect(...); future.awaitUninterruptible(); if (!future.isSuccess()) { future.getCause().printStackTrace(); } future.getChannel().getCloseFuture().awaitUninterruptibly(); factory.releaseExternalResources(); } }
代码说明
1) ClientBootstrap对象的connect方法返回一个ChannelFuture对象,这个ChannelFuture对象将告知这个连接操 作的成功或失败状态。同时这个ChannelFuture对象也保存了一个代表这个连接操作的Channel对象引用。
2) 阻塞式的等待,直到ChannelFuture对象返回这个连接操作的成功或失败状态。
3) 如果连接失败,我们将打印连接失败的原因。如果连接操作没有成功或者被取消,ChannelFuture对象的getCause()方法将返回连接失败的原因。
4) 现在,连接操作结束,我们需要等待并且一直到这个Channel通道返回的closeFuture关闭这个连接。每一个Channel都可获得自己的closeFuture对象,因此我们可以收到通知并在这个关闭时间点执行某种操作。
并且即使这个连接操作失败,这个closeFuture仍旧会收到通知,因为这个代表连接的 Channel对象将会在连接操作失败后自动关闭。
5) 在这个时间点,所有的连接已被关闭。剩下的唯一工作是释放ChannelFactory通道工厂使用的资源。这一步仅需要调用 releaseExternalResources()方法即可。包括NIO Secector和线程池在内的所有资源将被自动的关闭和终止。
关闭一个客户端应用是很简单的,但又该如何关闭一个服务端应用呢?你需要释放其绑定的端口并关闭所有接受和打开的连接。为了做到这一点,你需要使用一种数据结构记录所有的活动连接,但这却并不是一件容易的事。幸运的是,这里有一种解决方案,ChannelGroup。
ChannelGroup是Java 集合 API的一个特有扩展,ChannelGroup内部持有所有打开状态的Channel通道。如果一个Channel通道对象被加入到 ChannelGroup,如果这个Channel通道被关闭,ChannelGroup将自动移除这个关闭的Channel通道对象。此外,你还可以对 一个ChannelGroup对象内部的所有Channel通道对象执行相同的操作。例如,当你关闭服务端应用时你可以关闭一个ChannelGroup 内部的所有Channel通道对象。
为了记录所有打开的socket,你需要修改你的TimeServerHandler实现,将一个打开的Channel通道加入全局的ChannelGroup对象,TimeServer.allChannels:
@Override public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) { TimeServer.allChannels.add(e.getChannel()); }
代码说明
是的,ChannelGroup是线程安全的。
现在,所有活动的Channel通道将被自动的维护,关闭一个服务端应用有如关闭一个客户端应用一样简单。
package org.jboss.netty.example.time; public class TimeServer { static final ChannelGroup allChannels = new DefaultChannelGroup("time-server" ); public static void main(String[] args) throws Exception { ... ChannelFactory factory = ...; ServerBootstrap bootstrap = ...; ... Channel channel = bootstrap.bind(...); allChannels.add(channel); waitForShutdownCommand(); ChannelGroupFuture future = allChannels.close(); future.awaitUninterruptibly(); factory.releaseExternalResources(); } }
代码说明
1) DefaultChannelGroup需要一个组名作为其构造器参数。这个组名仅是区分每个ChannelGroup的一个标示。
2) ServerBootstrap对象的bind方法返回了一个绑定了本地地址的服务端Channel通道对象。调用这个Channel通道的close()方法将释放这个Channel通道绑定的本地地址。
3) 不管这个Channel对象属于服务端,客户端,还是为响应某一个请求创建,任何一种类型的Channel对象都会被加入ChannelGroup。因此,你尽可在关闭服务时关闭所有的Channel对象。
4) waitForShutdownCommand()是一个想象中等待关闭信号的方法。你可以在这里等待某个客户端的关闭信号或者JVM的关闭回调命令。
5) 你可以对ChannelGroup管理的所有Channel对象执行相同的操作。在这个例子里,我们将关闭所有的通道,这意味着绑定在服务端特定地址的 Channel通道将解除绑定,所有已建立的连接也将异步关闭。为了获得成功关闭所有连接的通知,close()方法将返回一个 ChannelGroupFuture对象,这是一个类似ChannelFuture的对象。
1.10. 总述
在这一章节,我们快速浏览并示范了如何使用Netty开发网络应用。下一章节将涉及更多的问题。同时请记住,为了帮助你以及能够让Netty基于你的回馈得到持续的改进和提高,Netty社区 将永远欢迎你的问题及建议。
相关推荐
Netty入门教程
Netty 入门与实战:仿写微信 IM 即时通讯系统
netty入门到精通
Netty 入门与实战:仿写微信 IM 即时通讯系统
Netty 入门与实战
本工程采用maven+netty4.1.0+PrefixedStringDecoder+json技术,包括客户端和服务端。先运行服务端SampleServer,再去等客户端SampleClient。示例中发的是心跳包,其中消息格式定义为msgType + msgNo + content(json...
Netty 入门与实战:仿写微信 IM 即时通讯系统,掘金小册子,netty教程。章节齐全无缺失,排版非常不错。 1.仿微信IM系统简介 1 2.Netty是什么? 2 3.服务端启动流程 8 4.客户端启动流程 11 5.实战:客户端与服务端双向...
Netty 入门与实战:仿写微信 IM 即时通讯系统
掘金小册 Netty 入门与实战:仿写微信 IM 即时通讯系统,自己就是用这个来学习的,分章节的html版本,还不错
netty 入门Reactor示例
netty入门进阶之前
Netty实现IM通讯
Netty快速入门系列源码, 参考 https://blog.csdn.net/netcobol Netty是一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。 Netty是...
一个简易的netty入门案例,打印客户端地址和客户端输出hello netty字符串,编码清晰易懂,注释全面。解释清楚,入门快,高效
netty入门与实战-netty-learn
1.该项目基于maven构建,相关博客 ... 一个最简单的netty应用 使用:运行EchoServer,打开cmd命令...5.第四个示例 com.user_1 Netty将java对象作为数据的发送与接收升级版。将数据的预处理模块化提炼出来。 使用:同上