1 回答
TA贡献2012条经验 获得超12个赞
OP 有许多棘手的问题摆在桌面上。然而,我觉得这些都是值得关注的(我自己也曾与它们斗争过),所以让我们把它拆开。为了大正义;主屏开启:
解决并发请求问题
(L)AMP 堆栈中的并发连接存在多种可能的问题和解决方案。然而,在讨论调整 Apache 和 MySQL 之前,让我先介绍一个常见的“神秘”问题,该问题会导致并发问题;即,一个必要的罪恶称为“ PHP 会话锁定”。
PHP 会话阻塞和并发请求
简而言之:当您在应用程序中使用会话时,调用 后session_start()
,PHP 会锁定存储在您目录中的会话文件session.save_path
。该文件锁将保持不变,直到脚本结束或被session_write_close()
调用。结果:同一用户的任何后续调用都将排队,而不是同时处理,以确保不会损坏会话数据。(想象一下并行脚本写入相同的内容$_SESSION
!)
演示这一点的一个简单方法是创建一个长时间运行的脚本;然后在浏览器中调用它;然后打开一个新选项卡,并再次调用它(或者实际上,调用共享相同会话 cookie/ID 的任何脚本)。您将看到,在第一个调用结束之前,第二个调用不会执行。这是奇怪的 AJAX 延迟的常见原因,尤其是来自单个页面的并行 AJAX 请求。处理将是连续的而不是并发的。然后,10 个调用,每次调用 0.3 秒,总共需要 3 秒才能结束,依此类推。我们不希望那样,是吗!
您可以通过确保以下内容来补救由 PHP 会话锁定引起的请求阻塞:
使用会话的脚本应在会话数据存储完成后调用
session_write_close()
。会话锁将立即释放。不需要会话的脚本一开始就不应该启动会话。
只需要读取会话数据的脚本:使用
session_start()
with['read_and_close' => true]
选项将为您提供一个只读(非持久)$_SESSION
变量,而无需会话锁定。(自 PHP 7 起可用。)
选项 1 和 3 将为您提供对$_SESSION
变量的读取访问权限并释放/避免会话锁定。$_SESSION
会话关闭后所做的任何更改都将被默默丢弃;不显示警告/错误。
会话锁定请求阻塞问题仅对单个用户(使用同一会话)产生影响。
Apache 和 MySQL 并发请求
曾几何时,在意识到 PHP 是阻塞/排队我的并发调用的罪魁祸首之前,我花了一小段时间来调整 Apache 和 MySQL,并想知道会发生什么?
Apache 2.4默认支持150个并发请求;任何进一步的请求都将排队。MPM/多处理模块下有多个设置,您可以调整它们以支持所需的并发连接级别。
MySQLmax_connections
有(默认 151)和max_user_connections
(默认无限制)选项。如果您的应用程序为每个用户发送大量并发请求,您将需要确保全局最大连接足够高,以确保少数用户不会占用整个 DBMS。
取消对 Apache/PHP/MySQL 的请求
就您的应用程序的具体接线而言,我们没有太多可做的,但我从评论中了解到,就目前情况而言,用户可以在前端取消请求,但不会采取任何后端操作。(即任何后端响应都会被忽略/丢弃。)
“有没有办法让 Apache 删除已取消的请求?” 我假设您的前端直接无延迟地将请求发送到 Apache;然后转到 PHP > MySQL > PHP > Apache。在这种情况下,不,你不能真正让 Apache 取消它已经收到的请求;或者你可以点击“停止”,但很可能 PHP 和 MySQL 已经把它吃掉了......
持有“取消窗口”
但是,您可以将“取消窗口”延迟编程到前端,其中请求仅在等待可能的取消的 0.5 秒睡眠后才传递到 Apache。这可能会对用户体验产生负面影响,也可能不会产生负面影响;如果取消了很大一部分请求,则可能值得实施以节省服务器资源。这假设 UI 带有 Javascript。如果您直接对 API 进行 HTTP 调用,则可以使用“休眠代理接收器”。
使用“取消控制器”
如何取消 PHP/MySQL 进程?显然,只有当对 API 的调用导致处理时间较长时,这才是可行的/可行的。如果后端需要 0.28 秒来处理,并且用户在 0.3 秒后取消,那么就没有太多可以取消的了,是吗?
但是,如果您确实有可能运行更长时间的脚本,例如几秒钟。您总是可以在代码中找到相关的断点,其中有“未取消”检查或终止/回滚例程。基本上,您将具有以下流程:
前端将带有唯一ID的请求发送到主脚本
PHP 脚本开始构建响应的长征
取消时:前端将 ID 重新发送到轻量级取消控制器
取消控制器将 ID 记录到临时文件/数据库/任何地方
PHP 在断点处检查当前进程是否有取消请求
取消时,PHP 执行终止/回滚例程而不是进一步处理
这种“取消监视”显然会产生一些开销,因此您可能只想将其合并到较重的脚本中,以确保您实际上在整体上节省了一些处理时间。此外,您最多只需要在重要的交汇处设置几个断点。对于读取请求,您可以直接终止该进程;但对于写入请求,您可能希望进行正常回滚以确保系统中的数据完整性。
您还可以使用mysqli:: kill 取消/终止已由 PHP 启动的长时间运行的 MySQL 线程。为了使这一点有意义,您需要将其运行为MYSQLI_ASYNC
,这样 PHP 就可以停止使用了。PDO 似乎没有针对异步查询或终止的本机等效项。
PHP 连接处理
作为“从侧面”传递取消信号的控制器的替代方案,您可以查看PHP 连接处理并使用connection_aborted()
. (有关代码示例,请参阅上面的“MySQL Kill”链接。)
CONNECTION_ABORTED
如果用户单击浏览器中的“停止”按钮,则会出现一种状态。PHP 有一个ignore_user_abort()
设置,默认为“Off”,它应该在用户中止时中止脚本。(不过,根据我的经验,如果我有一个流氓脚本并且会话锁定处于打开状态,那么即使我在浏览器中点击“停止”,我也无法执行任何操作,直到超时。想想看。)
如果您将“忽略用户中止”设置为 false,即。PHP 脚本在用户中止时终止,请注意,这将是完全不受控制的终止,除非您已register_shutdown_function()
实现。即便如此,您也必须在代码中标记检查点,以便关闭函数能够从终止点向前“倒回时钟”。另请注意此警告:
我没有通过 AJAX/JS 实现“用户中止”的经验。
- 1 回答
- 0 关注
- 80 浏览
添加回答
举报