/首页
/开源
/关于
《GM技术这两年》之原始人时代(一)
发表@2018-12-15 16:02:38
更新@2023-01-21 22:47:40
其实之前已经写了一大坨系列关于GM的文章了 , 只不过那些文章总体来说来涵盖面还是比较广阔 , 更多还是倾向于八卦扯淡 , 技术方面的问题只是涉及了一小部分 . 所以打算新开一个全新的系列 , 专门用几个章节来聊聊GM当初的技术演变 . 其实写这个并不是说GM的代码有多牛逼都DIAO , 相反 , 代码质量真是差的一BI . 那为什么还要写呢 ? 我想原因有如下几个原因 : - 大部分PHPer还是很少能亲自经历从几千用户到百万级用户这个过程的 - 大部分PHPer还是很少能接触过近2W并发级别的 - 大部分PHPer还是很少能接触到关于架构的一些东西 - 大部分PHPer还是很少能在生产环境接触过swoole的 - 大部分PHPer缺少从LNMP结构跳出的机会 所以 , 如果你对上面五条中任意一条有兴趣 , 都可以考虑先打赏一波儿 , 至于看不看都不重要 ... ... 话说回来 , GM的代码质量是很比较差的 , 差不代表性能烂 , 差表示代码风格垃圾 , 代码可扩展性差 , 几乎不可维护 , 说到底还是那句经久不衰的老话 : ![](https://ti-node.com/static/upload/6479342622899961856) 2016年的6月份 , 于巨柱在微信上偷偷摸摸问我 " 有个外包 , 接不接 ? " , 那会儿我在白鹭引擎工作 , 由于身处边缘三不管部门负责一个没人负责的项目 , 每天主要工作内容就是骗工资或者看别人骗工资 . 总之 , 大家看起来好像都在骗工资 , 只不过他们不如我骗的厉害 . 反正闲着也是闲着 , 就索性接了这个项目 . 于巨柱的意思是 " 这个项目已经上线了 , 但是有问题很多 , 几乎已经不能用了 , 现在是想修一修 " . 实际上一听到这个实际上是不太想接的 , 作为同行 , 尤其是作为一个平时也会接外包骗钱的同行 , 我对外包的代码质量还是有底的 . 一般说来 , 我认为外包代码质量的底线就是 " 又不是不能用 " , 但眼下这个外包 , 已经显然突破这个下限了 , 也就说 " 真的是不能用 " . 所以 , 我是不太想接的 . 于是我就问于巨柱 " 之前这项目是谁做的 ? 还找他们不行么 ? " , 于巨柱说 " 之前的听说是找的一个外包团队做的 , 肯定是不想管了 " . 琢磨了琢磨 , 怎么着都是骗工资 , 索性再多骗一份外包的钱得了 . 先说下GM是个什么产品 , 这是一个基于LBS地理位置的APP , 玩法比较简单 , 和探探是十分类似的 , 总体就是搜索你附近的用户然后以卡片的形式展现出来 , 向右滑动卡牌表示你喜欢这个用户 , 向左滑动卡牌表示你不喜欢这个用户 , 如果对方也恰巧右滑你那么你们两个人就会匹配成好友 , 然后就可以开心地做爱做的事情了 ... 这个时候的GM的架构是就是最正统的LAMP模式 , 注意是LAMP不是LNMP , 其中 , 为了实现地理位置运算 , 还额外引入了MongoDB作为地理位置运算 . 如果非要画个什么架构图之类 , 也就是下面这个样子 : ![](https://ti-node.com/static/upload/6479556426338402304) 值得注意的是,图中是将PHP和Apache之间的上下距离隔离的特别近,几乎快贴到一块儿了,这么画的原因我认为我有必要解释一下。 首先,大家做为花式CURD全能手,靠人家Apache骗吃骗喝骗工资好几年,我认为不了解一下实在是说不过去,那老话不都这么说的么,“吃屎不忘挖井人”。 先来说下Apache的中文名字,其实就是阿帕奇,这个词除了能代表下面要说的http服务器外! #### 还有代表着下面这个响当当的玩意: ![](https://ti-node.com/static/upload/6479577687382818817) Apache是一个HTTP服务器,对外可以提供HTTP服务,也能干点儿别的事儿,比如反向代理什么的,不过这些可以认为是副业,主业还是搞HTTP。一般说来,Apache有三种运行模式,你们理(bei)解(song)一波儿: - MPM Prefork模式。这是最古老最传统,性能最差但也是最香的模式。这种方式中,Master进程根据配置提前fork出子进程,每个子进程中只有一个线程,然后靠这些子进程干活。一个子进程同时职能干一件事儿,假设说干完一件事需要1秒钟,然后你想1秒钟内同时此后10个人的请求,那你就老老实实开10个进程干活。如果一个进程crash了,并不影响另外九个进程。 - Worker模式。稍微先进点儿,还是Master进程fork出N个子进程,但是每个子进程内部不再是单线程了,而是多线程,处理具体每一个http请求就由线程来负责处理。这种模式的优点是节省内存资源和CPU资源,因为N个线程可以共享一个进程的内存地址空间,而且线程间切换耗费CPU也要比进程切换耗费CPU少很多。总之,提供相同的服务量,可以节省内存和CPU;相同的内存和CPU,可以提供更多的服务量;缺点就是要考虑多线程编程安全的问题,因为线程共享进程的内存空间和数据,所以会带来线程安全问题。 PS:以上两种模式,都有一个缺点就是keep-alive的连接会长期占据霸占一个进程或者线程,一直到该连接超时,该进程或者线程才会被空闲出来准备接受另外新的http请求。 - Worker event模式。最新最先进的模式了,这个是要靠事件监听混饭吃的春药模式,本质上就是要利用epoll搞。进程和线程模型和Worker模式一模一样,理论上说由于利用事件监听,会更加节省资源,而且对于keep-alive类型的连接,不会存在进程或线程被长期霸占的问题,这都是得益于epoll事件监听。 上面这一波儿,你要是背过了,面试的时候有人问你,你就可以无形之中装一波儿逼!但如果对方又接着问,那么Apache是怎么解析PHP的呢?上面说的都是Apache处理http请求这些个事儿。 这里一定千万切记务必必须要注意的是:Apache仅仅是个http服务器,它可以处理Http请求,但是不能解析PHP代码,所以它收到一个HTTP请求后,开始要执行PHP代码执行完毕后将数据再吐给客户端,那么Apache究竟是利用什么方式来解析PHP代码呢?其实吧,你回答一个“当然是利用PHP解析器啊!”,恭喜你,你就可以回家等通知了,用面试行话说就是“送走吧”。 在默认情况下,PHP解析器是以一个模块形式直接融入到Apache进程中,你可以认为最原始最赤裸的Apache进程除了自身的东西外,别的什么都没有;Apache进程允许你对进程进行改装,往上挂自己的东西,把PHP模块挂上去辣么这个Apache进程就可以解析PHP代码了,屌不屌。当然了,做为你随意往上挂模块的代价,这个Apache进程占用的空间会比较大, #### 但是,又不是不能用,理解万岁! ![](https://ti-node.com/static/upload/6479595194445463553) #### 但是实际上,这种模式是最常用的PHP和Apache工作方式了。 ![](https://ti-node.com/static/upload/6479596046501543936) 除了这种方式外,还有一种方式就是CGI方式,这种方式下,Apache进程不会挂PHP解析器模块,每次需要解析PHP代码的时候,Apache会通过CGI方式调用一次PHP解析器解析PHP代码,PHP解析器在执行完毕PHP代码后将结果再以CGI的方式返回给Apache,同时,该PHP解析器也会销毁掉;如果再来第二个请求,那么会再次调用PHP解析器,完成任务后销毁掉。这种模式,这是性能最低下最辣鸡的方式,如果不是恒河水喝多了,千万不要用。 (PS:教你一个小窍门,如果你接了一个外包,可以先用方式搞;如果百分之一的概率对方业务做起来了,系统一卡,你可以把这种CGI模式改成PHP模块的模式,几分钟就可以让性能瞬间提升很多,然后你就可以趁机骗一波儿优化费了。) 这会儿的GM就是在这种经典LAMP方式下运行,用户量大约在6000左右,每日新增大约50+用户左右,所有软件都跑在一台服务器上,出现的问题主要是两个: - API响应缓慢 - APP端时长出现自身动画卡顿、闪退 所以本次外包的任务就是解决上面两个问题。 #### 但是! ![](https://ti-node.com/static/upload/6479607112530919424) 所以,再不改动原来那么烂代码的基础上,如何加速一下API呢?我用的是下面的方法,你们可以参考一下: - 首先打开MySQL的general log,也就是凡事执行过的sql语句全部记录下来 - 然后总结一下所有类型的查询语句,开始加索引 果然,这个还是比较有效的,顿时API就比原来快了一些,但是这个终究是治标不治本的。外包代码的质量实在是太差太差了,相比之下,我写的代码仅仅是风格差、扩展性差、不可维护,但不会性能差,他们这家伙一波儿扫操作连代码本身都带着极大的性能问题,真是服了。 原来的团队是用YII框架做为开发框架,但实际上,这种面向Web而生的框架并不适合做API,因为API和Web是有很多不同的,Web框架里带着太多太多为解决Web而生组件和库,其实对于API来说都是压根用不到的,做为API开发,我一向主张使用快捷轻量级的方式,严格意义上来说只要能满足一个简单的路由分发功能和兼容composer即可!这方面的典范,大家可以选用惠新宸的Yaf或者fligt框架等,都是非常粗暴的解决方案,做为个人偏好,从不推荐Lavarel(包括Lumen)这种玩意。 这里,架构图上有个不能错过的组件,就是MongoDB。做为一款LBS APP,地理位置运算几乎是业务核心。引入MongoDB完全没别的作用,就是为了利用MongoDB的Geo空间索引进行地理位置运算,最终实现附近的人,这个没什么好说的:无可厚非。 我们说,这一系列文章讲的是一个公司核心业务的整体,更多在于服务端整体而言。所以,除了API业务外,还应纳入考虑的还有后台管理,一般说来一个公司的后台管理主要用于两个方面: - 客服管理后台 - 运营管理后台 上面我画的结构图里没有涉及到这两块儿,原因很简单:GM这会儿压根就没有任何后台管理,如果非要强行说有的话,那就是原来的外包团队给安装的那个phpMyadmin。 6月份开始接手这个优化项目,服务端这里主要就是修改少数极其脑残的PHP代码,更多是优化MySQL数据;于巨蛀负责APP端闪退问题的修复以及部分动画的卡顿以及OOM。其实,闪退这个问题出了APP自身鲁棒性不好外,还有相当一部分原因也是由于API返回数据不合格导致APP闪退。