« 游戏数值策划 | 返回首页 | Protocol Buffers for C »

开发笔记 (3)

这周的工作主要是写代码。

开发计划制定好后,我们便分头写代码去了。我们希望一期早点做出可以运行的东西来,一切都从简。整体的代码量并不多,如果硬拆成很多份让很多人来做的话,估计设计拆分方案,安排工作,协调每个人写的东西这些比一个人全部实现一遍的工作量还要大的多。

所以,最终就是两个人在做。怪物公司在弄客户端的东西,蜗牛同学包干了服务器。好吧,基本没我的事了,我就是那个打酱油的,好听点说,就是设计方案。当然,事情没多少,空下来的时间也可以干活。训练自己可以找到事情做,并真的做有用的事情,还是很难的。

话说回来,我们在这么一个简单的框架下,一开始确定了要采用一些现成的技术方案,即要用到 Redis , Google Protobuffer , ZeroMQ 。

Redis 和 ZeroMQ 是我最早选的,想了很久。


采用 Redis 是因为历史上,我参与的项目都没有大规模使用 SQL 数据库的传统。这和 MMO 这种特定应用有关。在 Web 开发中,面对的用户是临时的,不依赖固定连接的。你不确定用户在不在那里,你不确定同时要面对的用户有多少。你需要从小到大,采用一种可扩展容量的方案。这个时候,成熟的 SQL 数据库方案是首选。

从软件开发角度看,数据库是 MVC 模式中的 M 。以 MVC 模式解决问题,M 如何实现,采用 SQL 方案只是一种可能,绝不是唯一选择。换个角度考虑,如果是一个桌面软件,为什么大部分的 M 却没有采用数据库,而更多的是在内存中直接构建数据结构呢?性能恐怕只是一个原因,更重要的原因是面对的用户的行为不同。

为什么 MMORPG 服务器,至少在网易历史上的多款游戏,没有使用 SQL 服务做 M 。一部分原因是,网易游戏的开发源头是 Mud ,Mudos 并没有使用 SQL 作为 M ,另一部分原因是,MMORPG 面对的,同时需要服务的用户有限。而用户需要操作的数据大部分限制在用户相关的数据体内。之外的数据体非常少。及时数据总量很大,但一层索引简单(以用户 id 为索引)。每个用户都是为它持续服务,数据易变。这种行为下,从 MVC 角度看,更接近网络应用之前的软件设计。

当然你甚至可以把 M 只在逻辑上划分出来,物理上并不切换,也就是没有独立意义上的数据库服务器。这样绝对性能最高,其实只是实现了一个简单的单机游戏,允许通过网络输入多条操作流,并把行为反馈通过网络发送回去罢了。甚至比单机游戏更简单,因为没有图形控制部分。

如果程序不出问题,机房不停电,可以一直的跑下去,不用考虑数据储存问题。数据持久化不过是为了容灾罢了。把一些结构化数据持久化到硬盘上,最简洁的方案就是写操作系统层面的文件,一定比再使用一个数据库要轻量,干净的多。

有了以上背景,就不难理解,为什么我对 Redis 天生有好感。我们并没有改变设计思路,它是一直延续下来的。Redis 帮助了我们将数据服务拆分出来。当然,MMORPG 也在发展,以上提到的用户应用环境也在逐步变化,我们在软件设计上也会跟进这些变化。这些就是后话了。


ZeroMQ 呢,我是希望有一个稳健,简洁的多进程通讯方案的基础。ZeroMQ 是不错的一个。至少比 OS 的 Socket 库要实用的多。它提供了更好的模式 。这是我最为看中的。另外,这是一个 C 接口的库,容易 binding 到不同的语言下使用。

在这个问题上,蜗牛同学是反对使用 ZeroMQ 的。对他的所有反对意见,我都持保留态度。但我尊重开发人员的意愿。毕竟许多代码不会是我自己来写。蜗牛同学希望采用 Erlang + C Driver 的方式来驱动整个框架。也就是用 Erlang 来做通讯上的数据交换。其它可能采用的开发语言,都通过 Driver 的方式插入到 Erlang 的框架中去。

