菜皮日记

菜皮日记

马上订阅 菜皮日记 RSS 更新: https://www.lipijin.com/feed

PHP 这么拉?长连接都搞不了?说说 PHP 的 socket 编程

2024年8月13日 00:00

对 PHP 的误解颇深

网络上似乎存在一种现象,一提到 PHP 人们的第一反应是简单且慢,这种简单甚至已经到了简陋的地步,比如不少人认为 PHP 无法独立创建一个服务,只能配合 Apache 或 Nginx 一起使用,而且 PHP 只能在处理完请求后销毁资源关闭进程,所以也无法处理长连接业务,这些都是对 PHP 的误解,我想这种误解的形成可能与 PHP 的发展历史有关,实际上 PHP 能做的有很多,下面就先从 PHP 的发展历史说起。
 

PHP 的发展简史

在我看来,PHP 的发展路线确实与其他主流编程语言不太相同。PHP 天生就是为了 Web 而生的,早期的 Web 网页都是静态的,例如在个人主页上展示一些固定的个人信息,为了能够让网页展示一些动态的统计数据和简单的交互,Rasmus Lerdorf 在 1995 年开发了 Personal Home Page 工具集合,简称为 PHP,PHP 通过 CGI 协议与 Web 服务器交互,通过实时计算生成动态的内容。这里可能是与其他主流编程语言差别最大的点,其他语言的运行环境要么是通过编译后直接执行,要么是在命令行中调用解释器执行的。
因为 PHP 最初的目标就是做一些简单的计算,所以并不具备主流编程语言中的一些高级特性,后来越来越多的网站开始使用 PHP 并希望能提供更多的功能,之后 Lerdorf 将 PHP 开源,在这之后 Zeev Suraski 和 Andi Gutmans 重写了 PHP 的解析器,并从此开始 PHP 改为 Hypertext Preprocessor,新版的解析器命名为 Zend Engine,Zend 的命名来自于两位作者的名字。至此 PHP 支持了面向对象、命名空间等特性,已经脱胎换骨成为了一门完善的编程语言。在 2015 年 PHP 7 发布,重构了 PHP 中很多重要且常用的数据结构,内存占用得到显著优化,性能也得到了大幅提升。
 

火爆的 LAMP 架构

虽然 PHP 经过几次版本迭代已经具备了现代编程语言的必要特性,但依旧有很多人对 PHP 有着类似前面提到的种种误解,造成这种误解的原因很大程度上是因为曾经 Web 领域中应用最广泛的架构 - LAMP 实在是太火了。在这套架构中 Linux 作为操作系统,MySQL 用于数据存储,Apache 负责处理网络连接和 HTTP 协议,而 PHP 放在其后面负责处理动态内容。由于这套架构简单有效且开源免费,可以低成本快速搭建起一个可用的服务,这对于初创团队业务试错来说十分具有吸引力,一度出现了很多一键安装的集成软件包,让这套架构的上手门槛进一步降低,但长此以往可能让不少人以为 PHP 只能配合 Apache 或 Nginx 使用,而 PHP 远不止于此。放在 Apache 或 Nginx 后面只是 PHP 运行模式的一种,也就是 CGI 模式,此外 PHP 支持其他模式,下面做一个对比。
 

PHP 运行的几种模式

按我的理解,PHP 运行模式严格来说就分两种,CGI 模式和 CLI 模式,CGI 后来衍生出了 Apache mod、FastCGI、FPM 等模式。
 

CGI 模式

CGI (Common Gateway Interface)通用网关接口是一种协议,是早期 Web 服务器与外部程序交互的一种方式,Web 服务器与外部程序之间通过环境变量、标准输入和标准输出交换数据。
CGI 的 logo 是一个三棱镜,其中一束光穿过三棱镜被分解成不同颜色,象征着 CGI 可以将网络请求分解并传递给不同应用程序处理,展现出了 CGI 的多样性和灵活性。
notion image
遵循 CGI 协议的 Web 服务器一般会有一个名为 cgi-bin 的目录,目录下面默认都是可执行 CGI 脚本文件,如果前端访问到了这些文件那么 Web 服务器并不会像处理普通文件那样直接将文件返回给前端,而是会 fork 出子进程并在子进程中运行指定的 CGI 脚本,脚本运行完成后通过标准输出将结果返回给 Web 服务器,并关闭子进程。
运行前 Web 服务器会将一些必要的请求信息设置在环境变量中,CGI 脚本运行后便可以通过读取环境变量得到这些请求信息,例如 uri、请求参数等。CGI 脚本的标准输出会重定向给 Web 服务器,服务器接到输出后返回给前端,这就是为什么早期的 CGI 模式下运行的 PHP 程序可以通过 echo 来返回结果的原因。
这种模式特点是比较简单,并且由于每次处理完成后都会销毁进程和资源,所以也不会出现内存泄漏等问题,但缺点是由于每次都需要重新创建新的进程并销毁,性能开销较大,也无法利用到长连接或池化技术,在处理大量并发请求时处理能力较低。
 

FastCGI 模式与 PHP-FPM

为了解决 CGI 模式下每次都要新建子进程并销毁子进程导致的性能低下问题,FastCGI 模式在 CGI 基础上做出了改进,这种模式下会预先创建出一些 CGI 进程常驻内存,当有请求到来时会分配一个空闲进程处理,完成后并不销毁而是作为空闲进程重新等待处理请求。
FastCGI 是协议,而 PHP-FPM 是 FastCGI 的实现,全称为 PHP FastCGI Process Manager。这种模式根本上还是基于 CGI 模式衍生出来的,主要优化的是引入常驻内存特性以及多个...

剩余内容已隐藏

查看完整文章以阅读更多