和很多新手程序员一样,进到一个公司工作,主要是做业务逻辑。游戏的业务逻辑大体上分两类,单人玩法和多人玩法。单人玩法例如收发邮件,背包操作,多人玩法例如组队匹配。 逻辑层因为是单线程,所以做单人玩法就很简单,按照文档的逻辑实现即可,而多人玩法涉及先后顺序,就需要通过一些变量做标记(锁)(因为是单线程,对标记的操作不需要考虑竞争).
我自己写功能逻辑的流程是:
1. 通读文档一次,看文档逻辑是否有问题,现有框架是否可以解决,是否有性能上的问题。
2. 画思维导图,从内存数据结构,Db格式,C/S交互以及功能逻辑要点,配置需求这几个点去写,如果逻辑比较复杂,可以用visio。
3. 具体的逻辑编码。
4. 拿CheckList自测以及代码优化。 从是否存在刷道具情况(尤其是有循环发奖代码), 数据落地再Load出来后是否一致, 如果需要比较大的测试用例,则自己写机器人覆盖用例去测试,涉及到读频率远远大于写的数据(例如个人属性战力),会做cache。 一些排序的耗时操作是否可以让前端自己来做(复杂的计算尽量不在后端做,尽量做到大部分是O(1)复杂度的操作).
5. 提交QA测试。
大概写了几个逻辑模块后,应该是上手了。 这时候一般的逻辑模块实现起来没难度了。 这时候就可以去看一些比较核心的逻辑系统实现,譬如任务系统的框架(新增任务是否需要策划来配置即可),属性系统的框架(新加属性是否需要加代码),这些好的框架原则上是可扩展可配置的,也就是只需要策划负责就OK了。 但好框架需要策划和程序根据需求一起构思,缺一不可。 比较底层且核心的背包系统的实现,关注点有: 1)对其他系统暴露的api是否合适(增删查); 2).道具增加过程如果满了改成用别的途径发放。 3).删道具要保证原子性,要么可以删完,要么一个都不删。 关注点是系统的健壮性。
大概把任务属性背包这些非场景相关功能了解后,逻辑功能开发算是入门了。然后就可以去了解核心的场景相关模块。
场景相关模块主要是AOI和同步策略,AOI最通用的做法就是九宫格,即将场景划分成很多个边长为r的正方形格子,每个对象只关注自己所在的九宫格发生的事件(例如玩家移动,战斗技能释放),从一个九宫格A移动到另外一个九宫格B,则A-B(A有B无的格子)被告知该玩家离开了AOI范围,A and B(AB相关的格子)被告知该玩家移动了,B-A(B有A无的事件),则被告知该玩家加入了AOI范围。而同步策略主要考虑网络延时的影响,这个没接触过就不多说了。
以上逻辑系统都熟悉之后,就可以去看底层代码了。
把代码流程都看懂之后,就可以往性能方面去思考了。
http://blog.sina.com.cn/s/blog_72995dcc0102uzdg.html 提到影响服务器高性能的四大来源,
1. 数据拷贝. 譬如数据从TCP层拿到应用层后,往往要进行粘包拆分检查,数据格式化组装然后发送到应用进行处理的过程。但数据拷贝更多是发生在逻辑代码中(例如函数使用值传递而不是引用),时刻提醒自己能用引用绝不用拷贝。
2. 上下文切换. 限制活跃线程数少于或等于CPU个数,例如目前Linux下都是用Epoll去管理多连接,而不是Apache的一个连接一个进程。
3. 内存分配. 比较好的做法是内存池,例如Stl的Allocator就是这样做的,容器释放的内存被Allcator回收到一个free_list,等容器需要分配内存时就先到free_list去拿,内存不足再向系统申请内存。 比较成熟的库有 tcmalloc,直接用吧。
4. 锁竞争。 这个主要是针对多线程应用的。 涉及锁的一个原则是: 两个请求不应该产生竞争,除非它们在同一个阶段需要同样的数据集。根据这个原则不断去去掉不必要的锁。 还有一个是我自己的体会,不确定是否正确。 如果是I/O密集型的应用,推荐使用CAS锁,而计算密集型的应用则可以使用Mutex。 应为I/O密集型的应用计算量小,一般占用锁后去处理事件的时间不会长,使用CAS锁的一个特点是会一直询问锁是否可用,这个过程不会释放线程,这就不会产生上下文切换。 只要询问的时间代价
比上下文切换的代价小,必然选用CAS锁。 而Mutex则会释放线程的占用,直到锁可用之后才会占用线程继续操作。
熟悉代码方法:
1. 熟悉服务器启停方法,了解所需的进程数目以及其具体的作用.
2. 了解RPC框架(目前接触的两个游戏都是使用rpc通信),熟悉数据流。 对于RPC框架,需要了解几个部件。
①. 通讯的方式,是socket,共享内存还是管道,面向客户端的通讯大多数是采用TCP,而服务器内部服务之间的通讯则不一,单机下架构可以选择共享内存或者socket,分布式架构则必须socket。如果是socket,则再要深究是长连接,对于游戏服务器,必然是长连接。
②. 如何寻址,即A服务怎样告诉RPC框架,如何连接到B服务,方法名称是什么。 我只接触过socket通讯,最简单的方法就是写一份配置,所有服务共享这份配置,从配置中读出所有启动服务的ip和端口,然后告诉rpc框架发起tcp连接即可。建立连接后的通讯寻址就简单多了。
③. 发起rpc调用,方法和参数需要通过网络底层协议如TCP发送到另外一个服务,网络协议是基于二进制的,这些在内存的方法和参数就要序列化成二进制形式,然后发送。 对应的,接收端要对这些二进制信息进行反序列化,转换成内存可以表达的形式。 序列化工具比较有名的就是protocol buffer。
④. 第四步从结果看是数据从一个服务发送到另外一个服务了,实际上在数据进入网络层传输前后,还需要经过一系列的调度,也就是消息调度的算法,才能对消息进行发收。 网上开源的有Zeromq,Kafka,游戏相关的话可以了解云风开源的skynet或者是其精简版hive,基于Actor模型。 这一步很关键。
⑤. 了解网络模型,目前Linux下一般是采用epoll,这个只要网上多查几个示例就大概知道原理了。
⑥. 了解db和缓存的接口和用法。
7. 以上的东西都了解后,就可以自己写个demo去测试一下了,然后就可以去了解业务逻辑,
开始愉快的搬砖之旅了O(∩_∩)O.