2014总结

很高兴渡过了有意义、有价值的一年。这一年大部分时间都很忙,工作满满。试过维护前和大家回归测试到一两点,试过自己连续几周工作7天赶制作,甚至经历了整个组一起处理事故到凌晨4、5点的时候。 但总体来说,这些辛苦是值得的。能看到自己的付出和努力,得到了回报,这是最开心的事情。产品的市场成绩还算可以,超出了我当初的预期。

去年在业务上的工作,无论从量还是质量,我个人还算比较满意,做出了自己对产品该有的产出。但有一些模块或数据的设计,自己后来看,不是很满意。 最主要原因,是一开始不熟悉服务端用的这套框架,里面有很多很不人性化的限制。 另外就是这套框架,还有原来底层的代码,坑确实有点多,比较影响程序员在设计时的思路,需要程序员在编写代码的时候还要分心去考虑各种不安全的因素,心智负担很重。

编码方面,还是坚持以往的高标准,无论工期多么紧,自己在代码可读性方面都还是花很大的功夫,去做好。 代码是给人读的,而开发过程,有很大一部分时间是在读代码,无论是读别人写的,还是读自己一个月前写的。 一份写得好的代码,能够降低程序员维护的难度,提高开发效率,减少出错的概率。以后工作中,在代码质量和代码规范这块,也要求自己起到带头作用,让团队每个成员阅读别人代码,都像看到自己写的代码一样熟悉、简单。

纯技术方面,这一年确实没做出什么有技术含量的东西。有可以做的,但我没去做; 我想做的,在这里似乎不能做。不过我还是想聊聊自己的一些想法,也可以看作是这一年我搞服务端这套技术的经验和总结。

我们服务端现在的架构是单进程多线程,除了网络和存盘是开了独立的线程做,其他逻辑都在主线程做。 这种架构的优点是部署起来简单,而且某个部分crash,整个进程都会挂掉,避免了一部分错误的程序还在执行。 但是,缺点也是非常明显的。由于大部分逻辑都在单个线程里做,无法利用多CPU,性能很容易见天花板。

我们的游戏有个特点,大部分逻辑都是基于“请求-响应”的形式,很像web。 玩家之间的交互以异步为主,没有AOI这种需要实时广播的需求。即便像世界聊天这种需要服务器push的需求,实际上通过客户端主动来拉取信息,也是没什么问题的。 所以更好的架构模型,应该是把逻辑和数据分离,分成两个模块,用进程间通信来交互。 逻辑模块,包含引擎和脚本,可以是一个多进程模型,通过启动一个master进程再fork多个子进程的形式运行,底层用IO多路复用技术监听socket。 每个子进程独立跑脚本,脚本里的逻辑都是可重入的,上下文无关。而数据模块,我们拉出一个数据库进程,来管理全局变量,处理上下文相关的部分。

举一个场景,我们的每一场战斗,跟npc打的这部分逻辑计算,实际上是相互独立的,上下文无关。 所以在多进程的环境下,就可以把并发的每个战斗请求,丢给不同的进程计算。我们有多少个CPU,就开多少个进程,这样很容易就能把CPU资源这块利用到极致。 但到了战斗的最后,检查是否插入到排行榜的时候,又是上下文相关,涉及和榜里的数据进行比较,这又需要对各个战斗结果进行互斥和同步处理。 利用数据库的锁机制和事务机制,我们可以轻松完成这点。扩展出去,像抽卡、飞升、融魂……这么多不同的逻辑,实际上大部分都是相互独立的,需要互斥的地方不多,这样的架构能更好地解决问题,并且获得更好的性能表现。

一般服务端应付高并发的业务,都会选用类似的逻辑与数据分离的架构,比如像一般的web框架,很少有单个进程跑所有东西的。 所以像我们这样一个项目,让我做服务端的技术选型,我是宁愿用web来做。第一,web技术从业务上满足需求,靠谱,没那么多坑和让人不爽的地方。 这点如果说mudos满足,那也只是在委屈程序员做很多额外工作的前提下。第二,web技术从性能上满足需求,通过加硬件的形式,性能很简单就能做到等比例的提升。 这点mudos是做不到的,无论给多少个CPU,它只能跑满一个(或者n个,算上其他线程,是个常数,多CPU的优势没法体现在它身上),而单CPU的频率目前在硬件上是没有多少提升空间的。 第三,web技术对程序员有吸引力,比较好招人,能让团队成员在技术上有所积累,有所成长。 这点mudos也是做不到的,因为太落后,业界根本不用。当然了,用web也是有缺点的,比如每次请求都要建立连接,http报文比较大,耗流量等。 实际上,用http还是tcp,这点根本不重要。把http换成tcp,选一个tcp的框架,只要有好的脚本语言,有个合适的数据库,大部分工作都变得很好做了。 这里面最重要的思路是,模块的合理切分。把整个框架切分成逻辑和数据两个模块,分别处理上下文无关,和上下文相关的逻辑,能更好地利用硬件资源,提高性能,并且更好地做横向扩展。 框架的模块切分得好,还有个好处,我们能针对不同的模块和部件,做更好的技术选型。 想用数据库,有MySQL、Mongodb、Redis等等,按需求单独用或组合使用都可以;想用协议,可以选Google protobuf,甚至简单点就用JSON; 只要选一个好的脚本语言,比如Python,就可以很好地把这些优秀的组件粘合在一起,让整个架构发挥最大作用,来满足我们的业务需求。 而且好的技术拥有完善的文档和活跃的技术社区,利用技术社区的力量,能帮助我们解决很多不同环节的问题。程序员也能在工作中得到能力上的锻炼,不断成长,为后续产品的技术研发积累经验。

