9

如何拯救一个有历史问题的PHP项目

 3 years ago
source link: https://0x1.im/posts/2016-12-18-how-to-save-a-php-project/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本文未经许可禁止转载,如有转载意愿请与作者联系。

1、项目历史

我们团队现在做的是一个微信第三方平台项目,项目起步时间不长,到现在差不多两年。起初是个探索性的小项目,但是随着业务的发展,已有的结构渐渐不能满足业务需求以及高峰时段的压力;不仅如此,一些历史问题也给我的开发流程带来了不少问题。所以这半年以来,再满足产品高速迭代的需求的前提下,我们也对后台框架以及服务器的结构做了持续的调整和优化。本文主要是针对后台部分的变动进行整理。

项目开始的时候,组里还有其他两个项目处于维护阶段。并且这个项目后台的起步也是直接从原来的项目里 copy 了部分结构代码并在这基础上进行改动,所以有很多历史遗留问题在里面。项目本身的框架还是 11 年左右的一个 cakePHP 的变种框架,项目框架从来没有更新过,甚至项目本身的 String 类封装每次调用都会触发 bug。

在服务器上,三个项目最开始的时候是共享所有的服务器资源的。而较老的项目由于只维护不开发,一些业务组件,比如 memcached、sphinx 中文版(Coreseek)以及一些 PHP 的扩展早已没有更新,但是每次服务器变更的时候还得考虑这些东西。

最近半年,通过不断的调整和修改,我们解决了大部分历史遗留问题,并成功升级到 PHP7。

2、基本能力

PHP 最近几年发展势头也是很快,Composer、Laravel 等组件和框架逐渐流行起来。借助第三方的力量能够很大程度的简化自己的工作、提高开发效率。

虽然以前项目中也有引入 PHPExcel 等第三方库,但都是通过下载源码丢到项目本身的方式来做的。这样做的问题是引入并不方便,并且一般不会及时通过升级来解决一些第三方库的 bug、漏洞等问题。为了提高框架的基础能力,我们在框架中引入了 Composer

在 Compoer 的引入的同时我们也引入了一些优秀的库来解决基础问题。之前项目中的 cURL 封装到处都是,不统一并且使用起来也不方便,所以在有了 Composer 之后项目中直接加入了 guzzle。同样,为了解决异常日志栈的记录问题,monolog 也被加入到项目中。

这其中最大的一个变更是,受限于框架底层实现的问题,框架本身的 ORM 极其难用,并且整个是基于 mysql_query 来实现的。所以我们设法直接在框架中集成了 Laravel 的 ORM eloquent 减轻痛苦。

至此,项目解决了四个基本问题:

  1. 第三方库的引入;
  2. 服务端发送请求的处理;
  3. 项目日志记录;
  4. ORM 的易用性。

这些改动都是为了解决开发上的问题。但开发上某些问题依旧存在:老的代码难以改动,并且 PHP 5.5 之后 mysql_query 系列的方法已经逐步被废弃。所以在后续的修改中一次性将这些方法都替换成了 mysqli 的实现,这也使得后续的 PHP7 升级工作能够继续进行下去。

3、项目拆分

项目的拆分分为两个部分,一个是逐步隔离两个老项目和新项目之间的资源、使新项目能够摆脱历史包袱快步前进;第二个是对新项目本身的拆分工作。

上文中提到,项目起步时的基础代码和环境都是从原来的两个项目里直接 copy 过来的,甚至硬件资源都是公用的。因为几个项目整个的战线拉的很长,所以一些依赖的组件和服务有些都是很古老的,甚至是没有继续维护的。也有一些内部的组件依赖。这导致无论想做什么新的尝试都需要瞻前顾后,总是要考虑到是不是会影响到原来的东西。

由于硬件资源完全共享,而一些业务高峰时间带来的压力总是会导致服务器资源占用飙升,几个项目的服务同时挂掉,并且经常短时间内难以恢复正常。为了解决这个问题,我们对之前的两个项目和这个项目的物理资源分配做了一些调整,并做了隔离。

