Nginx 黑魔法:使用 NGX-PHP 模块低成本实现高性能应用

  本篇文章分享一个和 Nginx 以及 PHP 有关的“黑魔法”:NGX-PHP 模块。通过这个方式,我们可以低成本的实现高性能应用,以及适合在服务器资源有限的情况下,同时体验到 Nginx 的高效以及 PHP 的灵活。

  如果你对 PHP 的印象还停留在“慢”,那么或许这篇文章可以帮助你打开新世界。

  写在前面

  提到 “NGX 和 PHP”,使用过 Nginx 和 PHP 的同学第一反映可能是 Nginx + PHP-FPM 这种架构。不过,这篇文章中,我们要提到的技术架构更简单高效一些:直接使用 Nginx 和三方模块(NGX-PHP),调用 PHP Embedded 库,来实现原本需要跨进程实现的功能,从而明显提升应用性能。

  之所以能够这样玩,需要感谢下面两个项目的相关实现:PHP 提供了一种有趣的调用方式:让其他的程序能够通过支持 C Bindings 的符号绑定的方式来调用它的核心引擎,Zend。这种接口调用方式,被称作 PHP SAPI 或者 PHP-Embeded,项目地址:https://github.com/php/php-src/tree/master/sapi/embed2016 年,有一位来自搜狐的工程师 rryqszq4,开始在 GitHub 上尝试开源一个项目,把 “Nginx” 和 “PHP-Embeded Library” 桥接到一起,这个项目经过多年发展,陆续支持了 PHP5、PHP7,以及最新的 PHP8。项目地址:https://github.com/rryqszq4/ngx-php

  在 Techem Power 的测试中,自 2020 年开始,“NGX-PHP” 这个技术选型出现之后,便取得了不错的成绩,比如:2020 年的Round 19[1],以及 2022 年的Round 21[2]。2020 年和 2022 年的两轮框架评分测试

  在最近的 2022 年测试中,框架开销[3]非常低,位于排行榜第五和第六名。2022 年测试中,框架开销排行

  如果用我们熟悉的 Node.js + MongoDB 作为基准,那么这套方案开销比它少 300%:跑的快,吃草少。换个角度来看,这个方案非常贴合 “Nginx” 和 “PHP” 的特性:快糙猛。

  好了,关于这个项目的概况就介绍到这里,我们先来使用 Docker 快速、实际的感受下它的性能。

  快速体验

  执行下面的命令:

  当 Docker 镜像下载完毕之后,我们将看到一个和普通 Nginx 镜像启动无异的日志输出:

  打开浏览器,输入 ,就能够看到效果啦。2022 年测试中,框架开销排行

  随手输入一些内容,能够看到程序“跑的”还是挺快的。发一些只有自己看的到的“微博”

  在不进行应用优化、Nginx 优化的前提下,我们能够看到处理一个请求不过 2ms 左右。单次请求服务端处理时间2ms左右

  接下来,我们来聊聊如何使用 NGX-PHP,学习了解这种开源方案背后的一些细节。完整的应用代码,我上传到了 soulteary/ngx-php-micro-blog[4],有需要可以自取。

  准备工作

  想要愉快的阅读和跟着本文游玩,只需要 Docker 环境,可以参考《在笔记本上搭建高性价比的 Linux 学习环境:基础篇[5]》文章完成基础环境的准备,就不过多赘述了。

  实现简单的微博应用

  我们来使用“最好的语言:PHP”,实现一个简单的“微博/推特”程序。

  简单实现模版类

  使用 PHP “画一个”页面出来,可以用的方式非常多,最具可维护性的方式是使用”“模版”。为了不过多引入复杂性,就不使用 PHP 包管理器来为项目添加“模版引擎”了,我们来实现一个简单的模版类(不到 30 行):

  在完成简单的模版功能之后,我们就能够在应用中使用 来进行页面结果的渲染了。

  简单实现主要逻辑

  接下来,我们来实现“微博”的主要流程逻辑,大概 130 行左右的代码就能够搞定:

  简单实现页面模版

  完成主要程序实现之后,我们来实现页面模版,大概 120 行就能够搞定:

  使用 PHP 官方镜像验证程序

  为了方便后续的演示和性能对比,这里我们直接声明一些路径为 Nginx 容器的地址,所以当你看到后续 Apache 镜像中使用的路径,不必惊讶:

  在完成程序调整之后,我们简单编写一个 配置,来使用 PHP 官方提供的 Docker 镜像来验证程序是否能够正常运行:

  使用 启动程序之后,我们访问 就能够看到文章一开头的界面了。随便输入点内容,然后点击“发布” 按钮,能够看到一切符合预期,功能可以正常工作。验证是否能够完成核心功能:发微博

  确认程序能够正常工作后,我们来将程序迁移到 NGX-PHP 环境中。

  这部分的代码,可以参考项目的提交记录:soulteary/ngx-php-micro-blog/commit/d97385b945c998385cbc7dee813529f05b4f15d3[6]

  构建 NGX PHP 容器镜像

  这里,我们借助很早之前提到过的一个项目 https://github.com/nginx-with-docker/nginx-docker-playground 来快速完成 NGX PHP 这个 Nginx 模块的构建,关于“如何在容器时代高效使用 Nginx 三方模块”,可以参考这篇文章[7]。

  完成构建之后,我们使用多阶段构建,制作最终的应用镜像就好了:

  这个小节的完整代码,在这里可以找到: soulteary/ngx-php-micro-blog/Dockerfile[8]。使用 ,完成基础镜像构建,我们将得到一个 12MB 左右的小巧的、包含了 Nginx PHP 模块的镜像。小巧可爱的容器镜像

  在完成了基础镜像构建之后,我们来进行程序的“改造”。

  将 PHP 程序适配 NGX PHP 环境

  如果我们不修改任何代码,通过调整 配置文件,切换容器镜像和挂载的文件,是可以让程序在我们新构建的 NGX-PHP 镜像中运行的。

  但是我们会得到一些报错,导致程序不能正常运行。

  解决变量、函数重复定义的问题

  我们首先可能遇到的问题就是类似下面的报错,告诉我们重复声明了“某些内容”,比如常量:

  或者重复声明了“某些类”:

  出现这两个问题的原因,是因为 NGX PHP 模块中,“全局变量和静态变量”都是不安全的。

  解决第一个问题,我们可以有两个方案,降低声明的作用域,或者加上一些防御性判断:

  解决第二个问题,我们只能够依赖添加判断来避免重复声明:

  解决完毕上面两个问题,程序就能够正常展示界面了。

  解决参数获取不到的问题

  虽然解决了上面的问题,程序能够正常展示,但是我们会发现提交任何内容,程序都不会有“正确的反应”,而 Nginx 日志中也没有任何错误信息。

  出现这个问题的原因是,在 NGX PHP 环境下,PHP 获取用户提交数据的方式由 和 改为了 和 。

  为了解决这个问题,并且保持我们的程序依旧能够在官方 PHP 环境中运行、调试,可以实现一个简单的 方法,让程序兼容不同的环境:

  对应的,调整上文中程序获取用户输入数据的方法,就能够让程序正常的在 NGX PHP 容器中运行啦。

  最终应用程序

  最终的应用程序,算上换行大概 220 行左右:

  简单的性能比较

  除了相信相对中立的机构的测试结果之外,我们也可以自己进行应用性能测试,来验证 NGX-PHP 是否真的能够“降本增效”。

  下面我们就用上面最终实现好的程序,分别在我们构建的 镜像和 PHP 官方镜像 中进行简单的请求性能测试:

  先使用开启 OPCACHE 之后的官方镜像(),完成30s 的压力测试:

  接着,使用我们构建好的 NGX PHP 镜像,在不开启缓存的情况下进行测试:

  可以看到,性能提升还是比较明显的。

  最后

  好了,关于 NGX PHP 的第一篇文章就聊到这里吧。关于更多的细节,或许后面有机会,我会再写一两篇文章进行分享。

  --EOF

  引用链接

  Round 19: https://www.techempower.com/benchmarks/#section=data-r19&test=composite Round 21: https://www.techempower.com/benchmarks/#section=data-r21&test=composite 框架开销: https://www.techempower.com/benchmarks/#section=data-r21&test=db soulteary/ngx-php-micro-blog: https://github.com/soulteary/ngx-php-micro-blog 在笔记本上搭建高性价比的 Linux 学习环境:基础篇: https://soulteary.com/2022/06/21/building-a-cost-effective-linux-learning-environment-on-a-laptop-the-basics.html soulteary/ngx-php-micro-blog/commit/d97385b945c998385cbc7dee813529f05b4f15d3: https://github.com/soulteary/ngx-php-micro-blog/commit/d97385b945c998385cbc7dee813529f05b4f15d3 这篇文章: https://soulteary.com/2021/03/22/how-to-use-nginx-third-party-modules-efficiently-in-the-container-era.html soulteary/ngx-php-micro-blog/Dockerfile: https://github.com/soulteary/ngx-php-micro-blog/blob/main/Dockerfile

  我们有一个小小的折腾群,里面聚集了一些喜欢折腾的小伙伴。

  在不发广告的情况下,我们在里面会一起聊聊软硬件、HomeLab、编程上的一些问题,也会在群里不定期的分享一些技术沙龙的资料。

  喜欢折腾的小伙伴,欢迎阅读下面的内容,扫码添加好友。关于“交友”的一些建议和看法

  添加好友,请备注实名和公司或学校、注明来源和目的,否则不会通过审核。苏洋:关于折腾群入群的那些事

  如果你觉得内容还算实用,欢迎点赞分享给你的朋友,在此谢过。

  如果你想更快的看到后续内容的更新,请戳 “点赞”、“分享”、“喜欢” ,这些免费的鼓励将会影响后续有关内容的更新速度。

  本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 署名 4.0 国际 (CC BY 4.0)

  本文作者: 苏洋

  创建时间: 2022年10月05日 统计字数: 16668字 阅读时间: 34分钟阅读 本文链接: https://soulteary.com/2022/10/05/nginx-black-magic-low-cost-high-performance-applications-using-ngx-php-modules.html