`
秋天的童话穷
  • 浏览: 78750 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Netty入门5

    博客分类:
  • Java
阅读更多

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

 

ChannelGroupJava 集合 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社区 将永远欢迎你的问题及建议。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics