听《陌陌移动开发技术分享》有感

听陌陌CTO李志威同学讲陌陌的技术发展历程,有一些启发和思考以记之。

(1)前期技术选型时,尽量用业界已有的通用和成熟技术,以避免自己闭门创车和重复发明轮子,这对于一个创业团队尤为重要!
(2)Redis+MongoDB的存储系统,为后续灵活扩容和性能支撑提供保障;
(3)选用“云”主机,可以避免前期没有专业运维人员和DBA的烦恼;
(4)用扛住,后优化的思维;
(5)衡量代码质量的中心指标是代码的可读性和可维护性,以及设计是否够简单;
(6)自动监控和报警是运维必不可少的环节;
(7)一键部署与持续集成工具的运用;
(8)移动化的运维,即用手机要查看服务器的各种监控信息,如:Redis队列,各类用户统计数据等等,个人认为这是运维工作的一个很好创新;
(9)应用supervisor来管理所有服务器后台进程;
(10)用“重启”这种看似偷懒的做法,来解决部分实际中遇到的问题;
(11)使用Ganglia来监控服务器集群的各项性能指标,如:cpu 、mem、硬盘利用率, I/O负载、网络流量等;
(12)使用fabric来管理批处理任务,以方便自动化部署;
(13)使用puppet自动化配置和部署系统;
(14)一台新服务器自动上架和部署的全过程如下:
     1)供应商送到机房,按我们的布线方案上架
     2)使用远程装机,10分钟
     3)使用Puppet自动同步一切配置
     4)使用yum安装自定义配置打包的服务
     5)使用Supervisor管理所有服务进程
     6)加入到Nagios、Ganglia、自定义监控
(15)工程师移动运维时所做的工作如下:
     1)无聊时查看最新的用户注册情况、系统状态
     2)手机上线代码
     3)上线服务后通过手机查看数据、队列、服务器负载是否正常
     4)户外用手机重启服务

WebGame游戏服务器技术要点

(一)服务器技术实现方案选择的问题?

(1)选择标准WebServer+CGI开发(偏轻)
【优点】
    1)快速开发:WebServer都是现成的,如:Nginx和Apache;CGI程序能也快速编程;
    2)灵活开发:整个后台服务可由多个CGI程序组成,一个CGI程序可以对应一项或多项业务功能,增加新功能时则增加新的CGI程序即可;
    3)不停机更新:部署新的CGI程序,不影响已运行的CGI程序;
    4)可适合处理SNS类玩家异步(离线)交互数据的情形;
【不足】
    1)CGI程序如果过多,可能会加大维护的难度;
    2)业务需求如果要涉及到多个CGI程序之间的通信,即有状态的关联依赖,可能会有较复杂的逻辑和可能的性能瓶;
(2)选择自定义通信服务器+GameSvr开发(偏重)
【优点】
    1)可应对复杂业务逻辑的处理,如:业务需求之间的关联依赖较深等;
    2)可轻松处理玩家之间同步数据的需求;
【不足】
    1)开发复杂度较大,需要较多的开发资源;

(二)游戏服务器Cache设计方案

【背景】
    1)游戏服务器cache数据可大大缓解数据存取时压力;
    2)多台分布式游戏服务器作Cache,可有效利用硬件资源;
【实现方案】
    1)基于共享内存的对象池设计;
【注意事项】
    1)数据一致性:对于SNS强交互类游戏,需要作多玩家修改同一数据的设计;
    2)数据同步的时机:根据业务数据的重要程度,分级设计同步时间,越重要的数据,同步时间越短;

(三)游戏服务器数据分类设计方案

【基本思想】
    把业务数据按重要程度进行分类,每类数据采用不同的存储和逻辑处理策略;
 
【应用说明】
    SNS类游戏需要重点考虑

(四)游戏服务器全局锁的设计

【背景】
    1)同一玩家数据会被多玩家修改;
    2)某一Cache数据会被多个应用修改;
【方案一】:热点数据分布在各个游戏服务器的Cache中
    1)数据被修改点(热点数据)设置一个全局Seq,如:玩家数据修改,则在玩家身上设置一个Seq;
    2)当Client读热点数据时,则直接返回数据给Client,并将Seq一齐带上;
    3)当Client写热点数据时,会在写数据请求里带上上次请求获得的该热点数据的Seq,热点数据的处理逻辑先比较写数据请求的Seq,是否与当前热点数据的Seq一致,若不一致(该请求来之前已被其它Client修改过),则有如下两种异步处理方案:
        1. 直接报错给请求Client,此时Client可能需要提示玩家重新刷新浏览器;
        2. 返回当前最新热点数据给请求Client,Client需要重新处理一遍之前的业务逻辑,再提交写数据请求;
    4)每次写热点数据成功后,热点数据本身的Seq加1;
    5)数据修改的逻辑统一在热点数据所在的游戏服务器处理;