我个人觉得这样做的确可行,加上蜗牛同学有好几年 Erlang 开发经验,他能担负起实现框架的责任。我不是特别喜欢这个方案是因为,Erlang 这个东西还是太庞大了。我对庞大的东西天生反感。虽然以蜗牛同学的原话说,Erlang 以及他的 OTP 能写出这么多行代码来有他的必要性。我们用 C/C++(Python/Lua/Golang 等等) + ZeroMQ 实现一个拙劣的方案出来,只会漏洞百出。

我个人是不以为然的,毕竟已经做了 10 年的网络游戏,对这个领域已经很熟悉了。对于陌生领域,我们会面对许多未知的问题;但在熟悉领域,无论怎么做,方案都不可能太拙劣。只要保持最基本的简洁,我是有信心保证可以解决 MMORPG 中的各种需求的。做出来的东西也能很稳定。关键点在于,它会足够简单,能轻松的理解实现的每一行代码。

不过争论都放在一边。我的原则是,最终采用实现者自选的方案,只要它没有明显的问题。

我们最终不采用 ZeroMQ 。


Google Protobuffer 我不是很喜欢的。但采用它是多个角度妥协的结果。其实我更愿意自定义一套协议。而只是裁剪 GPB 的功能。

我认为,GPB 在最底层的协议编码定义上,做的还是不错的。改进它是多余的。作为一种协议定义,协议描述语言也算定义的不错。但只到此为止。接下来的部分就不甚满意。

GPB 协议本身,默认也是用 GPB 本身定义编码出来的。这看起来很 Cool ,但我不甚喜欢。当然所有完备的类似协议都应该有描述自己的能力。对于描述自己这件事情来说,GPB 还是稍显复杂了。

比如,如果你不借助已经有的 GPB 代码和工具,很难解析一个 GPB 协议。就好比,如果世界上第一款 C 编译器,就是最难实现的 C 编译器。因为大部分的 C 编译器是用 C 写的,实现一个 C 编译器,就陷入了先有鸡还是先有蛋的问题。

在这类问题上,据说 Lisp 比 C 要干的漂亮。不过我还是和世界上大多数程序员一样,用 C 多一些。好吧,我们还是继续用 GPB 好了。

google 在实现和使用 GPB 的时候,默认采用了一种为每个协议,生成一组代码的方式。而不是提供一套 C/C++ 库,供其它语言做 binding 。这也是我所不喜的。或许是为了性能考虑,但总觉得别扭。如果把 GPB 换成正则表达式来看,你就能理解我的心情。

现存的大多数正则表达式的实现,都是提供一组 API ,允许使用者把需要的模式以一种人类可读的串形式,编译为另一种计算机方便处理的数据结构。当你需要的时候,使用这个数据结构,交给库,就可以匹配,替换字符串了。如果默认的选择是把正则表达式编译成 C 代码,然后你用的时候再 link 到你的项目中,恐怕用的人要疯掉了。当然,生成代码这种可以带来更高的运行性能。

唔,其实这只是怎样使用 GPB 这种协议的问题,和协议定义关系不大。可惜 google 在开源之初就给出了官方的方案,引导其它语言也如法泡制,成了 GPB 的惯用法。老实说,对于 C++ ,这么做性能是不错的(其实也未必)。换到 Python 里,就非常低下了。去年我按我的思路实现了 lua 的 protobuf 解析库 ,性能可以达到和 C++ 版本差距不到一个数量级,甚至快过 java 版。

这周的剩余时间我都在写一个纯粹的 C 版的 protobuf 库,不依靠代码生成器的。希望能够作为它语言使用 GPB 的基础。别的语言只需要做 binding 就够了。这玩意挺难写,光接口设计我就改了两版。今天终于快收工了。过两天再写一篇文章专门谈谈这个问题吧。当然,还有开源。

既然在 GPB 上花了这么多功夫,当然,采用 GPB 就是最后的决定了。

Comments

本来是找之前有个留言回复一下,发现几年前有一个留言。

这么多年使用下来感觉是GPB确实有优势,多语言支持和tests写得全的优势……

