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);
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