Netty API使用总结

Q1:服务器主动关闭连接后,客户端没有监听到socket关闭的事件。

想象中,调用org.jboss.netty.channel.Channel.close();-->GateServerHandler.closeRequested-->channelDisconnected-->channelClosed-->client

实际上,调用org.jboss.netty.channel.Channel.close();-->GateServerHandler.closeRequested 就停止了,导致游戏实现上,玩家重复登陆,客户端没法收到服务器已经关闭了同一玩家前面建立的连接,所以客户端表现为无法断开与服务器的连接。

后来发现少了这样的一句话,导致closeRequested 就停止了。

	public void closeRequested(ChannelHandlerContext ctx, ChannelStateEvent e) {
		ctx.sendDownstream(e);
	}
closeRequested 一般为服务器主动发起的关闭,另外,关于channelDisconnected和channelClosed的区别,看源码:

org.jboss.netty.channel.socket.nio.NioWorker.java

//netty version: 3.1.5GA
//file: org.jboss.netty.channel.socket.nio.NioWorker.java
//method: static void close(NioSocketChannel channel, ChannelFuture future)
//line: 581

future.setSuccess();
if (connected) {
    fireChannelDisconnected(channel);
}
if (bound) {
    fireChannelUnbound(channel);
}

cleanUpWriteBuffer(channel);
fireChannelClosed(channel);

我们可以看到,在上述代码中,在close channel的时候,会先判断当前channel是否处于connected状态,即是否已经成功地与远程地址建立了连接,如果是的话,就触发channelDisconnected事件;最后,再统一触发channelClosed事件。
 
也就是说,任何对NioWorker.close(NioSocketChannel channel, ChannelFuture future)方法的调用都会触发channelClosed事件,这些事件可能包括如下几种:
1. 已经与远程主机建立的连接,远程主机主动关闭连接,或者网络异常连接被断开的情况
2. 已经与远程主机建立的连接,本地客户机主动关闭连接的情况
3. 本地客户机在试图与远程主机建立连接时,遇到类似与connection refused这样的异常,未能连接成功时 
而只有当本地客户机已经成功的与远程主机建立连接(connected)时,连接断开的时候才会触发channelDisconnected事件,即对应上述的1和2两种情况。
上述猜想已经通过编写测试代码,模拟不同的情况得到了证实。

Q2:一个Boss线程(一个服务器端口对于一个)--->接收到客户端连接--->生成Channel--->交给Work线程池(多个Work线程)来处理。

一个连接会绑定一个worker线程,选择的线程算法如下:

 public E nextWorker() {
        return (E) workers[Math.abs(workerIndex.getAndIncrement() % workers.length)];
    }

按顺序循坏往复。

这个连接的所有接受消息和发送消息最终都会由这个worker来执行

在写出消息时,如果不是IO线程,则会写入到发消息队列里,由IO worker线程去处理,如果是IO 线程则直接写出去

监控点:UnpooledSendBuffer.transferTo

http://www.haogongju.net/art/2034726

http://xsh5324.iteye.com/blog/1915534
具体的Work线程---读完已接收的数据到ChannelBuffer---触发ChannelPipeline中的ChannelHandler链来处理业务逻辑。
注意:执行ChannelHandler链的整个过程是同步的,如果业务逻辑的耗时较长,会将导致Work线程长时间被占用得不到释放,从而影响了整个服务器的并发处理能力。
所以,为了提高并发数,一般通过ExecutionHandler线程池来异步处理ChannelHandler链(worker线程在经过ExecutionHandler后就结束了,它会被ChannelFactory的worker线程池所回收)。
Boostrap.bind 方法包含两个参数NioServerSocketChannelFactory、ChannelPipelineFactory。NioServerSocketChannelFactory包含连个线程池bossExecutor和workerExecutor,workerExecutor: 包含缺省为处理器个数×2个NioWorker进程。
对于ExecutionHandler需要的线程池模型,Netty提供了两种可选:
1) MemoryAwareThreadPoolExecutor 通过对线程池内存的使用控制,可控制Executor中待处理任务的上限(超过上限时,后续进来的任务将被阻塞),并可控制单个Channel待处理任务的上限,防止内存溢出错误;
2) OrderedMemoryAwareThreadPoolExecutor 是 MemoryAwareThreadPoolExecutor 的子类。除了MemoryAwareThreadPoolExecutor 的功能之外,它还可以保证同一Channel中处理的事件流的顺序性,这主要是控制事件在异步处理模式下可能出现的错误的事件顺序,但它并不保证同一Channel中的事件都在一个线程中执行(通常也没必要)。

Q3:如果压力测试写日志频率较高,会阻塞netty发送消息,需要给日志起单独线程


Q4:默认netty起来后,如果用Executors.newCachedThreadPool(),默认会起1个boss线程,8个work线程,但实际上后台并不需要这么多work,

// Configure the server.
ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),
Executors.newSingleThreadExecutor()));

发现,其他客户端连上来后,断开后,就再也没法连了,

Executors.newSingleThreadExecutor()换成Executors.newFixedThreadPool(1) 就没问题了。


netty客户端这边一个ClientBootstrap  可以连多个server

你可能感兴趣的:(Netty)