PHP 多进程和多线程的优缺点

那天,我在调试公司的订单处理系统时,后台几个进程突然崩溃了,但主进程仍在运行,订单完好无损。
当我看着显示器上的 CPU 峰值时,我突然想到,多处理和多线程之间的权衡无法用一两句话清楚地解释清楚。

例如,使用pcntl_fork将处理任务拆分为1 0个子进程,直接将内存增加到1 .5 G。
这还没有结束。
当子进程终止时,资源恢复缓慢,CPU 被占用等待。
但另一方面,使用线程会导致计算密集型顺序检查任务,5 个线程宕机,锁争用程度很高,日志中充满了警告:无法锁定()...我检查了信息。
当 PHP5 .3 于 2 01 3 年左右首次添加 pthreads 扩展时,文档明确指出应谨慎使用,因为死锁的可能性很高。
那时,我们使用消息队列将任务填充到 RabbitMQ 中。
父进程只是检索消息并将其发送到队列,子进程从队列中检索任务并执行它们。
它确实很稳定,但消息队列延迟有时可能会令人沮丧。
在高峰时段,我们不得不等待几秒钟才能轮到我们。
然后我切换到 swoole 多处理,内存和 CPU 曲线平坦得多。
但配置文件中worker_num的适当设置仍然取决于压力测试。
我记得去年的测试中,8 个进程比 1 6 个进程更快,并且使用的内存更少。
当时我很困惑,但后来意识到某些任务特别是CPU密集型的,并且随着进程数量的增加,CPU切换成本也随之增加。
这很奇怪。
按理说,任务越多,进程就会越快。

突然,我想起了另一个细节。
2 01 5 年,Linux服务器上的PHP-FPM进程数设置为5 0,导致系统宕机。
但当我切换到 Windows 时,相同的配置运行得非常好。
这与操作系统对多进程/线程的支持直接相关。
Linux 上的 fork() 比 Windows 上优化得多。
但是,当您在 Windows 上使用 pthreads 扩展时,调试器中所有线程的状态都是运行,您无法确定哪些线程正在等待锁。
我尝试使用Xdebug进行跟踪,但结果是CPU已满,我看不到任何输出。
我最终不得不依靠 usleep() 来让位于线程并一一读取日志。
这种调试体验真是太棒了。

因此,您在选择技术方案时,还是需要根据您的具体场景进行选择。
后台任务队列。
只要你有足够的内存,稳定性是优先考虑的,多处理是通常的方法。
然而,与 API 接口一样,如果请求很短,CPU取决于核心数量和锁争用。
在某些情况下,异步协程比多线程更麻烦。
但最终,最安全的方法是使用云服务提供商的负载平衡将请求分发到不同的机器。
每台机器都在独立的进程中运行您的 PHP 应用程序,无需担心任何进程间问题。
这一招虽然简单粗暴,但却确实让人放心。
等等,还有别的事。
我见过使用多个进程的旧系统。
如果子进程崩溃了,父进程不会注意到。
结果花了两天时间才发现任务积压。
当时运维人员差点被上级骂了。
如果这个监控机制不完善的话,多处理的稳定性收益就直接降为零。

如何处理PHP的多线程和并发?

PHP 没有内置的多线程。
有两种方法可以实现并行性。

其中之一是多进程。
使用 pcntl_fork() 创建子进程。
每个请求都会单独处理。
适用于多核处理器。
但进程间通信是有问题的。

第二个是协程。
使用 Swoole 扩展实现。
在一个线程中处理多个请求。
避免切换线程的开销。
性能好,并行度高。

还有一个后台任务队列。
使用 RabbitMQ 或 Cron 异步处理耗时的任务。
主要请求很快返回。
减少服务器的负载。

缓存也很重要。
使用 Memcached 或 Redis 缓存数据。
减少数据库查询次数。
避免重复计算。

代码和数据库也应该优化。
循环较少。
减少冗余。
SQL 使用索引。
使用准备好的语句。

粗略地说,要实现并行性,必须综合运用PHP。
需要多处理、协程、队列、缓存和代码优化。
高并发依赖Swoole和Redis。
代码效率是最重要的。

你自己看看。