程序员

首页 » 常识 » 问答 » GCTT出品PHP不会死我们
TUhjnbcbe - 2020/6/3 13:18:00

在过去的十年中,无论是世界强企业或是仅拥有名用户的企业,我的团队都曾为他们开发过软件。在此期间,我的工程团队主要使用PHP进行后端开发。2年前,我们在开发项目中引入了一些东西,这不仅彻底改变了我们产品的性能,也改变了它们的可扩展性————我们将Golang引入到我们的开发框架中。

很快,我们发现Golang的引用使得我们能够为客户设计更大型,速度提高40倍的应用程序。我们可以利用Go的强大功能来增强我们用PHP编写的产品,并充分利用这两种语言的优劣进行取长补短。

我将解释如何结合Golang和PHP这两种语言解决实际开发中的问题,这将为你的PHP开发带来全新的道路,以此解决垂死的PHP模型相关的一些问题。

首先介绍常用的PHP设置

在回答我们如何使用Golang来将PHP起死回生之前,我们先介绍一下标准的PHP设置。

在大多数情况下,PHP开发者会使用nginxWeb-server和php-fpm服务器组合运行应用程序。当php-fpm执行PHP代码时,Nginx提供静态文件并将特定请求转发到php-fpm。也可以将Apache与mod_php一起使用。即使这种工作方式和上面那种略有不同,但它们的原理还是类似的。

对于开发者来说,理解php-fpm如何执行应用程序的代码是最有趣的。当一个请求发送时,php-fpm启动一个PHP子进程,并且将请求内容作为进程状态的一部分(_GET,_POST和_SERVER等)。在执行PHP脚本期间,状态无法更改,因此获取一组新输入数据的唯一方法是销毁该进程并重新开始。

像这样的执行模型有很多好处。你不必担心内存使用情况,所有进程都完全隔离,如果其中任何进程死亡,那么它们将自动创建而不会影响其他进程。但与此同时,当你尝试扩展应用程序时,这一特性会成为程序开发的绊脚石。

一般的PHP设置使用起来很麻烦并且非常低效

如果你今天正在进行专业的PHP开发,那么你应该已经知道开始一个新项目的第一步-选择框架。框架提供了依赖注入,ORM,翻译和大量丰富的库。当然,所有用户输入数据都可以方便地放在一个对象(Symfony/HttpFoundation或PSR-7)。框架用起来是那么得心应手!

但任何事都有两面性。所有的企业级框架都要求你加载至少十二个文件,构造多个类并解析一些配置,以便处理简单的用户请求或查询数据库。最糟糕的部分是每个任务完成后,你不得不抛弃这些代码。你刚刚启动的所有代码现在都变得无用,并且永远不能拿来处理另一个请求。若是说给任何使用PHP之外的开发人员听,他们一定会对此满脸困惑,不能理解。

多年来,聪明的PHP工程师一直试图通过使用延迟加载技术,微框架,优化良好的库,二级缓存等技术来缓解这些问题。但是在你项目结束时,你仍然不得不扔掉你的整个流程并一遍又一遍地重新开始重复的工作。

在Golang的帮助下,PHP能否支持多请求?

只要不是几小时或几天的生命周期,编写生命周期超过几分钟的PHP脚本还是可以的:比如cron作业,CSV解析器和队列使用者。所有这些脚本都遵循相同的过程:检索值,执行作业,等待下一个值到来。代码在整个过程中都保留在内存中,最终只能节省几毫秒,因为加载框架和引导程序需要进行大量的交互。

开发能够长时运行的脚本并不容易。任何错误都会彻底杀死进程,诊断内存泄漏非常麻烦,我们无法再使用f5-debug。

然而,随着PHP7的推出,情况有所改善,这个版本提供了一个可靠的垃圾收集器,使得错误更容易得到处理并防止核心内存泄漏。虽然工程师们仍然得小心他们的代码中的内存和状态问题,但是,你不必担心找不出问题所在并有效解决这些问题。

是否有可能采用模型来处理那些,需要长期运行的PHP脚本并使其适应更复杂的任务需求,如处理HTTP请求和消除每个请求的引导加载?

首先,我们需要实现一个服务器程序,该程序可以接受HTTP请求,然后逐个将它们转发给PHP工作者,而不是每次都杀死工作者。

我们知道我们可以使用纯PHP(PHP-PM)实现Web服务器,或者使用C-extension(Swoole)编写。虽然这两种方法都有各自优势,但两者都不能让我们满意,我们需要更好的方法。

我们需要的不仅仅是一个Web服务器,而是希望能够去掉PHP开发中的繁重操作和其他负面因素同时,仍然保障每个应用程序的可扩展性和多样性。我们需要一个能够多元化的应用服务器。

Golang可以帮助我们创建这样的应用服务器吗?我的答案是,它可以。因为这种语言是跨平台的,它可以将应用程序编译成单个二进制文件,我们还可以利用其非常优雅的并发模型和HTTP标准库,最重要的是,我们可以使用Golang所拥有的数千个开源库和集成环境。

如何使两种编程语言进行一体化开发

首先,我们需要了解两个或多个应用程序如何相互通信(进程间通信)。

一种方法是使用AlexPalaistras在英国发布的令人生畏的库,可以在PHP和Golang进程(类似于Apachemod_php)之间共享内存。但是,这种库在我们实际开发中对我们造成了很大的限制。

我们决定使用另一种更经典的方法,即使用Socket/Channel上的二进制流完成进程之间的通信。我们选择这种方法是因为这种通信方法被使用了数十年,是一种可靠的通信方法,并且在操作系统级别上得到了很好的优化。

首先,我们创建了一个轻量级二进制协议,用于在进程之间交换数据并处理错误。在最简单的实现方法中,这种类型的协议是类似于netstring的实现,拥具有固定大小的包头(点击这里查看我们给出的例子),其包含每个包类型,大小和二进制掩码等信息,以便验证数据完整性。

在PHP方面,我们使用了包PHP函数。对于Golang,我们使用了编码/二进制库。

我们甚至在创建协议上更进一步。添加了直接从PHP调用Golangnet/rpc服务的功能。这个功能在开发中非常实用,因为我们可以轻松地将Golang库集成到我们的PHP应用程序中。你可以在我们发布的另一个名为Goridge的开源产品中看到这项工作的结果。

实现PHP高并发处理任务

一旦建立了通信,下一个目标就是最有效地将作业传递给PHP进程。对于任何传入作业,应用程序服务器必须选择一个空闲工作程序来执行所需任务。如果worker/process失败或死亡,我们会舍弃它并为他创建一个替代的进程。另一方面,如果worker/process成功,我们会将其返回池中并使其可用于下一个作业。

在此需求的实现中,我们使用有缓冲的通道来存储活动工作池。

最终结果是一个能够处理任意二进制作业的有效`PHP`服务器。

为了使我们的应用程序能作为Web服务器工作,我们必须选择一个可靠的PHP标准来表示任何HTTP传入请求。想要满足此需求,我们只需将Golangnet/HTTP请求转换为PSR-7(

1
查看完整版本: GCTT出品PHP不会死我们