Loading... # [PHP过往及现在及变革](https://blog.p2hp.com/archives/1711) ## PHP过往及现在及变革(一) 众所周知,PHP是一个单线程的脚本开发语言,它常在Web开发及系统集成中出现。其灵活简单成本低廉深受互联网公司青睐,初期大量公司使用它进行快速迭代高效迭代出大量产品服务,但是当流量增长后他的弊端会渐渐展现,很多公司为此吃过不少他的苦头。但往往都是短期放弃后,待后端底层数据完善后又用起来,让人又爱又恨,其中发生了什么,是什么造成这个状态,下面我简单介绍下PHP目前架构中碰到的各种问题及解决方法来慢慢分析事情的原因经过结果,当然最后还要介绍下PHP新的技术革命的并发编程开始。 声明:此篇文章不是批判PHP,而是希望他更好的发展,请各位原谅我的狂放 相信每一家成长中的公司多少都会经历以下事宜: 问题:PHP不是常驻内存的进程,每个请求过来后都会重新加载解析一次脚本,平时流量不多的时候看不出来,当业务发展后业务复杂度增加而PHP加载的文件越来越多,磁盘IO负载不断增加,由于并发增多磁盘效率下降,每次请求都会渐渐变慢。 解决方法:使用opcache缓冲转换后的代码,文件如果没有更新则只需转换一次后就一直在内存中。 问题:不是常驻内存,从持久层获取来的数据也不会有任何内存保存(当然其他常驻内存的语言即使能做到但也不会这么做,因为会因为数据不同步导致脏数据)当流量不断增加的时候持久层一直是个单点服务器(主从后主库也是唯一单点)前端服务器可以横向扩展但是单点的持久层是不能增加的,最后导致大量的持久层拆分及相关繁琐事宜。 解决方法:使用Memcache或Redis缓冲热数据进行缓冲或启用其他可分布式事务持久层。如果没有统一的方案,只能使用架构对业务特有环境特性适配各种架构方案。目前redis虽然是个好的关系NOSQL但是还是太烧人和钱。或拆库拆表,又一条悲壮的路。 问题:不是常驻内存,很多特殊服务无法使用PHP脚本实现,只能依靠外力去做,如制作计数服务只能依靠C或Redis等服务去做,而自己本身的扩展对于通讯及进程类支持很弱这导致了PHP对于集成功能逐渐走下坡路。社区的扩展热情还是太小,这要加强!只要这个功能强化了用PHP搞分布式计算和数据挖掘都是小意思。 解决方法:发展社区,广开言路,支持各种野八路提想法和扩展,不断开发PHP能力扩展插件,要知道PHP是靠Extension扩展来扩展其能力的,而扩展是用C开发的,这意味着C能做什么PHP就能做什么,只要扩展社区活跃神马语言都竞争不过他。之前有人说PHP性能不好,很多是因为PHP的弱类型变量,那好PHP7开始支持强类型了期望PHP走的更好更稳,能做到别人有我也有,别人没有我也有的境界。不要说风格不是以前PHP了,要知道任何一门开发语言一直在升级变化适应需要。另外,可以考虑通讯使用Swoole。 框架规范性问题,PHP是一个很灵活的语言,相对于任何一个语言来说他没有强制的规定开发人员可以做什么事情,不可以做什么事情。所以往往集团混战的时候会导致项目可重用性可维护性较差,当然也是正式因为如此PHP才被互联网采用(为什么?因为互联网产品就是不断的快速迭代翻新的,常规软件的瀑布流方式会死) 解决方法:只要高手或者自律性极强或及其听话的研发人员,统一制定开发标准,划分模块及项目,一人负责一块,谁也不插手别人思考后的写法。虽然不在一起工作,但是思想一定要统一。或采用一个隔离性极强的方式去做,如Socket通讯RPC等。 PHP是单进程单线程的,当处理复杂的业务的时候我们会发现他串行执行命令的时候CPU、磁盘、内存等利用的都很低有很多时候都是在排队等待,有的时候我们想并发的让他去执行一批任务然后一起拿解决结果是一件很痛苦的事情(自己用pthread或者其他方式才能解决,但是这很痛苦) 解决方法:分前后端,前端可以通过消息中间件,同步、异步 调用一个或多个接口。但是socket的扩展确确实实不咋好用。不是普通小企业能做的出来的。 问题:资源争抢问题,当我们使用持久层的时候往往有些关键数据是不能及时同步的,如库存剩余,抢购这类高并发的业务我们都会很自觉的在持久层或者前端做了大量拦截和锁定,这时我们会发现我们的持久层压力很大很多其他无关的业务也躺枪。 解决方法:使用队列但是能让php使用的完善队列并不是很多,全局单个数据或事务锁定服务目前PHP还在石器时代大多数小公司都是直接在持久层锁定或使用Redis(Java研究分布式很久了,所以有zookeeper一类) 问题:单个文件并发写乱问题,当业务产生错误及时的预警,文件无法异步写只能等待写完后(只有写的内容多才会有感觉) 解决方法:使用扩展插件,并且将日志集中监控管理(facebook那个分布式日志系统就不说了光编译都够掉半管血),各位慢慢翻不着急。。。文件异步写暂时不要想了目前大部分语言都不能 问题:文件共享问题,同上个问题,目前可以和PHP配合使用(从小公司发展到大公司的过程都适用的方案很少)的分布式文件存储实在太少了,大猫小猫三两只,即使有也不出名不完善不好用。 解决方法:再翻翻开源找一个。。逼作者完善,或写到redis然后异步队列慢慢dump。 问题:并发流量增加后,资源争抢严重,很多简单的事情变得扑朔迷离,如mysql主从延迟超过30秒,我们往往查询一个用户是否点过赞他点过了,从库好好久才能同步在这个期间他可以点1000次刷分等。 解决方法:前端cookie加锁,服务加公共锁(setnx+expire),学会使用悲观锁乐观锁。 问题:由于大量使用memcache很多无用数据都扔进去,导致memcache成天在不同大小的slab乱扔数据。 解决方法:烧钱多搭建几个memcache,好好规划下自己的数据缓冲及规则。超过1M的数据memcache会不缓冲的。 问题:memcache内的数据是脏的 解决方法:集中封装底层DAO,一旦数据发生变更,自动更新相关cache,如果更新频率极高扔到redis内存储。确认前端哪些部分的数据是无所谓是否过时的。 问题:模块划分不清晰,很多数据重复获取 解决方法:模块层级太多,导致一些东西被重复调用,只能说改改吧。。或者用依赖注入,类自己本身缓冲一些数据,但是可能是过时的数据,或者导致PHP内存超。 事实上以上问题不仅仅PHP遇到了,很多主流开发语言碰到同样的问题。很多情况下小公司流量不大的时候是不会碰到以上大坑的,但是当某一天流量增长了这些问题都会慢慢浮现不断折磨每一个开发人员的神经。 造成以上原因的情况很多都是因为几个关键字: PHP没有常驻内存(虽然他能) PHP很难独立做服务 (虽然他能) PHP分布式云支持少 (虽然他也能) PHP扩展太少了(C开发难度大,国内程序精力问题) PHP规范太少(没人强制规定他标准对错) PHP对持久层依赖太大(继续等开源) 说了这么多,PHP没救了?NO!你想错了 发现了问题就去解决他 PHP没有常驻内存?那么我们用PHP Cli即可 PHP很难独立做服务?那是因为通讯组件不好用,用Swoole啊 PHP分布式云支持很少,通讯问题都解决了害怕啥自己写个都行 PHP扩展太少了?没事我们后期也可以使用php写扩展了请看zephir PHP规范太少了?没关系,我们团队自己商量着来,比规范的太规范没有空间只能hack做事要好太多了! PHP对持久层依赖太大?好吧这个是一直存在的,我们多用各种开源NOSQL吧,有的没给我们PHP写驱动?那好我们用Swoole+zephir搞定他,什么mysql代理,什么redis一致性hash代理,什么longtail。 PHP性能不好?等新版吧~虽然不是最快的但也不是最慢的我相信这个世界上没有最好的开发语言,只有最好的社区。 PHP目前来说是互联网产出最快,经历最多的一门语言之一。 只要我们认为他是一个好开发语言,不断努力改进它,丰富他 任何语言,思路,想法都不会超越PHP,因为他们有我们也可以有。 走起~PHP社区加油!Swoole加油! 期望Swoole的出现是一个引爆点,再次将PHP的辉煌重现,要知道C能做什么,PHP就能做什么,要知道 linux也是C系列写的~:p 别人能编译我们也能编译(HHVM) 别人能做代理我们也能做代理(Swoole) 别人能做长连接我们也能做长连接(Swoole,Socket,libevent,Libuv) 别人能并发异步调用我们也能并发异步调用(Swoole,LibUV,Libevent) 别人能用LXC写系统隔离我们也能用它写系统隔离(等PHP性能再提升一次,鸟哥加油我们看好你:D) 别人能用开发语言写自己的扩展我们也能(Zephir等) 别人有高性能框架我们也有(Phalcon,Yaf) 别人能开发分布式存储,我们也能开发分布式存储 当然他们还在成长,并且很缓慢。各位,我们需要作什么才能加速这个过程?请支持他们,那就是使用!宣扬!反馈!促进他们成长!也让我们自己成长! 他们成长了,我们脚下的巨人才会更健壮。我们才能站得更高,做的更广。 ## PHP过往及现在及变革(二) 随着网站的发展,我们会发现有很多服务和操作其实都是抽象且独立的。 当我们做了分层后,这一部分服务渐渐的成为最底层服务,他们稳定且固定的为我们提供了方便且坚实的服务。 如业务的:订单服务,用户中心,权限管理,周边景观查找 如底层的:分词服务,队列服务,推送服务,计数服务,短信服务,邮件通知服务。 事当分层到一定程度的时候,我们会将服务分为前端和后端服务器群。并将各个项目隔离开期望有一个科学的方式能够管理各个项目之间的依赖关系。 事实上拆分后意味着我们开始向着平台化更进一步其中前端主承压展示界面并拼装组合来调用后端的业务和服务。后端主业务逻辑不承压、但却需要处理各种复杂的业务逻辑他是一组或多组独立的简洁的业务逻辑封装模块组成,他们会为前端提供了丰富的标准化的接口。 你会发现这个概念其实在渐渐的向SOA方向发展,SOA是个很好的集成概念,特别是当我们抽象的很好的时候。我们可以使用前端的逻辑快速的将过去的服务接口拼装出各种所需业务或者一个业务执行了对另外订阅的业务发送通报怎么处理看其他业务心情。 整理过程是需要架构和开发人员一心去组织标准化后端的接口的,不断抽象总结才能完成这个宏伟的目标,没有整理好的结果会很惨。 要知道项目越复杂隔离性越重要,当某一天你发现你改一行代码要影响很多部门的时候你会发现能够快速整理出依赖关系和后果会有多么幸福。 题外话:Restful不适合PHP特别是互联网,只是简单的增删改查并不能诠释API的所有功用,Restful适合封装数据接口,但是业务接口事实上是SOA里面最重要的组成部分之一。 可能有的读者会被这个SOA的计划惊到了,事实上这也是作者自己在公司一直推广散播的想法,实际上实施过程还是很艰苦和残酷的,很多公司也在做类似的实施期望达到这个目标,但是期间需要很多底层支撑来做后面我会慢慢说。 有很多场景并不适合使用SOA概念去做,如内容发布系统只要生成静态页面就好了,做成接口就算了。不断变化迭代的项目也不太适合这么做,因为往往依赖管理会很痛苦,所以对于那些服务需要公开给前端各个项目使用要好好规划,分析好现状明确哪些项目适合,哪些不适合。 当然这个实现方式并不是最优的,不过PHP使用的公司都开始向这个方向发展了,JAVA这么玩有好几年了。。。他们有自己的集成消息总线及中间件和标准,而PHPER们仍需靠自己丰衣足食至于标准,谁做的谁就是标准大家认同一致就ok。 在前后端分离后,用户的一个请求过来后前端往往会产生平均10~200个请求到后端来完成一个用户一次的页面请求,对此建议常规的数据请求接口和业务接口要有明确的划分,否则页面展示的会很慢且后端压的很惨,实际运行的效率也会极低因为这些接口很有可能是串行调用一个个阻塞执行的。 知识点: 并发:请求是指将一组多个API调用的名称和参数分发给多个服务器同时执行待所有api都执行成功并返回结果后将结果整理一起返回给请求方。当然这个事情给线程做也成,但是PHP线程。。。还是用Swoole通讯其他服务器吧。。。简单粗暴还省心。 同步请求异步请求:当你请求一个接口他执行完毕把结果返回给你后才继续执行吓一跳指令的样子就是同步的,异步只要把请求下发下去不管他或者有空去问问结果的这类都是异步的不阻塞你代码执行的。 说了这么多事项总结如下: 好处:前后分离后,我们可以将业务抽象接口整理的很好,我们都知道一个业务做出来后往往会有很多业务去展示,去使用,如果只是简单的include会导致底层改版的时候很难找到哪些代码和业务会受到影响。分离后我们可以根据接口的关键字进行全站grep很轻松找到所有相关的代码及模块。我们的代码依赖渐渐减少了,因为我们都是通过API相互调用的可以通过日志统计出各个API的被那些人调用,哪些频繁,哪些缓慢等,方便测试,方便管理。 坏处:前后分离后,通讯成了最大的压力,目前我们使用优秀的Swoole作为PHP的RPC通讯支撑。当然如果一个用户请求的前端过多的话仍旧会很慢,我们也提供了优化方案如对用户展示结果没有影响的处理扔到后端异步去做反正操作成功了就行、执行没有先后顺序的请求并发下发到后端应用服务器并发执行等都出结果一起返回结果等,但是这也导致了我们持久层会承受了很大压力,每个请求过来后都会并发下10~20个并发任务给后端去做前端跑4000个请求,后端就要并发8w个处理进程,还是瞬时并行的!后端数据服务连接数也增加20倍,目前正在考察连接池。。。这自己挖的大坑,含着泪也要填完。。。PHP的连接池真不多,swoole的worker启动的太多了目前我们一个服务器启动了4k个task,grep一下满满好几屏幕进程。。。 后话 相信大家看到了好处和坏处,事实上很多互联网公司目前已经都开始这么做了,将展示承压在前端,拼装在前端。复杂的逻辑在后端,但是目前来说PHP这方面差很多、至少总的来说支撑还是太少。 之前如果没有swoole我们一直靠include后端代码来工作,这很形式化开发直接new别的项目对象都可以,很伤心。 我们使用了Swoole哪些特性:TCP定长包头非定长body通讯协议,客户端长连接(不开前端服务器端口肯定不够用),task同步异步处理业务逻辑并定期重启(清理未回收资源),worker接收task结果并判断是否全执行完毕以此返回结果(并发处理任务),客户端超时,worker进程管理。 没有使用curl是因为,前端服务器的端口回收很慢,并发高的时候绝对不够用。三次握手多次会很慢,http每次请求后拿到结果都会马上关闭而开keepalive好难做完善普通开发很难做完善,客户端服务端通讯很难自定义协议都不是包级别的协议定制,弄不好漏到外网就残了,因为业务接口权限是很大的,全删都可以。 目前我们只是让前端直接通讯后端对应服务器上,其实中间我们可以加一个中间件,对消息进行转发和控制,swoole性能很变态已经达到了nginx的性能水准,有这么一层可以通过检测应用服务器的返回结果来判断后端服务器是否太忙或者死掉,可以通过php代码将故障机摘除或者降低投放任务量,并将失败的请求转发到其他活着的应用服务器,当然这都是后话。 最后修改:2023 年 08 月 08 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