配置 Apache 和 MySQL 为客户端提供并行且可取消的服务

我们有一个客户端服务器架构,客户端使用 Angular,服务器端使用 Apache2 PHP PDO 和 MySQL。服务器端向客户端公开 API,为客户端提供要显示的数据。

一些观察:

  1. 某些 API 调用可能需要很长时间才能计算并返回响应。

  2. 服务器端似乎在任何给定时间处理每个客户端的单个请求(我只看到在 mysql 中执行的一个相应查询),该限制来自 apache 或来自 mysql,因为前端肯定会并行发送请求。

  3. 前端取消不再相关的请求(正在获取的数据将不可见)

  4. 似乎前端取消的请求不会在服务器端取消并继续运行,我认为即使它们排队,它们在轮到时仍然会运行(即使它们在客户端被取消)

需要帮助理解:

  1. 没有让所有请求(或至少 X>1 个请求)并行运行的确切原因是什么?可以改变吗?

  2. 我应该在 apache 或 mysql 中更改哪些配置来克服这个问题?

  3. 有没有办法让 apache 删除已取消的请求?至少那些仍在排队且未开始的?

谢谢!

编辑

在@Markus AO评论之后(感谢 Markus!!!),这是与会话阻塞相关的...希望我之前知道这一点!


白板的微信
浏览 81回答 1
1回答

繁花如伊

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()实现。即便如此,您也必须在代码中标记检查点,以便关闭函数能够从终止点向前“倒回时钟”。另请注意此警告:在尝试向客户端发送信息之前,PHP 不会检测到用户已中止连接。我没有通过 AJAX/JS 实现“用户中止”的经验。
打开App,查看更多内容
随时随地看视频慕课网APP