【方案二】:热点数据集中在一个全局公共服务器的Cache中
    实现方法与【方案一】类似
【两个方案的比较】
    1)【方案一】:Cache分布在每台游戏服务器中,可以有效利用硬件资源,但SS交互逻辑复杂一些;
    2)【方案二】:可以减少一些SS协议的交互,但需要单独的全局Cache服务器;
【注意事项】
    1)需要分别考虑玩家在线和离线数据的修改;

 (五)如何有效利用机器的多核CPU

1)多个通信服务器+1个游戏服务器;

    2)多个通信服务器+多个游戏服务器;
 

(六)如何评估网络I/O的瓶颈

    1)网卡(1000M bit/100M bit)本身的极限处理能力:理论处理包的数量:网卡带宽[1000M bit]/(最小包大小[64Byte] * 8);
    2)游戏业务对包的数量较包的大小更敏感,因为每个网络包在CPU处理时,会产生IO中断,因此,常用的策略是,合并多个请求包为一个包,提高带个包的信息量;
 

(七)深入了解MySQL的存储性能

    1)Mysql5.5+InnoDB1.1;
    2)Mysql的Cache解决方案;
【声明:若有摘录,请注明来自http://gameislife.info/archives/109】

Social Game与MMORPG在服务器架构上的差异

最近在作公司的一个Social Game的项目服务器架构设计,与之前做过的MMORPG(简称RPG)相比,差别还是较为明显的,现总结一二,以供分享!

(一)协议通信

1)Socail Game为了快速开发,在通信协议的选择上均会选择http作为底层通信协议,这样选择的好处大致有:

1、方便客户端编程:http为一应一答的同步模型;

2、可以选择成熟的开源http服务器,如:apache、nginx;

2)RPG选择的是基于socket的TCP通信,这是由游戏本身的特点所决定的,如:聊天、多人视野、服务器主动通知事件等需求

(二)游戏逻辑服务器的承受能力

1)RPG的游戏逻辑服务器(地图服务器)所能承载的最高在线(PCU)是在3000-5000不等;

2)Social Game由于没有RPG复杂的移动、战斗、视野管理等需求,逻辑服务器承受的在线一般都是比较高的,如:10000-30000不等

(三)游戏逻辑服务器的Cache和回写机制

1)RPG的游戏逻辑服务器一般都需要Cache玩家数据,并且采取定时回写的策略,如根据数据重要程度分别作5min-15min不等的定时回写;

2)Social Game的游戏逻辑服务器一般都无需Cache玩家数据,玩家的每次请求都是即时读即时写(这样会涉及到另外一个问题,即DB读写的性能问题,见下一条);

(四)DB存储模型的选择

1)RPG存储服务器常用的还是MySQL+innodb,中间还由业务自己写一个Cache代理服务器,以Cache热点数据;

2)Social Game则会选用TC、MemCached等所谓Key-Value全内存数据存储,有实力的公司会自己实现一个类似这种机制的存储系统,它可以无源,也可以是有源,并且还可以选择用MySQL作数据落地,毕竟MySQL作为互联网业务DB的成熟解决方案,已毋庸置疑;

(注:我们公司是选择自己开发基于key-value机制的全内存DB,现网测试的数据是平均1KB的数据可以分别达到5w左右的读/写,还是很牛X的了)

(五)交互数据的一致性

1)在SNS Game中,会经常出现同一个玩家在某一个时刻同时被多个好友访问和修改数据的情况,这时就需要保证,每次数据的更新都是正常有序进行的,而不能被写脏数据。一般地,都会使用一个类似全局锁服务的设计来解决这个问题;

2)RPG不会存在这样的问题,类似的需求是:玩家可能会跨地图服务器,即所谓的跳线或跨服,这个问题的通常解决方案是服务器重新作一次下线、重登录的处理,当然,玩家是感觉不到的;

(六)IDC部署

1)RPG一般是分区分服部署;

2)Social Game则是全区全服部署;

呵,先写这些多,后面再慢慢补充,也欢迎同行朋友拍砖!:)

Intel多核编程培训小结

这两天参加公司引进的外部课程《Intel多核编程》的培训,邀请的是Intel中国的两位专家,其中一位是《软件调试》的作者张银奎老师,现将基本内容小结如下,以便自己日后回顾。

