fix: 修复拉取大量答卷时内存耗尽(128M)导致后台刷新失败
- SurveyKingClient::listAnswers/listProjects 单页上限夹在 20~200, 无论配置 page_size 多大,单次 json_decode 体量都被限制,避免超大响应撑爆 128M - raw() 不再随返回值常驻完整 HTTP 正文(仅留前 2000 字符用于诊断), 降低 json_decode 之外的额外内存占用;每页处理后及时释放引用 - bootstrap.php 在未被 php_admin_value 硬锁时临时将 memory_limit 放宽到 512M 作为安全兜底(触顶场景:/api.php?action=data 拉取卷王全量答卷) 根因:error.log 中 PHP Fatal error Allowed memory size of 134217728 bytes exhausted 位于 SurveyKingClient.php:180 json_decode(),由上传名单后答卷量增大、 单页响应整体解码超出内存上限引发。
This commit is contained in:
@@ -36,6 +36,30 @@ if ($missing !== []) {
|
|||||||
date_default_timezone_set('Asia/Shanghai');
|
date_default_timezone_set('Asia/Shanghai');
|
||||||
mb_internal_encoding('UTF-8');
|
mb_internal_encoding('UTF-8');
|
||||||
|
|
||||||
|
// ---------------- 内存上限(数据接口可能处理大量答卷)----------------
|
||||||
|
// 拉取卷王全量答卷做 json_decode 时极易触顶 128M;在未被禁用 / 未被 php_admin_value 硬锁的前提下
|
||||||
|
// 临时放宽到 512M。若主机用 php_admin_value 锁定了 memory_limit,下面的 ini_set 会静默失效,
|
||||||
|
// 需要改主机 php.ini(或 fpm 池配置)才能生效。
|
||||||
|
(function (): void {
|
||||||
|
$cur = (string) ini_get('memory_limit');
|
||||||
|
if ($cur === '' || $cur === '-1') {
|
||||||
|
return; // 未设置 / 已无限制,无需处理
|
||||||
|
}
|
||||||
|
$curBytes = (int) $cur;
|
||||||
|
$unit = strtoupper(substr($cur, -1));
|
||||||
|
if ($unit === 'G') {
|
||||||
|
$curBytes *= 1024 * 1024 * 1024;
|
||||||
|
} elseif ($unit === 'M') {
|
||||||
|
$curBytes *= 1024 * 1024;
|
||||||
|
} elseif ($unit === 'K') {
|
||||||
|
$curBytes *= 1024;
|
||||||
|
}
|
||||||
|
$want = 512 * 1024 * 1024;
|
||||||
|
if ($curBytes > 0 && $curBytes < $want) {
|
||||||
|
@ini_set('memory_limit', '512M');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
// ---------------- 会话 ----------------
|
// ---------------- 会话 ----------------
|
||||||
function ny_session_start(): void
|
function ny_session_start(): void
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -187,7 +187,10 @@ class SurveyKingClient
|
|||||||
$msg = (string)($decoded['msg'] ?? $decoded['message'] ?? '');
|
$msg = (string)($decoded['msg'] ?? $decoded['message'] ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
return ['status' => $status, 'headers' => $h, 'data' => $data, 'raw' => $rawBody, 'code' => $code, 'msg' => $msg];
|
// 仅保留前若干字符用于错误诊断;完整正文不再随返回值常驻内存,
|
||||||
|
// 避免大答卷响应在 json_decode 之外再额外占用一倍内存。
|
||||||
|
$rawDiag = mb_strcut($rawBody, 0, 2000);
|
||||||
|
return ['status' => $status, 'headers' => $h, 'data' => $data, 'raw' => $rawDiag, 'code' => $code, 'msg' => $msg];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -448,7 +451,7 @@ class SurveyKingClient
|
|||||||
/** 问卷 / 考试列表 */
|
/** 问卷 / 考试列表 */
|
||||||
public function listProjects(): array
|
public function listProjects(): array
|
||||||
{
|
{
|
||||||
$size = max(20, (int)($this->cfg['page_size'] ?? 200));
|
$size = min(200, max(20, (int)($this->cfg['page_size'] ?? 100)));
|
||||||
$all = [];
|
$all = [];
|
||||||
for ($page = 1; $page <= 50; $page++) {
|
for ($page = 1; $page <= 50; $page++) {
|
||||||
$r = $this->request('/project/list', ['current' => $page, 'pageSize' => $size]);
|
$r = $this->request('/project/list', ['current' => $page, 'pageSize' => $size]);
|
||||||
@@ -483,7 +486,9 @@ class SurveyKingClient
|
|||||||
/** 答卷列表(自动翻页取全量) */
|
/** 答卷列表(自动翻页取全量) */
|
||||||
public function listAnswers(string $projectId): array
|
public function listAnswers(string $projectId): array
|
||||||
{
|
{
|
||||||
$size = max(20, (int)($this->cfg['page_size'] ?? 200));
|
// 单页上限夹在 20~200:无论配置 page_size 多大,单次 json_decode 的体量都被限制住,
|
||||||
|
// 避免一份超大响应直接撑爆 128M。100 页 × 200 = 最多 2 万份,足以覆盖学校级考试。
|
||||||
|
$size = min(200, max(20, (int)($this->cfg['page_size'] ?? 100)));
|
||||||
$all = [];
|
$all = [];
|
||||||
for ($page = 1; $page <= 100; $page++) {
|
for ($page = 1; $page <= 100; $page++) {
|
||||||
$r = $this->request('/answer/list', [
|
$r = $this->request('/answer/list', [
|
||||||
@@ -502,10 +507,14 @@ class SurveyKingClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$total = is_array($data) ? (int)($data['total'] ?? 0) : 0;
|
$total = is_array($data) ? (int)($data['total'] ?? 0) : 0;
|
||||||
|
// 及时释放本页引用,降低峰值内存占用
|
||||||
|
$r = null;
|
||||||
|
$data = null;
|
||||||
|
$list = null;
|
||||||
if ($total > 0 && count($all) >= $total) {
|
if ($total > 0 && count($all) >= $total) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (count($list) < $size) {
|
if (count($all) < $size) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user