[PPT No.1]
大家下午好,现在我来跟大家分享的是PHP在金山游戏运营中的应用,包括团队协助开发实现方式、网站Web架构设计、游戏运营平台设计这些信息。
[PPT No.2]
我议题主要有两个,一个是的一些应用,另一个是金山游戏运营系统Keyes中的架构设计。
[PPT No.3]
金山游戏官方网站包括、、、、各游戏,以及其他跟游戏相关的一些产品,主要采用64位CentOS Linux系统、Nginx、PHP 5.2版本、MySQL 5.5。
[PPT No.4]
首先来看团队协作开发。我们肯定遇到过这样一种情况,在很多项目中,都是多个人同时开发,涉及到开发环境和测试环境不一样。我们很多PHP工程师,都是在Windows上开发代码,虽然Windows上也可以配置Nginx+PHP+MySQL环境,但是,由于测试环境、生产环境都是Linux系统,而且一些功能只能在Linux下运行,还有一些PHP扩展(例如:分布式图片处理、金山通行证加密扩展),也只能运行在Linux环境中。当我们在Windows上修改完几行PHP代码,想马上看一下执行结果,如果利用FTP之类的工具传到Linux测试服务器上再测试,就太慢了。如果同一台Linux测试服务器上,有多少人同时开发,你上传上去PHP文件,可能会覆盖别人上传的同名文件,就没有办法做到版本控制。
[PPT No.5]
我们从图中可以看到,假如是程序员A和B都在Windows上开发代码,由于Nginx与PHP之间采用的是TCP FastCGI协议通讯,因此,两者可以分离到不同的服务器上。我们可以把Nginx安装在程序员各自的Windows PC机上,用本机的Nginx处理HTTP请求,用Linux测试服务器上的php-cgi程序,处理PHP请求。程序员在Windows上开发程序,保存之后,不用做任何上传操作,即可用Linux上的php-cgi调试程序。从图中这个流程可以看到,首先,两个程序员分别从SVN版本库,获取到一个项目的最新版本,各自进行一些修改。两人修改程序时,采用的是同一台Linux测试服务器的php-cgi,对各自PC机上的PHP程序进行调试。在PC机上本地测试没有问题,可以提交到SVN版本库。我们做了一个自动同步程序,利用SVN钩子,在每次发生svn commit提交时,在对应的测试服务器的对应项目路径内,执行svn update,将最新修改到文件同步到测试服务器。后来发现有一些问题,如果我们一个项目的目录、程序文件特别多的话,svn update需要遍历扫描目录列表,非常慢。因为我们的SVN是和Apache结合起来使用的,Apache可以记录日志,于是,我们进行了改进,将SVN提交日志记录到Linux下的命名管道内,再用一个程序从命名管道内读取日志,只svn update每次修改的几个文件,这样,速度就非常快了。设置hosts为Linux测试服务器的IP,就可以测试多位程序员代码合并后的效果了。
[PPT No.6]
为了方便程序员快速配置这套开发环境,我们开发了一个工具软件XDevelop,来自动做这件事。我们在XDevelop软件中添加一个虚拟主机,添加完成之后,XDevelop软件会将该虚拟主机的目录设置为共享文件夹,再利用SSH协议连接到Linux测试服务器上,将Windows上的共享文件夹自动挂载到Linux服务器上。
[PPT No.7]
设置hosts为本机IP,在自己电脑上,修改完一个文件,本地调试没有问题之后就可以提交。
[PPT No.8]
在Windows编写代码PHP代码文件,用Linux环境的PHP来调试,保证开发环境、测试环境生成环境统一。整个PHP开发环境、测试环境,即保证了程序员的快速修改,调式代码需求,又保证整个代码在SVN版本控制之中。
[PPT No.9]
我们在开发过程中,还会遇到这样一个问题,PHP代码统一性保证问题。从“开发环境”到“线下测试环境”,线下测试完之后再到“线上测试环境”,最终到“正式生产环境”,在这么多种环境中,配置文件会各不同。另外,我们的程序员开发、维护的项目比较多,大概有10多个项目。这些项目连接的MySQL、Memcached、TT、存储、接口、消息队列的IP、端口、用户名、密码各不相同,程序员经常搞错,或者一团雾水。谁最了解各个项目的服务器配置?自然是系统工程师。这样,就完全可以把二者进行分开,让最熟悉这部分业务的人去做最擅长的事。
[PPT No.10]
于是,我们开发了一个PHP扩展,提供了一个kae_config()函数,还开发了一个kae-config Web管理后台,用来为不同的项目配置各种服务的IP、端口等信息。这样,我们的PHP配置文件,能够保证在各个环境中,都是一样的,整个PHP代码在不同环境并无差异。我们在每个服务器上通过个后台进行发布,需要做的只是系统工程师在不同环境,对不同服务器系统,配置不同参数,不同IP端口。
[PPT No.11]
PHP程序进行统一之后,就涉及到PHP代码上线发布。我不知道其他公司是怎么做的,我们对于上线发布的权限,交给每个项目开发负责人去做,把系统工程师从代码发布中解放出来。从上线发布版本,假如我要发布一个项目的2.1.46版本,就将其对应上它的SVN版本号21901,当前版本和上个版本,有那些文件增删改,我们可以通过SVN的变化获取出来。发布代码的时候,我们会作相应的操作。
[PPT No.12]
基于此理念,我们开发了一个“代码发布系统”,从图中两个标红方框的地方可以看到,我们新增一个发布版本,会新增一个版本号,根据修改内容的多少,分为大版本、中版本、小版本,可以根据权限或功能点,由不同的负责人去进行上线发布。每次上线发布,需要提交跟这次发布相关的描述信息,便于将来查看。
[PPT No.13]
从右边按纽可以看到,我们可以启用发布过的每一个版本。如果我们发现某个版本上线之后,如果有重大问题,需要较长时间才能解决,可以一点击按钮,马上回滚到上一个版本。通过对每次上线的内容进行版本控制,使得整个版本发布过程一目了然。
[PPT No.14]
下面来看以下PHP与开源产品、C/C++程序的结合。我们主要采用两种方式,方式一:利用PHP客户端扩展,通过TCP协议与C/C++开源程序进行通讯,例如与Sphinx搜索、分布式图片处理、存储系统等等。
[PPT No.15]
方式二:我们开发了一些基于HTTP协议的C/C++程序,例如开源的简单消息队列软件HTTPSQS,利用HTTP协议实现与PHP的通讯。
[PPT No.16]
Web程序的特征,要求对每次请求的处理都要非常快,处理速度需要在毫秒级解决。我们也遇到一些服务,比如发送手机短信、发送邮件可能需要几十秒时间,如果让用户在前端页面等待,对用户体验非常不好。还有上传视频,我们需要对视频格式进行转换,添加用于支持拖拽的关键帧。我们进行数据挖掘,需要记录用户的一些操作步骤日志,这些都是耗时的操作,需要异步处理。
[PPT No.17]
解决异步处理问题,我们也是分为两种情况。一种是短耗时异步处理,耗时1至2秒钟时间,记录一些数据挖掘信息。这些功能耗费的时间相对比较短,这时候我们可以采用PHP-FPM提供fastcgi-finish-reques函数来解决。从调用这个函数之后的执行结果,用户不会去等待,这样可以实现一些功能的异步处理。
[PPT No.18]
fastcgi-finish-request函数也有缺点:我们通常在服务器上开启的PHP FastCGI进程数有限,正在处理异步操作的PHP-CGI进程,无法再处理新请求。如果并发访问量较大,php-cgi进程数用满,新访问请求,将没有空闲的php-cgi进程去处理,在Nginx服务器上,会表现为出现502 Bad Gateway错误码。
[PPT No.19]
一些耗时稍长的异步处理,我们采取队列方式进行解决。这样的话,在我们的产品应用中,例如图中提到的发送短信、邮件、视频转换、图片处理、刷新多台服务器的缓存等功能,我们可以先入队列,最后用另一组程序,从队列中读取信息,进行异步处理。
[PPT No.20]
我们的PHP Web服务器采用的是负载均衡集群池方式,从这个架构图,是一个进行简化后的游戏官网架构。最前端,我们采用两台Nginx服务器来实现负载均衡,在其之后,拥有一组通用Web服务器池,运行用户中心、密保卡、客服系统等一些服务。对于一些存在流量能够突发激增5至10倍的业务,例如论坛(游戏临时维护时,越大部分玩家都会转向访问论坛)、游戏活动(整点抢号、秒杀等),为它们各自单独划分一组PHP Web服务器。Web服务器,又会去连接MySQL数据库、Key-Value数据库、消息队列、搜索引擎、存储系统、分布式图片处理等服务。对于游戏官网的静态站点、图片、视频等,我们在两台Nginx负载均衡服务器之上,采用CDN进行缓存。
[PPT No.21]
我们的每一组Web服务器池,有多台WEB服务器,因此,我们开发PHP程序的时候也需要考虑到这个问题。例如一些PHP页面可以静态化,但是,如果采用PHP程序在本地服务器生成HTML静态文件的方式,可能在这台服务器生成了文件,在那台服务器上没有生成,多台服务器访问就会有问题了,将来HTML静态文件的清理,也会比较麻烦。还有一些程序员,喜欢将一些对象数据缓存在文件中,这会导致生成很多小缓存文件,而又没有LRU机制去清理,大量小文件会占用大量的磁盘Inode节点,有时候磁盘使用空间剩余很大,但是没有办法再新建文件了,因为大量的小缓存文件,把磁盘Inode用光了。对于数据对象缓存,我们采用APC、Memcached缓存来代替文件缓存。对于HTML输出网页缓存,我们采用前端的Nginx负载均衡,来做缓存。Nginx负载均衡现在也可以支持缓存,把后端PHP输出的页面,缓存起来。这样做,有一个优点,WEB服务器上,PHP文件能够通过代码发布系统统一管理,在集群池中添加、去除一台WEB服务器,非常快捷,清除缓存,能够由系统工程师去统一管理。另外一点,涉及到代码防篡改,我们在php-cgi中增加一个程序执行预判断机制,万一有人利用PHP程序漏洞,上传PHP木马程序到服务器,或者修改了原有的PHP程序,这个程序将不会被php-cgi执行。因为我们的PHP程序都是通过“代码发布系统”发布的,如果不是通过我们“代码发布系统”发布的程序,预判段机制会识别出来,从而拒绝执行,同时发报警短信给系统工程师,提醒有人试图入侵。
[PPT No.22]
一些登录操作、验证码,需要保存Session会话。在多台Web服务器情况下,我们采用两种方式解决:1、在Nginx负载均衡服务器上做IP哈希,根据用户IP的不同,将不同用户分到固定的Web服务器上;2、修改PHP Web服务器上的php.ini,利用Memcached做Session共享。
。
[PPT No.23]
接下来,介绍PHP在金山游戏运营系统Keyes中的应用。这是一个与多款网络游戏服务端进行数据指令通讯,并可以控制游戏服务端集群启动、停止、程序更新、非停机更新、心跳检测的平台化产品。这是我们设计的一个架构,为界面、接口和支撑三层架构设计。现在有很多项目,API接口只是给别的项目调用的,自己网站本身,还是直接与数据库打交道,并不调用自己的API接口。在我们这套架构中,界面部分不存储任何数据,管理界面只实现管理界面的逻辑,不连接数据库,对数据的操作完全调用API接口。用PHP开发的API接口,来处理具体的数据,不但可以供自己调用,也可以供例如“经营分析系统”的一些其他系统调用。在游戏服务器上,运行着我们用C语言开发的一 个Knose守护进程。我们知道,PHP不支持多线程、线程池,无法并发处理。所以,我们利用这个C程序,将PHP发出的串行指令,变成并发指令,向多个游戏服务端进程同时发送,比如查询在线人数功能。我们又能利用这个C程序,控制通讯顺序,比如停服,根据进程类型不同,是有顺序要求的。右边这个部分是属于一个游戏区服中的一台游戏服务器,左边是运营服务器。我们在每一台游戏服务器上,安装一个Knose守护进程,通过HTTPS/SSL加密协议,与远程的PHP接口进行数据传输。我们的Knose程序可以监控游戏进程是否存在,与这个进程的通讯是否正常。可能有这样一种情况,一个是游戏进程存在,但是游戏进程僵死了,这时候我们通过Knose定时向游戏进程发TCP心跳包,检测游戏进程是否存活。
[PPT No.24]
通过PHP Web程序,去管理游戏服务器,涉及到一个时间问题。因为Web程序的特征,要求处理时间非常短,例如发送一个停服指令,游戏进程需要断开与其他系统的一些连接,保存玩家数据,完成整个过程才能把游戏服务端停掉。如果在线玩家人数比较多,保存数据需要花费几十秒到1分钟。PHP程序不能异步等待,这样,我们在设计的时候,从PHP管理界面,发起一个指令到PHP功能接口,PHP功能接口再转发给Knose,整个请求就完成,在Knose处理完指令后,再作为客户端,发起HTTPS请求回调PHP功能接口,这样的话就变成一个异步过程。
[PPT No.25]
数据交互过程,分为四种类别。一是运营指令,涉及到修改经验倍数,踢人,发消息广播,启动游戏,停止游戏、更新游戏程序,更新配置文件、并服。二是服务器监控,包括CPU、磁盘、内存、进程数、系统负载。三是游戏心跳检测、游戏服务端进程是否存在检测,确定游戏是否正常运行。四是查看游戏各地图在线人数。
[PPT No.26]
我们在开发这套平台的时候,遇到这种情况,一套程序需要适应不同游戏服务端架构。金山有多个游戏研发工作室,现在又代理了其他公司研发的一些游戏,每款游戏服务端架构都不一样,这些服务端架构也是跨服务器的、多进程的,分为网关服务器、角色服务器、地图服务器等等。怎样用一套平台去适应每一个游戏工作室,或者游戏研发公司的架构呢?初期我们考虑这样一个方法,提供一个API接口,制定好协议,希望各游戏服务端都按照这个协议进行接入。但到实际进行开发的时候,遇到一些问题,各款游戏在需要接入运营系统的时候,这些游戏基本上已经成型了,达到内测状态了。每款游戏的研发团队,开发游戏时也会制定程序框架,对TCP协议包处理、变量类型进行封装,有着自己的内部约定。如果他们按照我们提供的接口协议来开发,可能从时间上、成本上、稳定性上都会存在问题。举例来说,我们派大巴车去接人来开会,有些人会说我喜欢自己开车过来,有些人喜欢骑摩托车过来,如果让他们放弃开车,他们会比较不情愿。让各游戏研发团队,对已经稳定的框架进行修改,是存在风险的,他们的不情愿可以理解。当然,也有欣然接受,“坐大巴车”的团队,乐意接受这套方案。
[PPT No.27]
为了能够让每一个游戏研发团队,都能够去适应我们这个平台,进行快速接入,我们后来对接口方案进行了改造。我们采用了一个更开阔的方案,“派大巴车去接”的方案仍然保留,“想自己开车的可以开车”。我们和各款游戏的研发团队约定,只要遵循Knose作为客户端,游戏进程作为服务端,采用TCP长连接进行通讯即可,具体协议可以由游戏研发团队自己制定,不想自己制定协议的,我们提供一套参考协议。这就好比去做一个道路设计,我们修一条公路,你们想开奥迪、宝马都可以,想骑摩托车过来也都可以。这代表着,游戏研发团队用自定义的二进制协议也好、XML等文本协议也好,我们都能支持。虽然可以给他们更宽松的条件,但是也需要遵守一些约束,比如你开车过来不能闯红灯,需要遵守交通规则,如果你说要开飞机过来,我们肯定是不会支持的。Knose程序是我用C语言开发的,C程序的开发、测试时间成本较高,所以Knose程序不会因每款接入游戏的协议不同而修改代码。Knose将TCP协议分为二进制协议、文本协议两类,进行预处理,即分包处理,区分出每一条指令。我们知道,TCP是流式的网络协议,在TCP长连接中,需要分包。对于TCP二进制协议,有些游戏用前两个字节表示一个协议包长度,有些用中间某几个字节表示一个协议包长度,我们通过Knose配置文件来适应这些不同;对于文本协议,我们在配置文件中,用设定的二进制分隔符,对两个包进行分割。这样,Knose就能分拆出每条TCP指令,转换成每条HTTP请求,与PHP接口进行交互。用PHP开发非常迅速,我们去编写解析一款游戏接入协议的PHP代码,半个小时就能搞定。针对不同的游戏,PHP管理界面修改工作量不大;C程序Knose不用修改,只用改配置文件;PHP API接口的代码可重用率达到90%,只需做10%的开发,去适应不同游戏的特殊性需求即可。去修改一个不用考虑CSS样式、界面模板的纯PHP API接口,开发速度可想而知。我们在3月份,用两周时间,同时完成三款游戏的接入。4月份,接入的一款游戏,双方从开发到测试到上线,仅用了三天时间。所以说,架构设计中的易用性、易维护性、灵活性,也是非常重要的。
[PPT No.28]
像图中正中央的这个游戏区服,由7个服务端进程构成,上面的3个进程运行在1台Windows服务器上,下面的4个进程运行在1台Linux服务器上。不管每款游戏的服务端架构如何复杂,不管一个区服是运行在几台服务器上,不管每台服务器上运行几个进程,忽略物理服务器的概念,抓住进程的概念,将进程组织起来,不管游戏架构怎么变,只要最终定位到进程上,与每一个进程实现通讯,就可以解决对不同游戏服务端的兼容性,这样就能够做到一个通用运营平台,做成一个游戏服务端架构之上的架构。
[PPT No.29]
我今天的演讲就到此,谢谢大家。
阅读(1617) | 评论(0) | 转发(0) |