(1)多核编程基本理念:任务分解,常用方法是给不同的子任务进行编号,然后,将线程各个子任务一一映射;

(2)编译器选项对程序性能优化的意义:一般在release版本中,建议选择-O2选项;

(3)Intel编译器特有编译优化功能:PGO;

(4)在Intel x86性能优化常用方法:自动并行化,自动向量化;

(5)windows多线程编程基本知识回顾,重点介绍了线程同步对象关键区、互斥量、信号量、事件;

(6)关键区Entercritticalsection和Leavecriticalsection之间不要抛异常或return,否则会造成死锁,要求这之间的代码尽可能简单;

(7)信号量一般用于多个线程对象的同步,即线程资源数往往大于1时,如:线程池中控制线程的分配同步,而互斥量和关键区均只用于单个资源的线程同步;

(9)关键区在作线程同步时,不进入到系统内核态,所以,其同步效率较信号量与互斥量要高,而对于某个临界资源数值(如:全局变量)进行加、减操作,则推荐用Interlock的系列函数作同步,性能最高;

(10)OpenMP编程基本知识讲解与练习,初步感觉,OpenMP编程简单易学,功能强大;

(11)Intel的性能优化工具VTune使用练习;

(12)Intel的线程检查工具thread chcker讲解与练习;

关于游戏服务器性能问题的几点思考(2)

工作中对项目压力测试的一些心得,先自我作一个小结吧!

(一)宏观与微观相结合
  (1)宏观层面
       即系统的一些关键性能指标,如:各进程所占CPU的百分比、内存消耗、网络包量、磁盘IO等等,详细指标列举如下:
名称 描述 参考值
CPU useage CPU 的使用时间百分比。 平均值小于70%
Process virtual memery size 进程使用的内存空间总量,包括物理内存和swap内存 进程长时间运行后该值不能大幅度的改变,否则是内存泄露
Disk rate 磁盘传输速率 一般少于2M/s, 日志级别太低时硬盘io会是瓶颈。
Bytes trans rate 网络发送速率 少于200Mbps
Bytes receive rate 网络接收速率 少于200Mbps
Pages swap in 每秒钟读入到物理内存中的页数 长期大于0表示物理内存不足
Pages swap out 每秒钟写入页面文件页数 参考上面
Context switches rate 每秒钟在进程或线程之间的切换率。 少于5000*cpu个数
Interrupt rate 每秒内的设备中断数。该指标代表了本地向CPU引起的本地中断,例如IO端口引起中断,系统时钟引起中断。 一个巨大的中断值,同时伴随着缓慢的系统性能表现,指示存在硬件问题。
    测试工具:nmon
  (2)微观层面
       这里是指具体到Server程序的逻辑功能模块,包括函数消耗CPU周期、函数调用次数等资源占用情况,以及系统内核哪一部分最忙等。
       测试工具:oprofile、gprof
 
(二)黑盒与白盒相结合
  (1)黑盒测试
       我们目前的压力测试程序,其实是归于黑盒测试范畴的,它模拟玩家的一些行为,应用具体项目的实际协议与被测游戏Server通讯,通过同时产生大规模机器人,来模拟与实际运营中相似的场景。这里编写的测试用例,其功能就是要尽可能真实地模拟client的功能,并能方便的配置化和部署client行为;
  (2)白盒测试
       我理解的白盒测试包括两部分,一是单元测试,它能有效地解决BUG回归测试的问题,二是代码覆盖率检查,它能有效检查到每个函数分支的执行情况。前者可借助业界成熟的自动化测试框架,如:cppunit、gtest;后者也有许多第三方工具,比较方便的有GNU自带的gcov,只要在编译程序时,加入-fprofile-arcs -ftest-coverage编译选项即可。
   如果要用一句话来概括两者的话,那就是:
  白盒测试能极大的保障程序逻辑功能层面的正确性(正常与异常流程均能检测到),而黑盒测试则能有效保障程序运行的稳定性。

关于游戏服务器性能问题的几点思考(1)

对于一个服务器程序来说,性能指标无非有CPU负载、内存消耗、I/O回写压力、网络包的流量等,目前测试组的测试都是在公司内网来做的,所以,网络包的流量不是瓶颈,但从测试的结果来说,这块恰好是我们的短板。我在想,有几个地方可能是我们可以优化的地方:

(1)字节序的转换问题:游戏服务器的内部架构与部署一般有二层或三层,即一个游戏大区包含若干台游戏逻辑服务器。在实际运营时,这些物理机器一般都是x86的,这样网络消息包在服务器内部进行传递时,可以省掉字节序转换这个步骤了,而经测试证明,这步往往消耗较大的CPU;