在项目上,最开始这个项目的所有功能模块,包括消息转发系统和主站部分全部都是耦合在一起的一套代码,由于框架本身的问题,效率并不高。在项目的改进中,我们逐渐分离了核心的消息系统部分并采用性能更高的框架(phalcon)完全重写。硬件上消息系统和主站也进行了分开部署,使两个部分不会相互影响。

4、持续改进

技术总是在持续的优化中进步,项目改进的脚步也从不曾停止。

在持续的改进过程中,我们继续进行了以下的部分工作:

  1. 使用 Redis 完全替换了以前依赖的 memcached 和 memcacheq,在缓存和消息处理上变得结构单一易于维护,引入 Redis 集群提高可靠性;
  2. 引入优秀第三方的库封装微信 API 调用的部分,节省工作时间;
  3. 在发布环境,增加了预发布环境尽量靠近线上环境,降低每周发布之后的 bug 量;
  4. 在开发环境打开所有 E_NOTICE 及以上的告警,尽量减少 bug 并优化代码逻辑,搭建 sentry 平台记录线上的错误及异常日志,便于查找和及时修复;
  5. 使用 elasticsearch 存储服务器日志以及全文检索的内容;
  6. 整理所有 crontab 脚本,将所有本身需要长期服务的任务使用 supervisor 集中管理;
  7. 推动 https 切换。

5、版本升级

PHP7 带来的内存使用量降低和性能优化是很令人心动的,在很长一段时间里,受限与项目环境的原因,项目基本上没有可能在短期内升级。

但在升级 PHP7 之前实际上已经进行过另外一次升级工作。起因是机房迁移,需要将所有的服务器都迁移到新的机器上去。借助这个机会,我们把所有的服务组件都进行了整理和重新编译,并且相应的升级了 Nginx(1.4 到 1.8)、openssl(解决之前 openssl 爆出的一些列安全问题),也将 PHP 从 5.4 升级到了 5.6。

在这期间,由于对 MySQL 存储 emoji 表情有需求,通过较长时间调研和实践,最终也将 MySQL 从 5.5 升级到了 5.7(但请注意:MySQL 主从同步,无法直接从 5.5 同步到 5.7,只能通过 5.5 -> 5.6 -> 5.7 这种方式)。

之后的几个月里,项目基本上保持着高速稳定的状态运行着。经过一段时间的等待,项目需要的几个开源的核心扩展完成 PHP7 版本的开发工作之后,我们也开始进行升级 PHP7 的工作。

升级的工作主要包括两部分:检查语法兼容性,解决扩展兼容问题。因为有些扩展并不是来自开源社区,需要自己手动去改。

整个升级的节奏也是分成了两个部分:先动消息框架,再升级主站。由于两个老项目的状态,所以决定不对其进行变更,还是维持在原来的状态。丢掉包袱才能快步前进。

消息框架依赖较少,主要就是 phalcon 和 redis,所以升级过程比较顺利。

主站依赖的内部扩展较多,语法兼容性问题也比较多。所以使用了开源项目检查语法兼容性。对框架底层做了一些改动,这里面最主要的还是从 mysql 换到 mysqli 的工作。

扩展部分在编译过程中就可以把大部分不兼容的语法暴露出来,进行修改并重新编译测试即可。

总结起来,一共经历过以下步骤:

  1. 重新编译所有服务组件、升级 Nginx(1.4 到 1.6),PHP (5.4 到 5.6);
  2. 升级 MySQL (5.5 到 5.7);
  3. 消息框架升级 PHP7;
  4. 修改主站框架,将底层 mysql_query 替换为 mysqli
  5. 检查主站代码兼容性;
  6. 修改主站使用的内部扩展,使其兼容 PHP7;
  7. 主站升级 PHP7。

随着业务的发展,项目也会面对越来越多的问题。技术上不断前进,不断提高个人水平。做好迎接更多的挑战的准备,也总会有更多的惊喜。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK