引言
本文件聚焦于 DouPHP 的「站点安装报告与数据分析系统」,即站点在安装、升级模块/主题/插件/小程序以及系统内核时,如何采集匿名基础遥测并上报到云端,用于统计安装/升级事件与站点环境快照。该系统以「零 admin 依赖、尽力而为、绝不阻断安装流程」为设计原则,通过 SiteReportService 统一封装采集与上报逻辑,由后台安装流程在收尾阶段触发上报。
最新更新:系统已增强大文件下载性能、模块配置写入并发安全、OPcache失效处理,以及新增邮件模块升级机制和会话超时延长至60分钟。
项目结构
DouPHP 采用「三端入口 + 共享核心 + 模块化」架构:前台 front、后台 admin、API api 各自独立入口,共享 core 核心层;业务按模块组织,配置集中于 config,运行时状态与缓存位于 storage。安装与升级相关的数据上报能力属于 core 层的云服务能力,被 admin 端的安装服务调用。
graph TB
A["前台入口<br/>index.php"] --> B["核心引导<br/>core/bootstrap.php"]
C["后台入口<br/>admin/index.php"] --> D["后台控制器<br/>CloudController"]
D --> E["安装服务<br/>InstallService"]
E --> F["站点上报服务<br/>SiteReportService"]
F --> G["云端 API<br/>CloudApi::postJson"]
H["存储目录<br/>storage/install/*"] -.-> E
I["配置中心<br/>Config / cloud.php"] -.-> F
J["会话服务<br/>InstallSessionService"] -.-> E
K["邮件模块升级<br/>email/_update"] -.-> E
核心组件
- 安装流程编排者:后台安装服务负责多步安装(预检、下载、解压、应用、收尾),并在收尾阶段触发上报。
- 站点上报服务:封装站点环境快照采集、开关判断、云端地址校验、异常吞掉等逻辑,保证上报失败不影响安装。
- 安装会话服务:管理安装会话的生命周期,提供60分钟超时支持和并发安全的文件锁机制。
- 前端入口与异常处理:前台入口负责路由分发与全局异常渲染,确保 JSON 请求返回结构化错误,HTML 请求走提示页或调试页。
新增功能:
- 大文件流式下载:支持~10M系统升级包的高效下载
- 并发安全的配置写入:使用文件锁和OPcache失效处理
- 邮件模块升级:支持email模块的数据迁移和版本升级
架构总览
下图展示从后台安装流程到云端上报的端到端时序,包含新增的大文件下载和会话管理机制。
sequenceDiagram
participant Admin as "后台控制器"
participant Install as "安装服务 InstallService"
participant Session as "会话服务 InstallSessionService"
participant Report as "站点上报服务 SiteReportService"
participant Cloud as "云端接口 CloudApi"
Admin->>Install : "runPreflight(type, cloud_id, mode, version)"
Install->>Session : "create(params)"
Session-->>Install : "install_id"
Admin->>Install : "runDownload(download_url)"
Install->>Install : "downloadFile() 流式下载"
Install->>Install : "attemptStreamDownload()"
Install->>Install : "完整性校验 + 重试机制"
Admin->>Install : "runUnzip()"
Admin->>Install : "runApply()"
Install->>Install : "changeModulePhp() 并发安全写入"
Install->>Install : "OPcache失效处理"
Admin->>Install : "runFinalize()"
Install->>Report : "reportSystem() 或 reportExtend()"
Report->>Report : "ensureCloudConfig()"
Report->>Report : "collectSite()"
Report->>Cloud : "POST /report/site"
Cloud-->>Report : "HTTP 响应"
Report-->>Install : "void静默"
Install-->>Admin : "返回结果包含 next_install"
详细组件分析
安装服务中的上报触发点
- 触发时机:安装收尾阶段
runFinalize,在写入审计日志之后、构建成功按钮之前。 - 触发条件:
- 系统升级:调用
reportSystem(eventType, toVersion, fromVersion),其中fromVersion来自runApply阶段暂存到 Session 的值。 - 扩展安装/升级(module/theme/plugin/miniprogram):调用
reportExtend(targetType, slug, eventType, toVersion)。
- 系统升级:调用
- 容错策略:对上报调用整体 try/catch,捕获 Exception 与 Throwable,确保任何异常都不会阻断收尾流程。
flowchart TD
Start(["进入 runFinalize"]) --> Audit["记录审计日志"]
Audit --> TryReport{"是否可上报?"}
TryReport --> |是| CallReport["调用 SiteReportService"]
TryReport --> |否| BuildButtons["构建成功按钮"]
CallReport --> CatchErr{"是否发生异常?"}
CatchErr --> |是| Ignore["静默忽略"]
CatchErr --> |否| BuildButtons
Ignore --> BuildButtons
BuildButtons --> Next["计算下一个待安装项"]
Next --> End(["返回结果"])
大文件下载性能优化
新增功能:针对~10M系统升级包的性能优化,采用流式下载替代内存加载。
关键改进:
- 流式下载:使用
Client::request()的stream_to参数直接落盘,避免整包读入内存 - 完整性校验:ZIP归档签名判定 + Content-Length比对 + ZipArchive结构验证
- 智能重试:对瞬时网络错误、截断包、空响应等场景自动重试最多3次
- 错误分类:区分上游不可用、权限不足、包损坏等不同错误类型
flowchart TD
Start(["downloadFile()"]) --> Clean["清理旧包文件"]
Clean --> Attempt["尝试流式下载"]
Attempt --> CheckCode{"HTTP状态码检查"}
CheckCode --> |非2xx| Classify["分类错误类型"]
CheckCode --> |2xx| Validate["ZIP完整性验证"]
Validate --> Signature["文件头签名检查"]
Signature --> Length["Content-Length比对"]
Length --> Structure["ZipArchive结构验证"]
Structure --> Success{"验证通过?"}
Success --> |是| Return["返回文件路径"]
Success --> |否| Retry["判断是否可重试"]
Retry --> |是| Backoff["退避等待后重试"]
Retry --> |否| Fail["返回失败"]
Classify --> Retry
Backoff --> Attempt
模块配置写入并发安全
新增功能:实现config/module.php文件的并发安全写入和OPcache失效处理。
关键特性:
- 文件锁保护:使用
file_put_contents()的LOCK_EX标志防止并发写冲突 - OPcache失效:写盘成功后调用
opcache_invalidate()确保下一请求重新编译 - 原子性操作:先完整构建输出数组,再一次性写盘,避免部分写入
- 键序保持:按原文件键序回写,保持配置文件结构一致性
flowchart TD
Start(["changeModulePhp()"]) --> Read["读取原始账本数据"]
Read --> Modify["修改模块标识和开关"]
Modify --> Build["构建输出数组"]
Build --> Write["file_put_contents(LOCK_EX)"]
Write --> Invalidate{"OPcache可用?"}
Invalidate --> |是| Inval["opcache_invalidate()"]
Invalidate --> |否| Skip["跳过失效处理"]
Inval --> Success["返回true"]
Skip --> Success
安装会话服务与会话超时
新增功能:安装会话服务支持60分钟超时,专为大体积系统升级包优化。
关键特性:
- 60分钟TTL:
TTL_SECONDS = 3600,适应共享主机上的长时间下载 - 文件锁机制:使用
flock()确保同一会话的并发请求串行化 - 智能GC:构造时清理过期会话文件,避免目录无限增长
- 健壮性设计:即使目录被删除也能重建,确保会话存续
classDiagram
class InstallSessionService {
+const SESSION_DIR = 'storage/install/session/'
+const TTL_SECONDS = 3600
+create(params) string
+load(installId) array|null
+save(installId, state) void
+delete(installId) void
+withLock(installId, fn) array|null
+pushLog(state, step, level, text) array
}
邮件模块升级机制
新增功能:新增email模块的升级机制,支持数据迁移和版本管理。
升级流程:
- 版本检测:检查客户端版本是否满足最低要求
- SQL升级:执行数据库结构变更和时间字段标准化
- PHP升级:运行自定义升级逻辑
- 缓存清理:升级完成后清理系统缓存
flowchart TD
Start(["email模块升级"]) --> CheckVersion["检查客户端版本"]
CheckVersion --> MinVersion{">= 20180703?"}
MinVersion --> |否| Skip["跳过升级"]
MinVersion --> |是| SQLUpgrade["执行SQL升级"]
SQLUpgrade --> PHPUpgrade["执行PHP升级"]
PHPUpgrade --> ClearCache["清理缓存"]
ClearCache --> End(["升级完成"])
Skip --> End
站点上报服务 SiteReportService
职责与行为:
- 提供两类上报方法:
reportSystem(eventType, toVersion, fromVersion):核心版本安装/升级事件。reportExtend(targetType, slug, eventType, toVersion, name):扩展(模块/主题/插件/小程序)安装/升级事件。
- 唯一出口
send(event):- 按需加载
config/cloud.php,确保安装器上下文可用。 - 检查开关
cloud.report_site与cloud.api_base,任一缺失则静默跳过。 - 采集站点快照
collectSite(),合并事件字段后 POST 到PATH_REPORT_SITE。 - 所有异常与 PHP Error 均被吞掉,绝不影响安装/升级。
- 按需加载
- 站点快照字段:
- domain、site_url、system_sign、cms_version、theme、language、php_version、mysql_version、server_software、os、cloud_account。
- 域名解析:
- 优先 ROOT_URL,回退 HTTP_HOST,去除端口并转小写。
- MySQL 版本探测:
- 若容器未绑定 Connection(如安装收尾请求),返回空串。
- 云账号标识:
- 仅读取
site.cloud_account.user,不携带 password。
- 仅读取
classDiagram
class SiteReportService {
+reportSystem(eventType, toVersion, fromVersion) void
+reportExtend(targetType, slug, eventType, toVersion, name) void
-send(event) void
-ensureCloudConfig() void
-collectSite() array
-detectDomain() string
-detectMysqlVersion() string
-detectCloudAccount() string
}
前台入口与异常处理
前台入口负责:
- 设置路由委托、语言前缀解析、Init 启动、路由分发。
- 捕获业务异常、重定向异常、HTTP 响应异常,并按 JSON 或 HTML 输出。
- 未捕获异常统一走
front_render_uncaught,根据 site.debug 输出调试页或通用错误。
flowchart TD
Entry["入口 index.php"] --> Boot["Init 启动"]
Boot --> Dispatch["路由分发"]
Dispatch --> Response{"是否 Response 对象?"}
Response --> |是| Send["发送响应并退出"]
Response --> |否| Continue["继续"]
Continue --> CatchHttp["捕获 HttpResponseException"]
CatchHttp --> CatchRedirect["捕获 RedirectException"]
CatchRedirect --> CatchDomain["捕获 DomainException"]
CatchDomain --> CatchGeneral["捕获 Exception/Throwable"]
CatchGeneral --> RenderUncaught["统一异常渲染"]
依赖关系分析
- InstallService 依赖:
- CacheClearService(清理模板缓存)、DB(表前缀与 SQL 执行)、Session(暂存旧版本)、Zip(解压)、FileHelper(文件操作)、RouteTableExporter/JsLangExporter(资源导出)、Client(网络请求)。
- 最终通过 app() 解析 SiteReportService 实例并调用上报。
- 新增:InstallSessionService(会话管理)、OpCache(缓存失效)。
- SiteReportService 依赖:
- Config(读取 cloud 配置)、CloudApi(POST 上报)、DB(可选探测 MySQL 版本)。
- 使用 ROOT_PATH、ROOT_URL、SYSTEM_SIGN、PHP_VERSION、PHP_OS、$_SERVER 等运行期常量与环境变量。
graph LR
Install["InstallService"] --> App["app() 解析"]
App --> Report["SiteReportService"]
Install --> Session["InstallSessionService"]
Install --> OpCache["OPcache"]
Report --> Config["Config"]
Report --> CloudApi["CloudApi"]
Report --> DB["DB可选"]
性能与可靠性
- 上报开销极低:仅在安装/升级收尾阶段触发一次,且开关关闭或未配置云端地址时直接跳过。
- 安装器上下文自愈:若 Config 尚未 bootstrap,按需加载
config/cloud.php,避免额外依赖。 - 异常吞掉:无论网络异常、配置异常、类解析异常,均不阻断安装流程。
- 最小化敏感信息:仅采集匿名基础遥测,不包含密码,云账号只取 user。
新增性能优化:
- 大文件下载优化:流式下载避免内存溢出,支持~10M系统升级包
- 并发安全写入:文件锁保护config/module.php,防止并发写冲突
- OPcache失效:配置变更后立即失效缓存,确保最新配置生效
- 长会话支持:60分钟会话超时,适应长时间下载场景
故障排查指南
常见问题与定位建议:
- 未收到上报:
- 检查
cloud.report_site是否为 true。 - 检查
cloud.api_base是否已配置。 - 确认
config/cloud.php是否存在并可读。
- 检查
- 上报失败但不影响安装:
- 这是预期行为;查看后端日志与网络抓包,确认 CloudApi 是否可达。
- 站点环境字段为空:
- cms_version 可能为空(安装收尾阶段),但核心事件会用 to_version 填充。
- mysql_version 在未绑定数据库连接时返回空串。
- domain 解析失败时回退 HTTP_HOST,仍可能为空。
- 云账号标识为空:
- 检查
site.cloud_account是否序列化正确且包含 user 字段。
- 检查
新增问题排查:
- 大文件下载失败:
- 检查网络连接和服务器防火墙设置
- 确认磁盘空间充足,支持临时文件创建
- 查看错误分类:upstream_not_found、upstream_unavailable、truncated_package等
- 模块配置写入失败:
- 检查config/module.php文件权限
- 确认OPcache函数可用:
function_exists('opcache_invalidate') - 查看是否有其他进程同时写入该文件
- 会话超时问题:
- 检查storage/install/session目录权限
- 确认TTL_SECONDS设置为3600秒(60分钟)
- 查看是否有GC进程误删活跃会话
- 邮件模块升级失败:
- 检查数据库连接和权限
- 确认最低版本要求:client_version_number > '20180703'
- 查看升级日志和错误信息
结论
站点安装报告与数据分析系统以最小侵入的方式嵌入安装/升级收尾流程,通过统一的 SiteReportService 完成站点快照采集与云端上报。其设计强调"尽力而为"和"零阻塞",在保证安装稳定性的同时,为平台提供匿名基础遥测能力,便于统计安装/升级趋势与站点环境分布。
最新增强:系统现已具备强大的大文件处理能力、并发安全的配置管理、健壮的会话管理机制,以及完善的模块升级支持,能够高效处理~10M级别系统升级包,同时保证多进程环境下的数据一致性和用户体验。
附录:数据模型与字段说明
云端接收到的上报载荷包含站点快照与事件字段,关键字段如下:
-
站点快照
- domain:站点域名(host,去端口、小写)
- site_url:站点根 URL
- system_sign:系统签名
- cms_version:CMS 版本(核心事件时可能用 to_version 填充)
- theme:当前主题
- language:当前语言
- php_version:PHP 版本
- mysql_version:MySQL 版本(不可用时为空)
- server_software:服务器软件
- os:操作系统
- cloud_account:云账号标识(仅 user)
-
事件字段
- target_type:core / module / theme / plugin / miniprogram
- target_slug:扩展标识(cloud_id),核心为空
- target_name:扩展名称(可为空)
- event_type:install / update / patch
- to_version:目标版本
- from_version:升级前版本(仅核心升级时有值)
新增数据模型:
- 安装会话数据:
- install_id:64位十六进制会话ID
- created_at/updated_at:创建和更新时间戳
- type/mode/version:安装类型、模式、版本
- current_step/completed_steps:步骤进度跟踪
- logs:安装过程日志数组
- result:最终结果包含按钮HTML和下一个安装项