TEL:400-8793-956
当前位置:网站讲堂

PHP Web 开发:PHP 永远不会消亡

提问者: 近期获赞: 浏览人数: 发布时间:2021-12-06 12:18:08

 在过去的 10 年里,我们一直在为各种规模的公司开发软件,从财富 500 强企业到只有 500 名用户的小型企业。在此期间,我们的工程团队主要 为后端提供 自定义 PHP Web 开发服务。2 年前,我们的开发发生了一些重大变化,这不仅极大地改变了我们产品的性能,而且还极大地改变了它们的可扩展性。

 
几乎立刻,我们发现 Golang 使我们能够为我们的客户设计更大、速度高达 40 倍的应用程序。我们可以利用 Go 的强大功能来增强我们现有的用PHP编写的产品,并利用这两种语言提供的优势并取代负面影响。
 
我们将解释 Golang 和 PHP 开发的结合如何解决现实世界的开发挑战,并成为您的武器库中的一个新工具,以解决围绕垂死的 PHP 模型的一些问题 。
 
让我们介绍您的日常 PHP 设置
在回答我们如何使用 Golang 来恢复垂死的 PHP 模型之前,让我们首先描述您非常标准的 PHP 设置。
 
在大多数情况下,使用nginx web-server 和 php -fpm server的组合运行您的应用程序是非常典型的 。Nginx 提供静态文件并将特定请求转发给 php-fpm, 而 php-fpm 执行 PHP 代码。较少情况下,您可以将 Apache 与 mod_php 设置一起使用。尽管这种方法的工作原理略有不同,但它仍然使用类似的原理。
 
对于开发者来说,理解php-fpm如何  执行应用程序的代码是最有趣 的。当请求通过时,  php-fpm 会 启动一个 PHP 子进程,并且请求详细信息作为进程状态的 一部分提供 (_GET、_POST 和 _SERVER 等)。在 PHP 脚本执行期间状态不能改变,因此获得一组新输入数据的唯一方法是破坏进程并重新开始。
 
像这样的执行模型有很多好处。您不必太担心内存使用情况,所有进程都是完全隔离的,如果其中任何一个进程死掉,那么它们将自动创建而不会影响其他进程。但与此同时,当您尝试扩展应用程序时,这会暴露出这种方法在 PHP 中的一些缺点。
 
平均 PHP 设置如何带来额外的挑战和低效率
如果您现在正在从事专业的 PHP 开发,那么您已经知道开始新项目的第一步 - 选择您的框架。很快,一个框架提供了用于依赖注入、ORM、翻译和模板的库。而且,当然,所有用户输入数据都可以方便地位于单个对象(Symfony/HttpFoundation 或 PSR-7)中。框架很棒!
 
但一切都带有价格标签。任何企业级框架都需要您加载至少十几个文件,构建多个类并解析一些配置,仅用于处理简单的用户请求或查询数据库。最糟糕的是,每个任务完成后,你必须扔掉所有东西。您刚刚启动的所有代码现在都变得无用了,并且再也不会处理其他请求。告诉任何使用 PHP 以外的语言的开发人员,您会看到他们脸上浮现出困惑的表情。
 
多年来,PHP 工程师一直试图通过使用巧妙的延迟加载技术、微框架、优化良好的库、二级缓存等来缓解这个问题。应用程序并一遍又一遍地重新开始。
 
PHP 可以在 Golang 的帮助下存活多个请求吗?
编写生命周期超过几分钟(甚至数小时或数天)的 PHP 脚本是可能的:cron 作业、CSV 解析器和队列使用者都是示例。所有这些脚本都遵循相同的过程:检索值、执行作业、等待下一个值的到来。代码始终保留在内存中,最终在引导加载框架和应用程序所需的无数交互过程中节省了宝贵的毫秒时间。
 
开发长期存在的脚本并不总是那么容易。任何错误都会彻底杀死进程,内存泄漏诊断起来非常烦人,我们不能再使用 f5-debug。
 
但是,随着 PHP 7 的引入,情况有所改善,它提供了可靠的垃圾收集器,可以更轻松地处理错误并防止核心扩展泄漏。工程师们仍然必须小心他们代码中的内存和状态问题(但是有没有什么语言不需要你注意这一点?)。但是,您不必担心的意外问题较少。
 
是否可以采用该模型来处理长期存在的 PHP 脚本,并使其适应更琐碎的任务,例如处理 HTTP 请求和消除每个请求的引导加载?
 
首先,我们需要实现一个服务器应用程序,它可以接受 HTTP 请求,然后将它们一个一个地转发给 PHP 工作线程,而不会每次都杀死工作线程。
 
我们知道我们可以使用纯 PHP (PHP-PM) 或使用 C 扩展 (Swoole) 来实现 Web 服务器。虽然这两种方法都有自己的好处,但都让我们不满意,想要更好的东西。
 
我们需要的不仅仅是一个网络服务器。我们需要一些东西来替代与 PHP 中繁重工作相关的负面影响,但仍然允许针对每个单独的应用程序轻松定制和扩展。我们需要一个应用服务器。
 
Golang 可以帮助创建应用程序服务器吗?我们知道它可以,因为它将应用程序编译成单个二进制文件,它是跨平台的,利用其非常优雅的并发模型和标准 HTTP 库,除此之外,我们可以使用数千个开源库和集成。
 
使两种编程语言合二为一的挑战
首先,我们需要定义两个或多个应用程序如何相互通信(进程间通信)。
 
