GeneralUpdate.Extension — 执行流程详解
目标读者: 需要理解 Extension 扩展管理引擎内部机制的开发者
阅读完你将理解:
GeneralExtensionHost的 DI 注入架构与遗留兼容模式UpdateExtensionAsync的完整九阶段执行链路- 依赖解析器(DependencyResolver)的拓扑排序与循环检测机制
- 版本兼容性检查与平台匹配的判定逻辑
InstallExtensionAsync的备份→清理→解压→原子写入流程- Zip Slip 路径穿越防护的实现细节
- Extension Catalog 的原子写入与崩溃安全设计
- 下载队列管理器(DownloadQueueManager)的并发控制
- 生命周期钩子(IExtensionLifecycleHooks)的 8 个事件注入点
目录
- 架构总览
- 入口:GeneralExtensionHost 的双构造函数设计
- ExtensionHostBuilder:DI Builder 模式
- UpdateExtensionAsync:一键更新完整流程
- 依赖解析:DependencyResolver 深度解析
- 兼容性检查:版本 + 平台双校验
- 下载:DownloadQueueManager 并发控制
- 安装:InstallExtensionAsync 安全保障
- Catalog:原子写入与崩溃安全
- 生命周期钩子:8 个事件注入点
- 关键代码路径索引
1. 架构总览
1.1 六层服务架构
Extension 采用依赖注入 + Builder 模式,所有服务均可替换:
┌──────────────────────────────────────────────────────────────┐
│ GeneralExtensionHost(编排层) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ IExtension │ │ IExtension │ │ IVersion │ │
│ │ HttpClient │ │ Catalog │ │ Compatibility │ │
│ │ 服务端 API 通信│ │ 本地扩展清单 │ │ Checker │ │
│ └──────┬───────┘ └──────┬───────┘ │ 版本兼容性检查 │ │
│ │ │ └────────┬─────────┘ │
│ ┌──────▼───────┐ ┌──────▼───────┐ ┌────────▼─────────┐ │
│ │ IDownload │ │ IDependency │ │ IPlatformMatcher │ │
│ │ QueueManager │ │ Resolver │ │ 平台匹配 │ │
│ │ 下载队列+并发 │ │ 依赖拓扑排序 │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ IExtensionLifecycleHooks(可选) │ │
│ │ 安装前/后、激活前/后、停用前/后、卸载前/后 │ │
│ └──────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
1.2 核心设计原则
| 原则 | 说明 |
|---|---|
| 全 DI 可替换 | 每个服务都有接口,通过构造函数注入,方便单元测试和自定义 |
| Builder 模式 | ExtensionHostBuilder 提供流畅的配置 API,支持 ConfigureServices |
| 遗留兼容 | 保留无参构造函数,内部自动创建默认实现,老代码无需修改 |
| 原子写入 | Catalog 的 manifest.json 先写 .tmp 再重命名,崩溃时不会损坏 |
| Zip Slip 防护 | SafeExtractZipAsync 验证每个条目的目标路径不超出安装目录 |
| 递归依赖安装 | 缺失依赖自动递归调用 UpdateExtensionAsync,确保依赖链完整 |
2. 入口:GeneralExtensionHost 的双构造函数设计
2.1 DI 构造函数(推荐)
public GeneralExtensionHost(
ExtensionHostOptions options,
IExtensionHttpClient httpClient, // HTTP 通信
IExtensionCatalog catalog, // 本地清单
IVersionCompatibilityChecker compatibilityChecker, // 版本兼容
IDownloadQueueManager downloadQueue, // 下载队列
IDependencyResolver dependencyResolver, // 依赖解析
IPlatformMatcher platformMatcher, // 平台匹配
IExtensionLifecycleHooks? lifecycleHooks = null, // 生命周期钩子
IExtensionMetadataMapper? metadataMapper = null) // DTO 映射器
2.2 遗留构造函数(向后兼容)
public GeneralExtensionHost(ExtensionHostOptions options)
{
// 自动创建默认实现
_httpClient = new ExtensionHttpClient(options.ServerUrl, options.Scheme, options.Token);
ExtensionCatalog = new ExtensionCatalog(options.CatalogPath ?? options.ExtensionsDirectory);
_compatibilityChecker = new VersionCompatibilityChecker();
_downloadQueue = new DownloadQueueManager();
_dependencyResolver = new DependencyResolver(ExtensionCatalog);
_platformMatcher = new PlatformMatcher();
// ...
}
2.3 初始化流程
构造函数
│
├── 解析 HostVersion(宿主程序版本)
├── 解析 ExtensionsDirectory(扩展安装根目录)
├── 创建 BackupDirectory = ExtensionsDirectory/.backup
│
├── 注入所有服务依赖
│
├── 订阅 DownloadQueue.DownloadStatusChanged
│ → 转发为 ExtensionUpdateStatusChanged 事件
│
├── 设置 DownloadQueue.DownloadHandler
│ → 委托给 _httpClient.DownloadExtensionAsync
│
├── Directory.CreateDirectory(ExtensionsDirectory)
├── Directory.CreateDirectory(BackupDirectory)
│
└── ExtensionCatalog.LoadInstalledExtensions()
→ 从 manifest.json 文件加载已安装扩展列表
3. ExtensionHostBuilder:DI Builder 模式
var host = new ExtensionHostBuilder()
.ConfigureOptions(options =>
{
options.HostVersion = "2.0.0";
options.ExtensionsDirectory = "./extensions";
options.ServerUrl = "https://api.example.com";
})
.ConfigureServices(services =>
{
services.AddSingleton<IExtensionHttpClient, CustomHttpClient>(); // 替换 HTTP 客户端
services.AddSingleton<IExtensionLifecycleHooks, MyLifecycleHooks>(); // 注入生命周期钩子
})
.Build();
Builder 内部维护 ServiceCollection,在 Build() 时创建 IServiceProvider 并解析所有依赖。
4. UpdateExtensionAsync:一键更新完整流程
这是 Extension 最核心的方法。它串起查询 → 兼容性 → 平台 → 依赖递归 → 下载 → 哈希校验 → 安全安装 → Catalog 更新 → 事件通知的全流程。
4.1 全流程总图
4.2 步骤详解
步骤 ②:查询并映射
var response = await QueryExtensionsAsync(query); // HTTP GET /api/extensions?id=xxx
var serverExtension = response.Body.Items.FirstOrDefault(); // 从分页结果中筛选
var metadata = _metadataMapper?.ToMetadata(serverExtension) // DTO → 领域模型
?? ToMetadata(serverExtension); // 回退静态方法
步骤 ③:版本兼容性检查
public bool IsExtensionCompatible(ExtensionMetadata extension)
{
return _compatibilityChecker.IsCompatible(extension, _hostVersion);
// 内部逻辑:
// MinHostVersion ≤ HostVersion ≤ MaxHostVersion
// 使用 SemVer 2.0 比较
}
步骤 ④:平台匹配
public bool IsCurrentPlatformSupported(ExtensionMetadata metadata)
{
var currentPlatform = GetCurrentPlatformFlags(); // Windows=1, Linux=2, macOS=4, Android=8...
return (metadata.SupportedPlatforms & currentPlatform) != 0;
}