回到我们这套技术,可以做的东西,其实有,比如引擎方面的优化。 LPC有很多恶心的限制,像mapping的65000,浮点数太小,序列化不了大数据结构,读不了大文件等等。引擎是用C写的,理论上,这些问题都是能解决的。 但是,我没有去解决它,包括部门这么多年下来,也没有人出来把LPC的问题解决掉。不是因为做不了,而是因为不值得,因为LPC在业界根本没人用。 我们解决了LPC的这些问题,只不过是解决别的语言而没有的问题。我们要做的,应该是用更好的语言。再比如我们的数据存储这块,现在引擎把内存数据flush到硬盘,是异步的,开了另外的线程去做。 但序列化数据结构这一步,还是在主线程做。所以如果把这块也开出来一个线程来做,这对系统性能应该有一定提升。再者,我一直觉得我们的逻辑代码其实很危险。 一些增删组合操作,没有事务机制的保护,中间万一哪个地方抛异常,前面的逻辑无法回滚,就有被刷的可能。我们最严重的那次事故,就是在这种情况下产生的。 所以说,如果能给数据存储这块,做个事务机制,肯定是好事。但是,像异步存储、事务机制这些,也只不过是解决了一部分数据库已经解决了的问题。我们要做的,应该是用一个成熟的数据库。

类似的例子其实还有很多。有时候我在这里做服务端技术这块,会陷入一种困境,到底要不要去做。 因为我心里面非常清楚,很多东西做了,只不过是解决一些别的工具已经解决了的问题。就像现在已经有了Nginx这种高性能服务器来处理高并发的应用场景,为什么还要拿个Apache来改造,去优化,去解决一些Nginx已经解决好的问题。 所以更多的时候,我宁愿做好需求,做好业务这块,带给玩家更多的游戏内容和更好的游戏体验。基于mudos+LPC这套,有东西可以做,但看不到价值所在。

今年技术方面的工作,计划逐步往客户端发展,做一个多面手。去年虽然也有做一部分客户端的工作,但相对比较皮毛,量也不够,达不到技术积累的程度。整个IT行业的发展,迭代更新得很快。 近10年来,既有像Google、苹果这样的千亿俱乐部公司起来了,也有像诺基亚、摩托罗拉这样的昔日巨人倒下了。 在这个行业,没有什么是永恒的。所以对于处于这个行业的我们,最需要的是保持危机感和饥饿感。我经常思考的问题,不是产品成功后,我能得到什么,而是产品万一失败了,我能做什么。 引用乔布斯的话,“stay hungry, stay foolish”,持续不断的学习新的知识,提高自己的能力,不管是技术上,还是业务上。 去年我在技术学习这个方面是做得不够的,需要检讨。业务繁忙是其中一个原因,还有一个比较重要的原因是服务端技术选型上的限制。 所以学习客户端技术,一方面是保持自己技术进步的空间,另一方面也是为团队储备技术力量,能用的时候随时能够上。

管理方面的工作,没什么具体计划。我和一年前来的时候一样,心里面的想法没有变过。喜欢小团队,喜欢相对平坦的管理模式,喜欢团队的每个成员都能独当一面,能自己管理好自己。 大家既有自己的个人目标,也有团队共同的目标——做好产品。我当初选择手游团队,也是因为手游团队更像创业团队。 团队规模小,但每个人都很能干,单兵作战能力很强,能在消耗较低成本的条件下创造巨大的价值。 所以我觉得在这样的团队里,最重要的,首先是做好自己,以身作则,用自己的行动带动每一位成员,激励大家共同努力。担当起标杆,起到凝聚力的作用,让大家朝着共同目标努力。 其次,深入倾听每一位成员的想法,在条件允许的情况下,分配给每个人最适合的工作,让每个人都能发挥所长,有足够的成长和发展空间。后面这一点,是我后面需要努力的。

失败的产品背后,没有成功的个人;而成功的产品背后,每一位为之付出过努力的人,都是英雄。期望接下来能继续创造出更多成功的产品。


2015年1月