陷阵之志,有死无生 --赵信
如大家对网络还不甚了解者,请先参照本系列网络介绍博文 游戏之网络初篇
现在很少有游戏公司自己用原生Socket API手写网络框架了,一是增加工作量,增加产品开发时长;二是如果综合性能、效率、高吞吐量、高并发,及稳健性来说,自己如果非大神,写出来的很可能还没别人已有的网络框架好,自己还需经大量测试验证才能保证线上稳定,不容出错。所以,现在很多的Java游戏框架,基本上都是用Netty或Mina作为网络通信框架的,Netty和Mina用起来有很多相似之处,知其一另一个上手就很容易,但在很多人看来,Netty比Mina更好用(更适合游戏使用),其一,Java游戏公司基本都是用Protobuf作为协议工具的,以应对游戏各种功能玩法中前后端所需不同的数据字段读取和传输要求,及减少协议联调出错几率,Netty包中自带对Protobuf的编解码器,而Mina还需自己去实现Protobuf的编解码器;其二,Netty比Mina更好地支持多种协议,比如对Http的支持,Netty就比Mina好些,在《游戏之网络初篇》中,游戏常用的协议Netty都支持,简直为游戏量身打造;其三,Netty比Mina更易使用,除了Netty自带很多编解码器外,在对ByteBuf的操作比Mina的IoBuffer的操作更简易,此外,Netty支持ByteBuf零拷贝,一听就是让人省心的提高效率和节约内存的高科技东东。
Netty 和 Mina都为韩国Trustin Lee开发出来的,这也是它们很多相似之处的原因之一。它们都是优秀的异步事件驱动,NIO(非阻塞)网络框架,获得了成百上千的商业项目验证。当然,在游戏开发中,也是锦上添花。
Protobuf 是谷歌的Protocol Buffers的简称,它是一个轻便高效存储和读取结构化数据的工具,用于结构化数据和字节码之间互相转换(序列化、反序列化),非常适合用于网络传输,且支持多种编程语言。游戏中使用Protobuf可以减少协议联调出错的几率,因为一旦使用Protobuf作为协议工具,当定好协议后,开发人员只管设置相应字段值即可,如果担心字段漏传,就用required限定即可;但是如果用其他的协议工具,那么开发人员除了设置相应字段值外,可能还需要正确设置字段长度,及字段顺序。
Netty的 ByteBuf 和 Mina的 IoBuffer 都是对JDK的 ByteBuffer 的高级封装,比起JDK的ByteBuffer,它们都支持动态扩展(而扩展JDK的ByteBuffer只能新建复制),且修复了JDK的众多NIO bug(比自己用原生JDK NIO API更少bug);但是 Mina的IoBuffer和JDK的ByteBuffer都只有一个标识位置的指针position,切换读写的时候要自己手动调用flip()和rewind()等,用起来稍微麻烦,而Netty的ByteBuf使用readerIndex和writerIndex分别维护读操作和写操作,实现读写索引分离,更加直观。
本文假设读者已对Netty有初步的了解,如知道Netty的大致框架,了解Netty的核心组件的关系及其作用,了解消息处理的大致流程。下面放图两张供读者回忆Netty基本知识点(摘自《Netty实战》),其余需更详细了解Netty的建议细读何品翻译的《Netty实战》和李林锋的《Netty权威指南》,更细节的需要读者自己钻研了。
netty核心组件.png
Netty核心组件的关系如下:【
- 一个 EventLoopGroup 包含一个或者多个 EventLoop;
- 一个 EventLoop 在它的生命周期内只和一个 Thread 绑定;
- 所有由 EventLoop 处理的 I/O 事件都将在它专有的 Thread 上被处理;
- 一个 Channel 在它的生命周期内只注册于一个 EventLoop;
- 一个 EventLoop 可能会被分配给一个或多个 Channel。
注意,在这种设计中,一个给定 Channel 的 I/O 操作都是由相同的 Thread 执行的,实际
上消除了对于同步的需要。】
注意上面【】中引用《Netty实战》的最后一句,即“一个给定 Channel 的 I/O 操作都是由相同的 Thread 执行的,实际
上消除了对于同步的需要。”,虽然Netty中是这样,但在实际游戏开发中,因为一个EventLoop管理多个Channel,而每个EventLoop上所有的I/O却只由一个Thread处理,这就相当于多个客户端的I/O请求都由一个Thread处理了,这明显同一时刻可能阻塞其他客户端了,所以在实际游戏开发中,每个Channel的I/O操作(即每条协议请求),都会再放到一个逻辑线程池中处理,这样避免阻塞其他客户端,哎呀,一不小心剧透后面游戏框架博文的内容了,具体详情请见后续框架解读吧。
netty消息流程.png
以上是I/O操作的流程示意图,实际中ChannelPipeline中的入站和出站handler都是类似如下添加的
bootstrap.handler(new ChannelInitializer<ServerSocketChannel>() {
@Override
protected void initChannel(ServerSocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("http_codec", new HttpClientCodec());
pipeline.addLast("http_aggregator", new HttpObjectAggregator(65536));
pipeline.addLast("protobuf_decoder", new ProtoDecoder(null, 5120));
pipeline.addLast("server_handler", new ProtoServerHandler());
pipeline.addLast("protobuf_encoder", new ProtoEncoder(checkSum, 2048));
}
});
本文的原来标题是《Netty+Protobuf实现游戏的TCP通信》,本来是想一篇文章即介绍完这个话题的,但发现因为想引入Netty和Protobuf的简介导致文章太长了,不简介又担心新手读者没有个承上启下的因果关系导致强行去理解Netty和Protobuf实现TCP通信显得略为僵硬,所以本文标题还是改为《Java游戏之网络进阶》算了,旨在对游戏中使用Netty和Protobuf有个大致的介绍,而实际上Netty的内容还远不止本文刚说的那一丁点,想想李林锋大神那两本Netty的书就知道了,希望读者能自己去钻研细读,广思集益,从而对游戏的网络框架有更好的了解。
通常,了解一个大众使用的框架的规则,比自己手写类似功能更为迅捷和靠谱,在现在快节奏的游戏更新迭代中,快速开发一款游戏成为节约成本,取得市场先手优势的先决条件。但这并不鼓励只是一味的“拿来主义”而不推崇自己的主观动手能力了,实际的技术沉淀还需我们自己不断拓展加深,说不定哪天一个很好的框架就出自这样动手能力极强的读者手中了。下篇博文可真正进入《Netty+Protobuf实现游戏的TCP通信》了,敬请阅读。
网友评论