---
layout: post
title: PHP异步执行的探索
tags: [PHP, 异步]
---

  看来异步总是个难以解决的问题啊……<!--more-->    

# 起因
  最近，我在写一个ThinkPHP框架为基础的代码时遇到了一个问题，我希望在执行完一个操作后立马返回数据，然后在后台继续运行一个比较耗时的东西。我以前写的一个[图片机器人](/2021/02/23/picbotpro.html)其实也遇到过这种问题，为了解决返回很慢的问题，我采用了“离弦之箭”——使用file_get_contents函数请求自己，并且使用'timeout'参数来防止页面挂起，另外接收的部分使用`ignore_user_abort(true)`和`set_time_limit(0)`保证在请求结束时让程序依然继续运行。不过我用的是TP框架啊，不知道用那两个函数会不会出问题，还有这个“timeout”到底填多少比较合适也不确定，所以就想试试别的方法。   

# 探索异步的方法
  对于正规的情况来说，直接去网上搜如何让PHP异步执行代码，估计大多会说使用什么消息队列或者用什么Swoole框架吧，或者简单点用popen函数来fork一个进程，或者用什么fsockopen，貌似原理和我用file_get_contents函数差不多？其实吧我觉得从本质来说那个所谓的Swoole框架已经不算PHP的东西了，我觉得它更像是一个能通过http请求的popen函数，通过某种方式也一样是在本地去调用PHP脚本，只是可能用了些什么比如线程啥的特性提高了性能而已。   
  我看完这些选择之后很不满意，我不希望代码里出现popen这样的函数，说实话这是个危险的函数，绝大多数情况下服务器应该要禁用这种函数的，毕竟如果因为某些原因被人上传了PHP木马，他们可以直接用这个函数去执行命令了。至于什么乱七八糟的异步框架，想不想学是一方面，主要是我的项目还没有达到用这种东西的规模。那难不成我只能继续用之前图片机器人的那个“离弦之箭”的办法了吗？   
  后来我找到了一个很不错的函数，叫做[fastcgi_finish_request](https://www.php.net/manual/zh/function.fastcgi-finish-request.php)，它可以在程序结束前把要输出的东西输出并且结束请求，不过使用它以后看文档说明好像说推荐再执行一下session_write_close函数，不然在这个程序执行完成前session会被锁住，没法操作。   
  不过这个函数只能在使用PHP-FPM的时候使用，如果是像Apache那样使用模块的方式运行PHP估计就用不了了，当然目前大多数环境应该都用的是Nginx+PHP-FPM吧，我反正是很少见到有人用Apache，虽然听说那样性能似乎会更好？   
  还有一个问题就是如果这个需要运行的脚本的时间太长了，以至于同时运行这个脚本的进程数量超过了pm.max_children，那么PHP-FPM就不能接受新的请求了，这也是一个缺陷。   
  另外在这种情况下我使用了TP框架还会遇到一个问题，那就是我不能使用return返回内容了，毕竟return了之后就不能执行其他函数了啊，所以就只能提前用echo之类的东西输出，执行完fastcgi_finish_request和所有需要长时间运行的代码后就只能return空值或者直接exit吧。   

# 感想
  看来用PHP来做异步果然是难事啊，不如说大多数程序想要处理好多线程的问题都挺难的吧，也许如果项目经常遇到这种问题，应该考虑用其他语言来编写了呢。