一种方法是使用 由 Alex Palaistras 在英国发布的很棒的库在 PHP 和 Golang 进程(类似于 Apache mod_php) 之间共享内存 。但是,对于我们如何将其用于我们的实现,它仍然有一些限制。
 
我们决定使用另一种更经典的方法,其中进程之间的通信是通过套接字/管道使用二进制流完成的。我们选择这种方法是因为它几十年来一直是一种可靠的通信方法,并且在操作系统级别得到了很好的优化。
 
首先,我们创建了一个轻量级的二进制协议来在进程之间交换数据并处理传输错误。在最简单的形式中,这种类型的协议是一个类似网络字符串的实现,带有 固定大小的包头 (在我们的例子中,17 个字节),其中包含有关每个包类型、大小和二进制掩码的信息,以验证数据完整性。
 
在 PHP 端,我们使用了 pack  PHP 函数。对于 Golang,我们使用了 encoding/binary 库。
 
我们比仅仅创建协议更进一步。我们添加了 直接从 PHP调用 Golang  net/rpc服务的功能。这个功能在开发后期变得非常有用,因为我们可以轻松地将 Golang 库集成到我们的 PHP 应用程序中。您可以在我们发布的另一个名为Goridge 的开源产品中看到这项工作的结果 。
 
跨多个 PHP worker 管理任务
 
一旦建立了通信,下一个目标就是最有效地将作业传递给 PHP 进程。对于任何传入的作业,应用程序服务器必须选择一个空闲的工人来执行所需的任务。如果一个工人/进程失败或死亡,我们将丢弃它并为它创建一个替代品。另一方面,如果工作进程/进程成功,我们会将其返回到池中并使其可用于下一个工作。
 
在我们的实现中,我们使用了一个缓冲通道 来存储活动工作线程池。一个错误观察者和状态管理器被放置到位,以从池中拉出意外死亡的任何工人。
 
最终结果是一个能够处理任何任意二进制作业的工作 PHP 服务器。
 
为了使我们的应用程序作为 Web 服务器工作,我们必须选择一个可靠的 PHP 标准来表示任何 HTTP 传入请求。对于我们的特定需求,我们只是将 Golang  net/HTTP 请求转换为 PSR-7  (https://www.php-fig.org/psr/psr-7/meta/) 格式,使其与大多数 PHP 框架兼容在市场上。
 
由于 PSR-7 被声明为不可变的(一些工程师可能会指出它在技术上不是不可变的),它迫使开发人员编写的应用程序不再按照设计将请求视为全局实体。这与长期运行的 PHP 进程的想法完美契合。我们尚未命名的最终实现如下所示:
 
RoadRunner 简介:高性能 PHP 应用程序服务器
我们最初的测试用例是一个 API 后端,它周期性地经历难以预测的比平时高很多倍的请求爆发。虽然  在大多数情况下nginx就足够了,但遇到 502 错误是相当普遍的,因为我们无法在预期的负载增加之前足够快地平衡系统。
 
2018 年初,我们在市场上部署了第一台 PHP/Golang 应用服务器,以取代这种设置。 效果立竿见影,令人难以置信。 我们不仅设法完全消除了 502 错误,而且最终将服务器总数减少了三分之二,这为工程师和产品所有者节省了大量的服务器成本和令人头疼的平板电脑。
 
到 2018 年年中,我们完善了该方法,在 MIT 许可下 将其发布到 GitHub ,并将其称为 RoadRunner,它描述了其令人难以置信的速度和效率。
 
RoadRunner 如何使您的开发堆栈受益
将 RoadRunner 引入我们的技术堆栈使我们能够使用网络/HTTP 中间件,以便在请求到达 PHP 之前启用 JWT 验证、处理 WebSockets 并将统计信息全局聚合到 Prometheus 中。通过使用嵌入式 RPC,我们可以将任何 Golang 库 API 公开给 PHP,而无需自定义驱动程序。最重要的是,我们可以使用 RoadRunner 库来设置不同于 HTTP 的新服务器。示例包括 在 PHP 中运行 AWS Lambda处理程序、创建可靠的队列使用者,甚至将GRPC添加  到我们的应用程序中。
 
迄今为止,在 PHP 和 Golang 开发社区的帮助下,我们提高了稳定性,在我们的一些测试中将应用程序的性能提高了 40 倍,完善了调试工具,将其与 Symfony 框架集成,并增加了对HTTPS、HTTP/2、插件和 PSR-17。
 
结论
有些人仍然坚持认为 PHP 是一种缓慢、笨拙的语言,您只能用来编写 WordPress 插件。他们甚至可能会说 PHP 有一个限制:一旦您的应用程序变得足够大,您就必须切换到更“成熟”的语言并替换多年的 PHP 代码。
 
对他们,我们说“再想一想”。我们相信 PHP 的唯一限制就是您设置的限制。 当您在 PHP 后端之上智能地设计移动应用程序时,甚至 可以开发 PHP 移动应用程序 。您可以用一生的时间从一种语言跳到另一种语言,试图找到满足您编程需求的“完美匹配”,或者您可以开始将语言本身重新想象为工具。
 
像 PHP 这样的一种编程语言表面上的缺点实际上可能是其成功的关键。通过将它与 Go 等另一种语言配对,您最终会创建出比单独使用任何一种语言更强大的最终产品。
 
在使用 Go 和 PHP 一段时间后,我们可以自信地说我们都喜欢它们。我们不打算因为另一个而牺牲一个,相反,我们将继续寻找从这种双栈编程中获得最大效率的方法。
上一篇: 12个移动应用开发趋势
下一篇: 2021年网站开发的平均成本——你应该知道的