从纯粹的协议设计上来说的话其实flatbuffers很简单完整,而且这几年一直在改进进步。

别的协议设计大都是过家家。

踩一个,证明我来过。

保持简单最重要,容易理解,容易维护。

我们用过erlang作为框架也用过zeromq搭建过通讯框架。
用erlang的问题是,erlang代码写起来容易,维护起来麻烦,框架开发者走了之后,谁都不愿意继续维护。
zeromq的问题是,封装层级太高了,一些有特殊需求的业务不好处理。

首先你选择自己熟悉的技术方案这点我是比较认同的,毕竟是在做产品,不是再搞科研。然后从学术角度看,我还是比较支持erlang,它已经发展20多年,它的成熟度毋庸置疑,再加上天生的分布式优势让人无法拒绝,对于很多没有你那么有经验的人来说这是非常好的解决方案,关于很多同学提到erlang写逻辑的问题,其实就是一种编程习惯,只要你愿意,你够open,那么你就可以。至少人才方面也不用担忧,随着webgame的方法,erlang已经满地开花了。

erlang没用过,只是懂一些概念,不过会担心大部分同事以及未来的新同事都需要重新学习的话,这个选择就算在技术上有可取之处,也需要谨慎,毕竟这项技术不是唯一方案。(私下说一句,用erlang还不如考虑一下ice,伸缩性更强的解决方案)
protobuf每个语言一个实现这个确实很郁闷,原生的python实现就很低效。不过python和c天生穿一条裤子,给予c模块的python-protobuf已经接近可用阶段,可以看出gpb的思路。

看你的文章 让人感觉到一种无助和压力 每一步都是非常艰辛 我想知道每个人不管成功了还是失败了 挫折就是能做到别人做不到的事情

这些不是外在的东西 是精神

好好设计,以后卖游戏引擎也不错

用erlang来做服务器端的数据交换确实应该是一件轻松的事情, 我自己也有和蜗牛同学一样的想法, 但是没有接触过游戏服务器的开发, 不知道会有哪些设计,该如何设计, 不知云风大侠能否画个整个游戏设计的数据交互流程图呀, 期待下一篇blog

用erlang的坏处就是,不是大家所有人都懂,以后单个人可能成为这个公司的single point of failure...

妥协就是悲剧的开始,同感。
而且就怕你们把精力专注在技术实现上,忽略了游戏本身的设计,最终落得个为别人所用的下场……

zeromq胜在简单,配合routing和push\pull可以做出很灵活的方案。

Erlang没有传说的那么神,为了支持高并发,里面有很多内存拷贝,导致效率不是很高。公司有个项目开始打算用erlang,后来经过严格的测试,效率没有达到要求直接放弃了。而且用erlang写逻辑的话,很蛋疼,对习惯类C语言的程序员来说,开发和调试都不是很方便。个人觉得做游戏的话,语言什么的,都不是最重要的,重要的游戏的玩法,应该把时间多花在游戏玩法的设计上。据我所知,弹弹堂服务器是用C#写,一样不妨碍它很成功。。个人愚见。

一个服到后期,不活跃的号占多数, 活跃的号占少数,Redis的话就有点浪费内存,而且并区之类的难以处理,Mongodb存储全部数据,redis存储hot数据好像比较经济。
另外,如果是侧重于web的话,json从前端到后端通用。

风哥,挺佩服你的,但看完这篇文章,有个不好的预感,我觉得你们把事情做复杂了,风哥你做了这么多年的C/Lua架构,不敢说轻易但还是放弃了自己的方案(其实应该完全有理由相信一定会做得比erlang更加简洁优雅), 如果基本的框架不是云风大侠亲自操刀,项目到一定阶段后一定会遇到各种问题,甚至失控

用这些技术问题都不大,不过最大的问题是,如果你打算整套搬这些开源的东西然后堆砌出一个系统出来,我看头疼的问题还是不少。其实你应该把这些东西的思想可以抽出来的抽出来,然后结合各种系统成一个完整体系,开发使用你这个体系就好了,既保证了开发效率也保证了性能和稳定性,扩展性。每一个开源项目的背后,都多多少少隐藏着一些让人蛋疼的东西,所以直接拿来堆砌,还是会很蛋疼的。

