Archive for the ‘Netty’ Category
Netty4服务端心跳机制
星期六, 五月 17th, 2014 4,385 viewsNetty4与Netty3.x的心跳机制略有不同,在Netty4中已经去掉了IdleStateAwareChannelHandler这个类,但IdleStateHandler依旧保留,只是心跳超时的触发事件的写法略有不同,Netty底层实现了一套类似信号和槽的事件通信机制。
这里且看实现。
首先是在 SocketChannel.pipeline 中注册 IdleStateHandler 进行心跳时间的定制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | // Configure the server. EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 100) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer<socketchannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline() .addLast( new LoggingHandler(LogLevel.INFO), new IdleStateHandler(30,0,0),// 心跳控制 new ServerHandler()); } }); // Start the server. ChannelFuture f = b.bind(port).sync(); // Wait until the server socket is closed. f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { // Shut down all event loops to terminate all threads. bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } </socketchannel> |
这里要注意IdleStateHandler()要放在ServerHandler()的前面,否则心跳管理无法触发ServerHandler中的userEventTriggered()方法。
此时已经成功在服务器中创建了心跳控制器:
IdleStateHandler(int readerIdleTimeSeconds, int writerIdleTimeSeconds, int allIdleTimeSeconds)
Property | Meaning |
---|---|
readerIdleTime | an IdleStateEvent whose state is IdleState.READER_IDLE will be triggered when no read was performed for the specified period of time. Specify 0to disable. |
writerIdleTime | an IdleStateEvent whose state is IdleState.WRITER_IDLE will be triggered when no write was performed for the specified period of time. Specify 0 to disable. |
allIdleTime | an IdleStateEvent whose state is IdleState.ALL_IDLE will be triggered when neither read nor write was performed for the specified period of time. Specify 0 to disable. |
Netty4客户端断线重连机制
星期五, 五月 16th, 2014 1,836 views首先要了解一下Netty4客户端的运行机制,NettyClient.java代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public class NettyClient { private final String host; private final int port; public NettyClient(String host, int port) { this.host = host; this.port = port; } public void run() throws Exception { // Configure the client. EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<socketchannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast( //new LoggingHandler(LogLevel.INFO), new NettyHandler()); } }); // Start the client. ChannelFuture f = b.connect(host, port).sync(); // Wait until the connection is closed. f.channel().closeFuture().sync();//运行后会在这里阻塞 } finally { // Shut down the event loop to terminate all threads. group.shutdownGracefully(); } } } </socketchannel> |
当我们在主线程中执行 NettyClient(NettyClient).run()时,主线程会在 ChannelFuture f = b.connect(host, port).sync(); 这里阻塞,这是Netty4的一个新特性,我们利用这个特性即可创建一个定时器来实现断线重连的功能。
Netty游戏服务器开发——利用Channel绑定机制 共享聊天服务器与逻辑服务器信息
星期二, 十二月 18th, 2012 2,702 views因为工作原因,之前做的网游项目一直没有时间对其技术点做一个统一的整理,今天正好朋友问到了这个问题,所以顺便把之前项目中的方案贴出来供大家参考。
首先我们需要明白一些概念,我们所玩的网络游戏的服务器是一台大规模的收发兼处理工厂,与客户端交互的数据流如同一个个快递。
Netty的NIO机制保证了服务器与每一个客户端之间都有一条特快专线,即Channel,游戏中的各种交互性数据即是通过这个Channel进行收发的。
现在问题来了,我们假设玩家的战斗行走等信息都是一个个包裹,聊天喊话信息都是一封封信件,客户端与服务器交互时,这些数据都是在一条该客户端独有的Channel里的,无论服务器还是客户端对数据的读取都是顺序执行的,如果此时客户端在接受一个角色战斗信息(包裹)时,聊天窗口有大量的聊天信息(信件)发来,那么客户端需要先读取完排在角色战斗信息(包裹)之前的聊天信息(信件)然后再读取战斗信息,接着才进行相应动画处理,这样就会产生一个delay,影响战斗的流畅感。反之服务器的接收也是如此。
所以为了保证在聊天信息大量收发的同时,战斗及其他游戏信息能够快速被相应,我们需要为每一个客户端再开辟一个收发聊天信息的Channel,把聊天信息通道分离出来。因此需要另外开始一个Netty服务器,即聊天服务器。
架设完成之后你将会看到,每个客户端与服务器之间都产生了2个Channel,分别为包裹专线,和信件专线。并且这两个服务器的Port是不一样的,需要客户端分别进行2次的连接。
这个时候又产生了一个问题,如果此时玩家要发送消息给周围的玩家或是自己的好友,而这些人物的列表并不在聊天服务器上,而是在逻辑服务器上。那么怎样将信息传达到对应人的Channel上呢。
传统的方法可能是在聊天服务器上再复制一份好友列表,甚至周围玩家列表,并且两个服务器之间时刻进行通信,以保证数据的同步性;或者每次发送聊天信息时,调用缓存区的数据,通过目标ID遍历寻找到目标的Channel。
我们来简单分析下这两种方案的运算开销:
方案一中,同步包的心跳最长不得超过1s,否则可能会出现玩家体验上的不同步。那么每1s同步一次,OK,我们设1000个玩家,每1s需要同步1000个玩家的各种信息,当然这显然不现实。所以继续优化方案一,改为触发型同步,即当逻辑服务器上的数据更新后,立即发送该更新数据的同步包给聊天服务器。此时1000个玩家如果聚集在主城附近来回跑动,最频繁的更新当属周围玩家列表的更新,或者采用的是九宫格灯塔AOI(此区域管理算法另外开篇说明)机制不需要周围玩家列表,但仍旧需要不停的更新自己的区域坐标——仍旧是大量的同步包。
(更多…)