(2)消息包的加解包与内存拷贝问题:服务器程序在发送消息包时,尽量减少不必要的内存拷贝,能通引用填充的尽量应用同一内存空间引用来完成;

(3)视野广播消息的定制问题:广播消息包是比较消耗下行流量的,所以可以根据实际业务需求来定制广播包的数量,而不要统一全部广播玩家视野里所有对象,因为这样会造成下发无谓的消息包,比如:玩家视野里所有的怪物是不需要发送任何消息包的,另外,视野中的某些特定的玩家也不需要收到广播包;

先总结这几点吧!

游戏服务器开发经验小结

(1)为每一个存放在共享内存的对象,编写Init()、Resume()、Reload()函数;

(2)服务器进程重启进行共享内存恢复时,谨记恢复存放在共享内存中的对象数据的同时,也不要忘了同时恢复指向共享内存数据的指针;

(3)用gcc编译时,选择-Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers选项,并试图消除所有告警;

(4)尽管使存放在共享内存中的数据不要为指针或引用,以免共享内存恢复数据时再来额外的开发复杂度;

(5)尽量多开发一些GM指令,以便于黑盒测试;

(6)仔细查看每一个ERROR级别(游戏运营时的日志级别)的日志内容是否丰富,以便于游戏运营期间查找可能出现的程序异常;

(7)主动严重关注运营中游戏服务器的每一条ERROR级别日志,认真分析程序可能存在的隐患,而不要等到客服报告玩家的投诉时再查;

(8)通过分析和解决错误日志内容,来加强对程序逻辑的理解;

另外,还有一些通用准则:

(1)技术的东西,永远不要一知半解;

(2)和代码交朋友,认真对待每行代码;

(3)精益求精,用苛刻的眼光对待自己写的代码;

(4)碰到问题,永远不要拖延,按事情的轻重缓急,力争早点解决;

 

思考游戏服务器的tick机制

游戏服务器程序内部都会统一设计所谓的tick机制,这个机制一般来说有两个用途,一是许多业务模块都会有定时处理的需求,如:技能Buff计时、时间道具检查、定时给玩家收益(打坐定时回血和蓝,跳舞定时回经验等)等等;二是游戏服务器内部会有一些日常主动驱动的事件,如:怪物移动、玩家移动等,这些需求都需要用到这个tick处理,夸张一点说,游戏服务器业务逻辑的处理绝大部分都会有涉及到tick处理的需求。

那这个相当重要的tick机制是如何实现的呢?目前我们采用的是在主程序大循环中,每一次循环取一次当前系统时间并保留上次进入tick处理时的时间,然后拿这两个时间相比较,若大于tick设定的间隔时间(如:200ms),则进入tick函数处理。这个tick函数中,注册了系统所有业务模块需要定时处理的接口。于是,在大循环中每一次tick调用,就会将系统所有定时处理的接口都处理一遍。于是,问题便也随之而来了… …

显而易见的问题是,所有的定时处理都放在一起调用,这会造成CPU的峰值瞬间拉高,并且也会造成server消息处理的能力下降(因为tick处理的这段时间,本质上是可以作为server对消息的延时处理时间),对于这个问题,我们也有显而易见的处理方法,即将各个业务模块的定时时间进行分类,大致可以分为200ms、500ms、1s、3s、20s、30s、60s等适合各业务需求的定时间隔,这样每一次tick调用时,就不会同时有许多模块一齐处理了,这可以在一定程度上平滑CPU的处理曲线。但也无法避免会在某个时间点,同时有许多(甚至所有)业务模块定时处理的情况,所以,我在想,是否还有其它更好的处理办法呢?

首先想到的是,是将各个业务模块的定时间隔进行更精确和合理的分类,比较理想的情况是:使各个定时间隔互不包含,即各个间隔之间不要存在整除的关系,如:300ms、500ms、1100ms等,这个可以做一个测试,看一下这样修改后,CPU的处理是否更平滑一些。

由于在linux下中,系统定时器是由SIGALRM信号来实现的,而一般游戏server为了稳定等原因,都屏蔽了linux的许多信号操作,所以,一般就采用自定义的定时器来实现tick机制了,只是我们的这种机制是否还有更好的实现方式呢?这的确是一个值得思考的问题,毕竟,tick处理在游戏server中占了相当大的比重。

当然,只有让每一个业务模块,每一次tick处理的时间尽可能缩小到最短,那么,tick处理对于CPU的消耗就会减轻到最小了!