protobuf这么多年都是使用.h文件+工具生成.c;.c++代码。自认为比GPB简单好用。

@grayflow

谢谢, 我刚才花了半个小时浏览这个项目. 他的需求的确和我想的一样.

但是他的解决方案我觉得不太好. api 搞的太细节, 太接近实现. 用起来不舒服.

我自己的版本已经基本完工了,这两天就可以放出来. 到时候跟这个作者交流一下.

云风写的不错学习了

客户端倒是可以好好研究

在项目中面临选择时,不管选择什么方案,解决了眼前的问题,但都会为未来带来新的挑战。对于某一个方案的选择,并不是因为这个方案绝对优越与其他方案,而是因为我们对于这个方案的后续思路非常清晰,对于未来所遇到的各种挑战,有明确的应对策略。当你自己放弃你的方案时,也就放弃了自己对未来挑战的应对措施。不久以后,你如果发现在处理后续挑战时,你的同伴并不能轻松的应对,你会后悔你现在的妥协(虽然在处理眼前问题时,他可能做的比较优美)!

哎,妥协就是悲剧的开始

如果手下拥有一帮erlang开发熟手,用erlang来构建网游的服务器端绝对没有问题,其语言级别的分布式支持太漂亮了.但现实是,了解erlang的程序员并不多,而且要很好理解掌握erlang的编程思想和方法的人也很少.尽管otp抽象出来的上层顺序编程模型可以降低开发的难度,但是前提是开发人员要对otp的整体设计和erlang的设计理念要有一个全局的掌控.网游需求相对还是不那么复杂的,用传统的语言开发,直观,容易理解,好维护,好修改扩展就可以了:)
protobuf+zeromq我们现在就在用,确实是不错的选择,一个抽象出传输层,一个抽象出协议解析层,在它们基础上搭好框架,利用protobuf的rpc框架客户端和服务器端实际上可以实现非常漂亮的rpc机制,最终交给上层逻辑程序员的工作很少.
个人觉得一个好的技术选择是,可以很方便的把工作独立分给多人做,每个部分都可通过测试或其它方法来确保其稳定可靠性,合并起来之后又能保证是一个健壮稳定的系统.
个人愚见,呵呵:)

https://github.com/haberman/upb/wiki
一个googler搞的protobuf c实现,貌似很符合你的要求

不太懂服务端开发。问一下像protobuf这样的序列化工具,都是用在哪些上面的?以及为什么应当使用?我接触到的网游服务端,都是用些很基础的东西。是不是为了不同语言间传递对象的方便?

个人不是很喜欢gpb这种序列化的东西,不过还是很期待c的gpb parser

总算是看懂一点了

protobuf采用生成代码的方式,首要意义恐怕还不是性能,而是静态类型检查。当然这只对C++、Java、ActionScript有意义,对于Python、Lua这种动态类型语言没意义。

ZeroMQ和Erlang都是好东西。二者区别在于一个是以协议为中心,一个是以程序语言为中心。

在二者都可行的前提下,遵从实现者个人选择,这很好啊。

Erlang是个好选择,不过GPB有所保留。

boost::xpressive

新游戏最好有更贴近用户更喜人的色彩对比以及交流表达方式~我路过的哦~呵呵~

个人觉得,创业的时候考虑用什么东西实现不重要,重要的是做什么,游戏表现是什么样子.话说服务器就那么点东西,稳定就行了,客户端倒是可以好好研究一下.

没想到服务器端竟然是用Erlang来实现的,虽然Erlang做大型分布式系统不错,但是一般网游也到不了这种规模。

采用redis是个不错的选择,采用过多的第三方东西进行组合不一定是好事,在架构上走自己的路实在。

期待gpb的c开源版本!

作为一个java程序员,我看不懂!

都爱用自己熟悉的东西来重构啊。。。如果赶着要出些东西出来看的话,会不会浪费时间。。。

Post a comment

非这个主题相关的留言请到:留言本