diff --git "a/docs/dev-1.18.0-webank/design/Spark3\345\212\250\346\200\201\345\217\202\346\225\260\346\224\271\351\200\240_\350\256\276\350\256\241.md" "b/docs/dev-1.18.0-webank/design/Spark3\345\212\250\346\200\201\345\217\202\346\225\260\346\224\271\351\200\240_\350\256\276\350\256\241.md" new file mode 100644 index 0000000000..e9e51248fc --- /dev/null +++ "b/docs/dev-1.18.0-webank/design/Spark3\345\212\250\346\200\201\345\217\202\346\225\260\346\224\271\351\200\240_\350\256\276\350\256\241.md" @@ -0,0 +1,251 @@ +# 阶段2:技术设计方案 + +## 1. 设计概述 + +### 1.1 设计目标 +在现有dealsparkDynamicConf方法的基础上进行简化,只保留spark.python.version的强制设置,移除所有其他参数覆盖,信任Spark启动时会自己读取管理台的参数,同时保留异常处理的兜底逻辑,提高代码可读性和可维护性。 + +### 1.2 设计原则 +- **最小改动**: 只修改必要的代码,不影响现有功能 +- **向后兼容**: 兼容现有系统的功能和API +- **清晰明了**: 代码逻辑清晰,易于理解和维护 +- **安全可靠**: 保留异常处理的兜底逻辑,确保系统稳定性 + +## 2. 架构设计 + +### 2.1 组件关系图 + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ 作业请求 │────>│ EntranceUtils │────>│ Spark引擎 │ +│ │ │ │ │ │ +│ Spark3引擎 │ │ dealsparkDynamicConf() │ │ +│ │ │ ↓ │ │ │ +└─────────────────┘ │ 检查引擎类型 │ └─────────────────┘ + │ ↓ │ + │ 强制设置python版本│ + │ ↓ │ + │ 处理异常情况 │ + └─────────────────┘ +``` + +### 2.2 处理流程 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ dealsparkDynamicConf处理流程 │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────┐ ┌───────────────┐ ┌────────────────────┐ │ +│ │ 接收请求 │───>│ 获取引擎标签 │───>│ 检查是否为Spark3 │ │ +│ └──────────┘ └───────────────┘ └─────────┬──────────┘ │ +│ │ │ +│ ┌─────────────┴─────────────┐ │ +│ │ 是Spark3引擎? │ │ +│ └─────────────┬─────────────┘ │ +│ 是 │ │ 否 │ +│ ▼ ▼ │ +│ ┌─────────────┐ ┌─────────────────┐ │ +│ │ 创建属性映射 │ │ 直接返回 │ │ +│ └─────────────┘ └─────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────┐ │ +│ │ 强制设置python版本│ │ +│ └─────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────┐ │ +│ │ 添加到启动参数 │ │ +│ └─────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────┐ │ +│ │ 返回结果 │ │ +│ └─────────────┘ │ +│ │ +│ ┌──────────┐ ┌───────────────┐ ┌────────────────────┐ │ +│ │ 异常捕获 │───>│ 创建属性映射 │───>│ 检查动态资源规划开关 │ │ +│ └──────────┘ └───────────────┘ └─────────┬──────────┘ │ +│ │ │ +│ ┌─────────────┴─────────────┐ │ +│ │ 开关是否开启? │ │ +│ └─────────────┬─────────────┘ │ +│ 是 │ │ 否 │ +│ ▼ ▼ │ +│ ┌─────────────┐ ┌─────────────────┐ │ +│ │ 设置默认参数 │ │ 直接返回 │ │ +│ └─────────────┘ └─────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────┐ │ +│ │ 添加到启动参数 │ │ +│ └─────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────┐ │ +│ │ 返回结果 │ │ +│ └─────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## 3. 详细设计 + +### 3.1 方法简化设计 + +#### 3.1.1 dealsparkDynamicConf方法 +**功能**:处理Spark3动态资源规划配置,只强制设置spark.python.version +**参数**: +- jobRequest:作业请求对象 +- logAppender:日志追加器 +- params:参数映射 +**返回值**:无 +**实现逻辑**: +1. 检查是否为Spark3引擎 +2. 如果是Spark3引擎,强制设置spark.python.version为python3 +3. 将设置添加到启动参数中 +4. 异常情况下,使用兜底方案,统一由后台配置 + +#### 3.1.2 isTargetEngine方法 +**功能**:检查给定的labels是否对应目标引擎类型和可选版本 +**参数**: +- labels:标签列表 +- engine:目标引擎类型 +- version:可选的目标版本 +**返回值**:布尔值,表示是否匹配 +**实现逻辑**: +1. 检查labels是否为null或engine是否为空 +2. 获取EngineTypeLabel +3. 检查引擎类型是否匹配 +4. 如果指定了版本,检查版本是否匹配 +5. 返回匹配结果 + +## 4. 关键代码修改 + +### 4.1 EntranceUtils.scala修改 + +#### 4.1.1 简化dealsparkDynamicConf方法 + +**修改前**: +```scala +def dealsparkDynamicConf( + jobRequest: JobRequest, + logAppender: lang.StringBuilder, + params: util.Map[String, AnyRef] +): Unit = { + // 复杂的参数处理逻辑 + // 包含大量参数覆盖 + // 包含动态资源规划开关处理 +} +``` + +**修改后**: +```scala +def dealsparkDynamicConf( + jobRequest: JobRequest, + logAppender: lang.StringBuilder, + params: util.Map[String, AnyRef] +): Unit = { + try { + val isSpark3 = LabelUtil.isTargetEngine(jobRequest.getLabels, EngineType.SPARK.toString, LabelCommonConfig.SPARK3_ENGINE_VERSION.getValue) + if (isSpark3) { + val properties = new util.HashMap[String, AnyRef]() + properties.put("spark.python.version", "python3") + TaskUtils.addStartupMap(params, properties) + } + } catch { + case e: Exception => + // 异常处理的兜底逻辑 + } +} +``` + +### 4.2 LabelUtil.scala修改 + +#### 4.2.1 新增isTargetEngine方法 + +```scala +def isTargetEngine(labels: util.List[Label[_]], engine: String, version: String = null): Boolean = { + if (null == labels || StringUtils.isBlank(engine)) return false + val engineTypeLabel = getEngineTypeLabel(labels) + if (null != engineTypeLabel) { + val isEngineMatch = engineTypeLabel.getEngineType.equals(engine) + val isVersionMatch = StringUtils.isBlank(version) || engineTypeLabel.getVersion.contains(version) + isEngineMatch && isVersionMatch + } else { + false + } +} +``` + +## 5. 配置示例 + +### 5.1 linkis.properties + +```properties +# Spark3 Python版本配置 +spark.python.version=python3 + +# Spark动态资源规划配置 +linkis.entrance.spark.dynamic.allocation.enabled=true +linkis.entrance.spark.executor.cores=2 +linkis.entrance.spark.executor.memory=4G +``` + +## 6. 兼容性说明 + +| 场景 | 行为 | +|------|------| +| Spark3作业 | 只设置spark.python.version为python3,其他参数由Spark自己读取 | +| 非Spark3作业 | 不执行任何参数设置,直接返回 | +| 异常情况 | 使用兜底方案,统一由后台配置 | +| 现有任务 | 兼容现有任务的执行,不影响现有功能 | + +## 7. 测试设计 + +### 7.1 单元测试 +1. 测试isTargetEngine方法的正确性 +2. 测试dealsparkDynamicConf方法对Spark3引擎的处理 +3. 测试dealsparkDynamicConf方法对非Spark3引擎的处理 +4. 测试dealsparkDynamicConf方法的异常处理逻辑 + +### 7.2 集成测试 +1. 测试Spark3作业的执行流程 +2. 测试非Spark3作业的执行流程 +3. 测试异常情况下的兜底逻辑 +4. 测试配置变更后的系统表现 + +### 7.3 系统测试 +1. 测试在高并发情况下的系统稳定性 +2. 测试在大数据量情况下的系统性能 +3. 测试配置变更后的系统表现 + +## 8. 风险评估和应对措施 + +### 8.1 风险评估 +1. **功能风险**: Spark无法读取管理台参数,导致作业执行失败 +2. **兼容性风险**: 修改后的代码影响现有任务的执行 +3. **异常处理风险**: 异常处理逻辑不完善,导致系统崩溃 + +### 8.2 应对措施 +1. **功能风险**: 保留异常处理的兜底逻辑,确保系统稳定性 +2. **兼容性风险**: 进行充分的兼容性测试,确保不影响现有任务 +3. **异常处理风险**: 完善异常处理逻辑,捕获所有可能的异常 + +## 9. 监控和维护 + +### 9.1 监控指标 +1. dealsparkDynamicConf方法的调用次数 +2. Spark3作业的执行次数 +3. 异常情况的发生次数 +4. 兜底逻辑的执行次数 + +### 9.2 维护建议 +1. 定期检查配置的阈值是否合理 +2. 监控方法调用情况,及时发现异常 +3. 根据业务需求调整配置的阈值 +4. 定期检查日志,发现潜在问题 + +## 10. 总结 + +本设计方案通过简化dealsparkDynamicConf方法,只保留spark.python.version的强制设置,移除所有其他参数覆盖,信任Spark启动时会自己读取管理台的参数,同时保留异常处理的兜底逻辑,提高了代码可读性和可维护性。该方案确保了系统的兼容性和稳定性,同时优化了代码结构,减少了维护成本。 \ No newline at end of file diff --git "a/docs/dev-1.18.0-webank/design/Spark\344\273\273\345\212\241\350\266\205\346\227\266\350\257\212\346\226\255\346\226\260\345\242\236_\350\256\276\350\256\241.md" "b/docs/dev-1.18.0-webank/design/Spark\344\273\273\345\212\241\350\266\205\346\227\266\350\257\212\346\226\255\346\226\260\345\242\236_\350\256\276\350\256\241.md" new file mode 100644 index 0000000000..6333d63a29 --- /dev/null +++ "b/docs/dev-1.18.0-webank/design/Spark\344\273\273\345\212\241\350\266\205\346\227\266\350\257\212\346\226\255\346\226\260\345\242\236_\350\256\276\350\256\241.md" @@ -0,0 +1,364 @@ +# 技术设计方案 + +## 1. 文档基本信息 + +| 项目 | 内容 | +|------|-----------------| +| 设计名称 | Spark任务诊断结果更新接口 | +| 需求类型 | 新增功能 | +| 设计日期 | 2025-12-25 | +| 状态 | 已完成 | +| 编写人 | claude-code | + +## 2. 设计背景与目标 + +### 2.1 设计背景 +在Linkis系统中,当Spark任务运行超时后,会触发诊断逻辑,调用doctoris诊断系统获取诊断结果。为了方便用户查看和分析诊断结果,需要将诊断信息持久化到数据库中,并提供相应的查询接口。 + +### 2.2 设计目标 +- 实现诊断结果的持久化存储 +- 提供高效的诊断结果更新接口 +- 确保系统的高可用性和可靠性 +- 支持后续功能扩展 + +## 3. 架构设计 + +### 3.1 系统架构图 + +```mermaid +flowchart TD + A[EntranceServer] -->|1. 检测超时任务| A + A -->|2. 调用诊断API| B[Doctoris诊断系统] + B -->|3. 返回诊断结果| A + A -->|4. 调用RPC接口| C[JobHistory服务] + C -->|5. 查询诊断记录| D[数据库] + D -->|6. 返回查询结果| C + C -->|7. 创建/更新诊断记录| D + D -->|8. 返回操作结果| C + C -->|9. 返回更新结果| A +``` + +### 3.2 核心组件 + +| 组件 | 职责 | +|------|------| +| EntranceServer | 检测超时任务,调用诊断API,触发诊断结果更新 | +| JobHistory服务 | 提供诊断结果更新接口,处理诊断记录的创建和更新 | +| 数据库 | 存储诊断记录,提供数据持久化支持 | +| Doctoris诊断系统 | 提供任务诊断服务,返回诊断结果 | + +## 4. 详细设计 + +### 4.1 数据模型设计 + +#### 4.1.1 诊断记录表(linkis_ps_job_history_diagnosis) + +| 字段名 | 数据类型 | 约束 | 描述 | +|--------|----------|------|------| +| id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | 主键ID | +| job_history_id | BIGINT | NOT NULL | 任务历史ID | +| diagnosis_content | TEXT | NOT NULL | 诊断内容 | +| created_time | DATETIME | NOT NULL | 创建时间 | +| updated_time | DATETIME | NOT NULL | 更新时间 | +| only_read | VARCHAR(1) | DEFAULT '0' | 是否只读 | +| diagnosis_source | VARCHAR(50) | NOT NULL | 诊断来源 | + +#### 4.1.2 索引设计 + +| 索引名 | 索引类型 | 索引字段 | 用途 | +|--------|----------|----------|------| +| idx_job_history_id | UNIQUE | job_history_id, diagnosis_source | 唯一约束,确保同一任务同一来源只有一条诊断记录 | +| idx_job_history_id_single | NORMAL | job_history_id | 加速根据任务ID查询诊断记录 | + +### 4.2 类设计 + +#### 4.2.1 JobReqDiagnosisUpdate + +**功能**: 诊断结果更新请求协议类 + +**属性**: + +| 属性名 | 类型 | 描述 | +|--------|------|------| +| jobHistoryId | Long | 任务历史ID | +| diagnosisContent | String | 诊断内容 | +| diagnosisSource | String | 诊断来源 | + +**方法**: + +| 方法名 | 参数 | 返回值 | 描述 | +|--------|------|--------|------| +| apply | jobHistoryId: Long, diagnosisContent: String, diagnosisSource: String | JobReqDiagnosisUpdate | 工厂方法,用于创建JobReqDiagnosisUpdate实例 | + +#### 4.2.2 JobHistoryQueryServiceImpl + +**功能**: JobHistory服务实现类,处理诊断结果更新请求 + +**核心方法**: + +| 方法名 | 参数 | 返回值 | 描述 | +|--------|------|--------|------| +| updateDiagnosis | jobReqDiagnosisUpdate: JobReqDiagnosisUpdate | JobRespProtocol | 处理诊断结果更新请求,创建或更新诊断记录 | + +**依赖注入**: + +| 依赖项 | 类型 | 用途 | +|--------|------|------| +| jobHistoryDiagnosisService | JobHistoryDiagnosisService | 诊断记录服务,用于操作数据库 | + +### 4.3 接口设计 + +#### 4.3.1 RPC接口 + +**接口名称**: updateDiagnosis + +**请求参数**: + +| 参数名 | 类型 | 描述 | +|--------|------|------| +| jobHistoryId | Long | 任务历史ID | +| diagnosisContent | String | 诊断内容 | +| diagnosisSource | String | 诊断来源 | + +**返回结果**: + +| 字段名 | 类型 | 描述 | +|--------|------|------| +| status | Int | 状态码,0: 成功, 非0: 失败 | +| msg | String | 响应消息 | + +#### 4.3.2 内部服务接口 + +**JobHistoryDiagnosisService.selectByJobId** + +| 参数名 | 类型 | 描述 | +|--------|------|------| +| jobId | Long | 任务ID | +| diagnosisSource | String | 诊断来源 | + +| 返回值 | 类型 | 描述 | +|--------|------|------| +| 诊断记录 | JobDiagnosis | 诊断记录对象,不存在则返回null | + +**JobHistoryDiagnosisService.insert** + +| 参数名 | 类型 | 描述 | +|--------|------|------| +| jobDiagnosis | JobDiagnosis | 诊断记录对象 | + +**JobHistoryDiagnosisService.update** + +| 参数名 | 类型 | 描述 | +|--------|------|------| +| jobDiagnosis | JobDiagnosis | 诊断记录对象 | + +## 5. 实现细节 + +### 5.1 诊断结果更新流程 + +```java +// 1. 接收RPC请求 +@Receiver +def updateDiagnosis(jobReqDiagnosisUpdate: JobReqDiagnosisUpdate): JobRespProtocol = { + // 2. 日志记录 + logger.info(s"Update job diagnosis: ${jobReqDiagnosisUpdate.toString}") + + // 3. 构造响应对象 + val jobResp = new JobRespProtocol + + // 4. 异常处理 + Utils.tryCatch { + // 5. 查询诊断记录 + var jobDiagnosis = jobHistoryDiagnosisService.selectByJobId( + jobReqDiagnosisUpdate.getJobHistoryId, + jobReqDiagnosisUpdate.getDiagnosisSource + ) + + // 6. 创建或更新诊断记录 + if (jobDiagnosis == null) { + // 创建新记录 + jobDiagnosis = new JobDiagnosis + jobDiagnosis.setJobHistoryId(jobReqDiagnosisUpdate.getJobHistoryId) + jobDiagnosis.setCreatedTime(new Date) + } + + // 更新诊断内容和来源 + jobDiagnosis.setDiagnosisContent(jobReqDiagnosisUpdate.getDiagnosisContent) + jobDiagnosis.setDiagnosisSource(jobReqDiagnosisUpdate.getDiagnosisSource) + jobDiagnosis.setUpdatedDate(new Date) + + // 7. 保存诊断记录 + if (jobDiagnosis.getId == null) { + jobHistoryDiagnosisService.insert(jobDiagnosis) + } else { + jobHistoryDiagnosisService.update(jobDiagnosis) + } + + // 8. 设置成功响应 + jobResp.setStatus(0) + jobResp.setMsg("Update diagnosis success") + } { case exception: Exception => + // 9. 处理异常情况 + logger.error( + s"Failed to update job diagnosis ${jobReqDiagnosisUpdate.toString}, should be retry", + exception + ) + jobResp.setStatus(2) + jobResp.setMsg(ExceptionUtils.getRootCauseMessage(exception)) + } + + // 10. 返回响应结果 + jobResp +} +``` + +### 5.2 诊断结果触发流程 + +```scala +// 1. 检测到超时任务后,调用诊断API +val response = EntranceUtils.taskRealtimeDiagnose(entranceJob.getJobRequest, null) +logger.info(s"Finished to diagnose spark job ${job.getId()}, result: ${response.result}, reason: ${response.reason}") + +// 2. 如果诊断成功,调用更新接口 +if (response.success) { + // 3. 构造诊断更新请求 + val diagnosisUpdate = JobReqDiagnosisUpdate( + job.getId().toLong, + response.result, + "doctoris" + ) + + // 4. 发送RPC请求到jobhistory服务 + val sender = Sender.getSender("jobhistory") + sender.ask(diagnosisUpdate) + logger.info(s"Successfully updated diagnosis for job ${job.getId()}") +} +``` + +## 6. 配置设计 + +| 配置项 | 默认值 | 描述 | 所属模块 | +|--------|--------|------|----------| +| linkis.task.diagnosis.enable | true | 任务诊断开关 | entrance | +| linkis.task.diagnosis.engine.type | spark | 任务诊断引擎类型 | entrance | +| linkis.task.diagnosis.timeout | 300000 | 任务诊断超时时间(毫秒) | entrance | +| linkis.doctor.url | 无 | Doctoris诊断系统URL | entrance | +| linkis.doctor.signature.token | 无 | Doctoris签名令牌 | entrance | + +## 7. 错误处理设计 + +### 7.1 错误码设计 + +| 错误码 | 错误描述 | 处理方式 | +|--------|----------|----------| +| 0 | 成功 | 正常返回 | +| 2 | 内部错误 | 记录日志,返回错误信息 | +| 1001 | 参数无效 | 检查参数,返回错误信息 | +| 1002 | 数据库异常 | 记录日志,返回错误信息 | + +### 7.2 异常处理机制 + +1. **接口层异常处理**:在updateDiagnosis方法中,使用try-catch捕获所有异常,确保接口不会因异常而崩溃 +2. **数据库层异常处理**:使用Spring的事务管理,确保数据库操作的原子性和一致性 +3. **调用方异常处理**:EntranceServer在调用updateDiagnosis接口时,捕获RPC异常,记录日志但不影响主流程 + +## 8. 性能优化设计 + +### 8.1 数据库优化 +- 添加唯一索引,加速查询和避免重复数据 +- 使用连接池管理数据库连接,减少连接创建和销毁开销 +- 优化SQL语句,减少数据库负载 + +### 8.2 接口优化 +- 采用异步处理方式,避免阻塞主流程 +- 合理设置超时时间,避免长时间等待 +- 实现接口限流,防止高并发调用导致系统崩溃 + +### 8.3 代码优化 +- 减少对象创建,使用对象池或复用对象 +- 优化算法,提高代码执行效率 +- 减少网络开销,合理设计接口参数 + +## 9. 测试设计 + +### 9.1 单元测试 + +| 测试用例 | 测试场景 | 预期结果 | +|----------|----------|----------| +| updateDiagnosis_normal | 正常更新诊断记录 | 返回成功状态码,诊断记录被更新 | +| updateDiagnosis_new | 创建新的诊断记录 | 返回成功状态码,诊断记录被创建 | +| updateDiagnosis_invalid_param | 无效参数调用 | 返回错误状态码,错误信息正确 | +| updateDiagnosis_db_exception | 数据库异常 | 返回错误状态码,错误信息正确 | + +### 9.2 集成测试 + +| 测试用例 | 测试场景 | 预期结果 | +|----------|----------|----------| +| entrance_diagnosis_flow | 完整的诊断流程 | 诊断记录被正确创建和更新 | +| concurrent_update | 并发调用更新接口 | 诊断记录被正确更新,无数据冲突 | +| long_running_test | 长时间运行测试 | 系统稳定运行,无内存泄漏 | + +## 10. 部署与运维设计 + +### 10.1 部署方式 +- 与现有Linkis系统一同部署 +- 无需额外的硬件资源 +- 支持集群部署,提高系统可用性 + +### 10.2 监控与告警 +- 监控接口调用频率和响应时间 +- 监控数据库连接池状态 +- 设置告警阈值,当接口响应时间超过阈值或出现异常时触发告警 + +### 10.3 日志管理 +- 记录接口调用日志,包括请求参数、响应结果和耗时 +- 记录数据库操作日志,便于问题排查 +- 采用分级日志,便于日志分析和管理 + +## 11. 后续扩展设计 + +### 11.1 功能扩展 +- 支持多种诊断来源 +- 添加诊断结果查询接口 +- 实现诊断结果可视化 +- 添加诊断结果告警机制 + +### 11.2 性能扩展 +- 支持分布式部署,提高系统吞吐量 +- 实现缓存机制,减少数据库访问次数 +- 采用消息队列,异步处理诊断结果更新 + +## 12. 风险评估与应对 + +| 风险点 | 影响程度 | 可能性 | 应对措施 | +|--------|----------|--------|----------| +| 数据库连接异常 | 中 | 低 | 使用连接池,设置合理的超时时间和重试机制 | +| 高并发调用 | 中 | 中 | 实现接口限流,优化数据库查询,添加缓存 | +| 诊断信息过大 | 低 | 低 | 使用TEXT类型存储,支持大文本 | +| 接口调用失败 | 低 | 中 | 记录日志,不影响主流程,提供重试机制 | + +## 13. 附录 + +### 13.1 术语定义 + +| 术语 | 解释 | +|------|------| +| Linkis | 基于Apache Linkis开发的大数据计算中间件 | +| Doctoris | 任务诊断系统,用于分析任务运行问题 | +| RPC | 远程过程调用,用于系统间通信 | +| JobHistory | 任务历史服务,用于存储和查询任务历史信息 | +| EntranceServer | 入口服务,负责接收和处理任务请求 | + +### 13.2 参考文档 + +- [Apache Linkis官方文档](https://linkis.apache.org/) +- [MyBatis官方文档](https://mybatis.org/mybatis-3/zh/index.html) +- [Spring Boot官方文档](https://spring.io/projects/spring-boot) + +### 13.3 相关代码文件 + +| 文件名 | 路径 | 功能 | +|--------|------|------| +| JobReqDiagnosisUpdate.scala | linkis-computation-governance/linkis-computation-governance-common/src/main/scala/org/apache/linkis/governance/common/protocol/job/ | 诊断结果更新请求协议类 | +| JobHistoryQueryServiceImpl.scala | linkis-public-enhancements/linkis-jobhistory/src/main/scala/org/apache/linkis/jobhistory/service/impl/ | JobHistory服务实现类,包含updateDiagnosis方法 | +| EntranceServer.scala | linkis-computation-governance/linkis-entrance/src/main/scala/org/apache/linkis/entrance/ | Entrance服务,包含诊断触发和更新逻辑 | \ No newline at end of file diff --git "a/docs/dev-1.18.0-webank/design/\346\227\245\345\277\227\346\224\257\346\214\201\347\273\206\347\262\222\345\272\246\350\277\224\345\233\236\346\224\271\351\200\240_\350\256\276\350\256\241.md" "b/docs/dev-1.18.0-webank/design/\346\227\245\345\277\227\346\224\257\346\214\201\347\273\206\347\262\222\345\272\246\350\277\224\345\233\236\346\224\271\351\200\240_\350\256\276\350\256\241.md" new file mode 100644 index 0000000000..a1ba5cecc6 --- /dev/null +++ "b/docs/dev-1.18.0-webank/design/\346\227\245\345\277\227\346\224\257\346\214\201\347\273\206\347\262\222\345\272\246\350\277\224\345\233\236\346\224\271\351\200\240_\350\256\276\350\256\241.md" @@ -0,0 +1,130 @@ +# 阶段2:设计方案文档 + +## 1. 总述 + +### 1.1 需求与目标 + +**项目背景**:在大模型分析场景中,当前获取用户任务日志接口会返回所有(info、error、warn)任务日志,导致大模型处理文件数量过多。为了优化大模型处理效率,需要对 filesystem 模块的 openLog 接口进行增强,支持根据指定的日志级别返回对应的日志内容。 + +**设计目标**: +1. 实现 openLog 接口的日志级别过滤功能 +2. 支持 all、info、error、warn 四种日志级别 +3. 保持向后兼容性,缺省情况下返回全部日志 +4. 确保实现的正确性、性能和可靠性 + +## 2. 技术架构 + +**技术栈**: +- 开发语言:Java (服务端), Scala (客户端SDK) +- 框架:Spring Boot +- 存储:文件系统 + +**部署架构**: +与现有 filesystem 模块部署架构一致,无需额外部署组件。 + +## 3. 核心概念/对象 + +| 概念/对象 | 描述 | +|-----------|------| +| LogLevel | 日志级别枚举类,定义了 ERROR、WARN、INFO、ALL 四种级别 | +| FsRestfulApi | filesystem 模块的 RESTful 接口实现类 | +| OpenLogAction | 客户端 SDK 中调用 openLog 接口的 Action 类 | +| filterLogByLevel | 新增的日志过滤方法 | + +## 4. 处理逻辑设计 + +### 4.1 接口参数变更 + +**原接口签名**: +```java +public Message openLog( + HttpServletRequest req, + @RequestParam(value = "path", required = false) String path, + @RequestParam(value = "proxyUser", required = false) String proxyUser) +``` + +**新接口签名**: +```java +public Message openLog( + HttpServletRequest req, + @RequestParam(value = "path", required = false) String path, + @RequestParam(value = "proxyUser", required = false) String proxyUser, + @RequestParam(value = "logLevel", required = false, defaultValue = "all") String logLevel) +``` + +### 4.2 日志过滤逻辑 + +``` +输入: log[4] 数组, logLevel 参数 +| +v +logLevel 为空或 "all"? --> 是 --> 返回原始 log[4] +| +v (否) +根据 logLevel 创建新数组 filteredResult[4],初始化为空字符串 +| +v +switch(logLevel.toLowerCase()): + case "error": filteredResult[0] = log[0] + case "warn": filteredResult[1] = log[1] + case "info": filteredResult[2] = log[2] + default: 返回原始 log[4] (向后兼容) +| +v +返回 filteredResult[4] +``` + +### 4.3 数据结构 + +日志数组索引与日志级别对应关系: + +| 索引 | 日志级别 | LogLevel.Type | +|------|----------|---------------| +| 0 | ERROR | LogLevel.Type.ERROR | +| 1 | WARN | LogLevel.Type.WARN | +| 2 | INFO | LogLevel.Type.INFO | +| 3 | ALL | LogLevel.Type.ALL | + +## 5. 代码变更清单 + +### 5.1 FsRestfulApi.java + +**文件路径**: `linkis-public-enhancements/linkis-pes-publicservice/src/main/java/org/apache/linkis/filesystem/restful/api/FsRestfulApi.java` + +**变更内容**: +1. `openLog` 方法添加 `logLevel` 参数 +2. 添加 Swagger API 文档注解 +3. 新增 `filterLogByLevel()` 私有方法 + +### 5.2 OpenLogAction.scala + +**文件路径**: `linkis-computation-governance/linkis-client/linkis-computation-client/src/main/scala/org/apache/linkis/ujes/client/request/OpenLogAction.scala` + +**变更内容**: +1. Builder 类添加 `logLevel` 属性(默认值 "all") +2. 添加 `setLogLevel()` 方法 +3. `build()` 方法中添加 logLevel 参数设置 + +## 6. 非功能性设计 + +### 6.1 安全 + +- **权限控制**:确保用户只能访问自己有权限的日志文件(复用现有逻辑) +- **参数校验**:对请求参数进行合理处理,无效参数不抛异常 + +### 6.2 性能 + +- 日志级别过滤对接口响应时间的影响可忽略不计(< 1ms) +- 过滤逻辑在内存中完成,无额外 I/O 操作 + +### 6.3 向后兼容 + +- 缺省情况下返回全部日志,与原有行为一致 +- 无效 logLevel 参数返回全部日志,确保服务不中断 +- 现有调用方无需修改代码即可继续使用 + +## 7. 变更历史 + +| 版本 | 日期 | 变更人 | 变更内容 | +|-----|------|--------|----------| +| 1.0 | 2025-12-26 | AI Assistant | 初始版本 | diff --git "a/docs/dev-1.18.0-webank/design/\347\263\273\347\273\237\347\224\250\346\210\267\347\246\201\346\255\242\347\231\273\345\275\225\346\224\271\351\200\240_\350\256\276\350\256\241.md" "b/docs/dev-1.18.0-webank/design/\347\263\273\347\273\237\347\224\250\346\210\267\347\246\201\346\255\242\347\231\273\345\275\225\346\224\271\351\200\240_\350\256\276\350\256\241.md" new file mode 100644 index 0000000000..6215295c41 --- /dev/null +++ "b/docs/dev-1.18.0-webank/design/\347\263\273\347\273\237\347\224\250\346\210\267\347\246\201\346\255\242\347\231\273\345\275\225\346\224\271\351\200\240_\350\256\276\350\256\241.md" @@ -0,0 +1,196 @@ +# 阶段2:技术设计方案 + +## 1. 设计概述 + +### 1.1 设计目标 +在现有登录拦截逻辑基础上进行增强,将登录来源判断方式从 request body 的 `source` 字段改为 HTTP Header 的 `webLogin` 字段。 + +### 1.2 设计原则 +- **最小改动**: 复用现有拦截逻辑,仅修改来源判断方式 +- **向后兼容**: 默认功能关闭,不影响现有系统 +- **可配置性**: 支持配置开关和系统用户前缀列表 + +## 2. 架构设计 + +### 2.1 组件关系图 + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ Web Frontend │────>│ Gateway Server │────>│ Backend API │ +│ │ │ │ │ │ +│ Header: │ │ UserRestful │ │ │ +│ webLogin=true │ │ ↓ │ │ │ +└─────────────────┘ │ tryLogin() │ └─────────────────┘ + │ ↓ │ + │ isWebLogin() │ + │ ↓ │ + │ checkSystemUser │ + └─────────────────┘ +``` + +### 2.2 处理流程 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 登录请求处理流程 │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────┐ ┌───────────────┐ ┌────────────────────┐ │ +│ │ 接收请求 │───>│ 获取用户名密码 │───>│ 检查功能开关是否开启 │ │ +│ └──────────┘ └───────────────┘ └─────────┬──────────┘ │ +│ │ │ +│ ┌─────────────┴─────────────┐ │ +│ │ 开关状态? │ │ +│ └─────────────┬─────────────┘ │ +│ 关闭 │ │ 开启 │ +│ ▼ ▼ │ +│ ┌─────────────┐ ┌─────────────────┐ │ +│ │ 继续正常登录 │ │ 从Header获取 │ │ +│ └─────────────┘ │ webLogin标识 │ │ +│ └────────┬────────┘ │ +│ │ │ +│ ┌─────────────┴───────────┐ │ +│ │ webLogin == "true"? │ │ +│ └─────────────┬───────────┘ │ +│ false │ │ true │ +│ ▼ ▼ │ +│ ┌─────────────┐ ┌───────────────┐ │ +│ │ 继续正常登录 │ │ 检查用户名前缀 │ │ +│ └─────────────┘ └───────┬───────┘ │ +│ │ │ +│ ┌───────────────┴─────────┐ │ +│ │ 匹配系统用户前缀? │ │ +│ └───────────────┬─────────┘ │ +│ 否 │ │ 是 │ +│ ▼ ▼ │ +│ ┌─────────────┐ ┌─────────────┐ │ +│ │ 继续正常登录 │ │ 返回错误信息 │ │ +│ └─────────────┘ │ 拒绝登录 │ │ +│ └─────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## 3. 详细设计 + +### 3.1 配置项修改 + +**文件**: `GatewayConfiguration.scala` + +| 配置项 | 当前值 | 修改后 | +|--------|--------|--------| +| PROHIBIT_LOGIN_PREFIX | `hduser,shduser` | `hadoop,hduser,shduser` | + +**新增配置项**: 无需新增,复用现有配置 + +### 3.2 代码修改 + +**文件**: `UserRestful.scala` + +#### 3.2.1 新增方法: isWebLogin + +```scala +private val WEB_LOGIN_HEADER = "webLogin" + +private def isWebLogin(gatewayContext: GatewayContext): Boolean = { + val headers = gatewayContext.getRequest.getHeaders + val webLoginValues = headers.get(WEB_LOGIN_HEADER) + if (webLoginValues != null && webLoginValues.nonEmpty) { + "true".equalsIgnoreCase(webLoginValues.head) + } else { + false // 默认为false + } +} +``` + +#### 3.2.2 修改tryLogin方法 + +**现有代码**: +```scala +if ( + GatewayConfiguration.PROHIBIT_LOGIN_SWITCH.getValue && + (!getRequestSource(gatewayContext).equals("client")) +) { + PROHIBIT_LOGIN_PREFIX.split(",").foreach { prefix => + if (userName.toLowerCase().startsWith(prefix)) { + return Message.error("System users are prohibited from logging in(系统用户禁止登录)!") + } + } +} +``` + +**修改后**: +```scala +if ( + GatewayConfiguration.PROHIBIT_LOGIN_SWITCH.getValue && + isWebLogin(gatewayContext) +) { + PROHIBIT_LOGIN_PREFIX.split(",").foreach { prefix => + if (userName.toLowerCase().startsWith(prefix)) { + return Message.error("System users are prohibited from logging in(系统用户禁止登录)!") + } + } +} +``` + +## 4. 接口设计 + +### 4.1 登录接口变更 + +**接口**: POST /api/rest_j/v1/user/login + +**新增Header**: +| Header | 类型 | 必填 | 默认值 | 说明 | +|--------|------|------|--------|------| +| webLogin | String | 否 | false | Web页面登录标识 | + +**请求示例**: +```http +POST /api/rest_j/v1/user/login HTTP/1.1 +Host: gateway.linkis.com +Content-Type: application/json +webLogin: true + +{ + "userName": "testuser", + "password": "xxx" +} +``` + +**错误响应** (系统用户被拦截): +```json +{ + "method": "/api/rest_j/v1/user/login", + "status": 1, + "message": "System users are prohibited from logging in(系统用户禁止登录)!" +} +``` + +## 5. 前端配合要求 + +前端在Web页面调用登录接口时,需要在HTTP请求header中添加: +```javascript +headers: { + 'webLogin': 'true' +} +``` + +## 6. 配置示例 + +### 6.1 linkis.properties + +```properties +# 开启系统用户禁止登录功能 +linkis.system.user.prohibit.login.switch=true + +# 系统用户前缀列表(逗号分隔) +linkis.system.user.prohibit.login.prefix=hadoop,hduser,shduser +``` + +## 7. 兼容性说明 + +| 场景 | 行为 | +|------|------| +| 旧前端(无webLogin header) | 默认webLogin=false,不拦截,正常登录 | +| 客户端登录(无webLogin header) | 默认webLogin=false,不拦截,正常登录 | +| 新前端(webLogin=true) + 普通用户 | 正常登录 | +| 新前端(webLogin=true) + 系统用户 | 拦截,返回错误 | diff --git "a/docs/dev-1.18.0-webank/design/\347\273\223\346\236\234\351\233\206\346\224\271\351\200\240_\350\256\276\350\256\241.md" "b/docs/dev-1.18.0-webank/design/\347\273\223\346\236\234\351\233\206\346\224\271\351\200\240_\350\256\276\350\256\241.md" new file mode 100644 index 0000000000..eb6dfa4bb5 --- /dev/null +++ "b/docs/dev-1.18.0-webank/design/\347\273\223\346\236\234\351\233\206\346\224\271\351\200\240_\350\256\276\350\256\241.md" @@ -0,0 +1,264 @@ +# 阶段2:技术设计方案 + +## 1. 设计概述 + +### 1.1 设计目标 +在现有结果集查看功能基础上进行优化,实现管理台请求不进行结果集拦截,非管理台请求按照配置阈值进行拦截,并且提示信息中动态显示配置的阈值。 + +### 1.2 设计原则 +- **最小改动**: 复用现有拦截逻辑,仅修改请求类型判断和提示信息生成方式 +- **向后兼容**: 不影响现有系统的功能和API +- **可配置性**: 支持通过配置项灵活调整字段长度阈值 +- **清晰明了**: 代码逻辑清晰,易于理解和维护 + +## 2. 架构设计 + +### 2.1 组件关系图 + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ 前端应用 │────>│ PublicService │────>│ 文件系统服务 │ +│ │ │ │ │ │ +│ 管理台请求: │ │ FsRestfulApi │ │ │ +│ enableLimit=true │ │ ↓ │ │ │ +└─────────────────┘ │ openFile() │ └─────────────────┘ + │ ↓ │ + │ 识别请求类型 │ + │ ↓ │ + │ 检查配置 │ + │ ↓ │ + │ 处理结果集 │ + └─────────────────┘ +``` + +### 2.2 处理流程 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 结果集查看处理流程 │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────┐ ┌───────────────┐ ┌────────────────────┐ │ +│ │ 接收请求 │───>│ 解析请求参数 │───>│ 检查enableLimit │ │ +│ └──────────┘ └───────────────┘ └─────────┬──────────┘ │ +│ │ │ +│ ┌─────────────┴─────────────┐ │ +│ │ enableLimit == "true"? │ │ +│ └─────────────┬─────────────┘ │ +│ 是 │ │ 否 │ +│ ▼ ▼ │ +│ ┌─────────────┐ ┌─────────────────┐ │ +│ │ 跳过截取逻辑 │ │ 检查截取功能开关 │ │ +│ └─────────────┘ └────────┬────────┘ │ +│ │ │ +│ ┌─────────────┴───────────┐ │ +│ │ 功能开关是否开启? │ │ +│ └─────────────┬───────────┘ │ +│ 关闭 │ │ 开启 │ +│ ▼ ▼ │ +│ ┌─────────────┐ ┌─────────────────┐ │ +│ │ 返回完整结果 │ │ 检查结果集大小 │ │ +│ └─────────────┘ └────────┬────────┘ │ +│ │ │ +│ ┌─────────────┴───────────┐ │ +│ │ 是否超过配置阈值? │ │ +│ └─────────────┬───────────┘ │ +│ 否 │ │ 是 │ +│ ▼ ▼ │ +│ ┌─────────────┐ ┌─────────────────┐ │ +│ │ 返回完整结果 │ │ 进行截取处理 │ │ +│ └─────────────┘ └────────┬────────┘ │ +│ │ │ +│ ┌─────────────┴───────────┐ │ +│ │ 生成动态提示信息 │ │ +│ └─────────────┬───────────┘ │ +│ │ │ +│ ┌─────────────┴───────────┐ │ +│ │ 返回截取结果和提示信息 │ │ +│ └─────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## 3. 详细设计 + +### 3.1 filesystem模块 + +#### 3.1.1 openFile接口 +**功能**:用于查看文件内容,支持分页和结果集限制 +**参数**: +- path:文件路径 +- page:页码 +- pageSize:每页大小 +- enableLimit:是否启用结果集限制(管理台请求标识) +- nullValue:空值替换字符串 +- columnPage:列页码 +- columnPageSize:列每页大小 +- maskedFieldNames:需要屏蔽的字段名 +- truncateColumn:是否允许截取超长字段 +**返回值**:文件内容和相关元数据 + +#### 3.1.2 优化点 +1. 增加管理台请求识别逻辑,根据enableLimit参数判断 +2. 管理台请求(enableLimit=true)跳过结果集大小检查和截取 +3. 修改提示信息生成逻辑,从配置中动态获取阈值 + +### 3.2 关键代码修改 + +#### 3.2.1 新增请求类型识别逻辑 + +**代码位置**:FsRestfulApi.java + +```java +// 检查是否为管理台请求(enableLimit=true) +boolean enableLimitResult = Boolean.parseBoolean(enableLimit); +``` + +#### 3.2.2 修改结果集截取逻辑 + +**现有代码**: +```java +// 优先截取大字段 +if (LinkisStorageConf.FIELD_TRUNCATION_ENABLED()) { + // 处理逻辑 +} +``` + +**修改后**: +```java +// 优先截取大字段 +if (LinkisStorageConf.FIELD_TRUNCATION_ENABLED() && !enableLimitResult) { + // 管理台请求(enableLimit=true)不进行字段长度拦截,兼容旧逻辑 + FieldTruncationResult fieldTruncationResult = ResultUtils.detectAndHandle( + filteredMetadata, + filteredContent, + LinkisStorageConf.FIELD_VIEW_MAX_LENGTH(), + false); + // 后续处理逻辑 +} +``` + +#### 3.2.3 修改提示信息生成逻辑 + +**现有代码**: +```java +String zh_msg = MessageFormat.format( + "结果集存在字段值字符数超过{0},如需查看全部数据请导出文件或使用字符串截取函数(substring、substr)截取相关字符即可前端展示数据内容", + LinkisStorageConf.LINKIS_RESULT_COL_LENGTH()); +``` + +**修改后**: +```java +String zh_msg = MessageFormat.format( + "结果集存在字段值字符数超过{0},如需查看全部数据请导出文件或使用字符串截取函数(substring、substr)截取相关字符即可前端展示数据内容", + LinkisStorageConf.FIELD_VIEW_MAX_LENGTH()); +``` + +## 4. 接口设计 + +### 4.1 openFile接口 + +**接口**:GET /api/rest_j/v1/filesystem/openFile + +**参数**: +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| path | String | 是 | 文件路径 | +| page | Integer | 是 | 页码 | +| pageSize | Integer | 是 | 每页大小 | +| enableLimit | String | 否 | 是否启用结果集限制(管理台请求标识) | +| nullValue | String | 否 | 空值替换字符串 | +| columnPage | Integer | 否 | 列页码 | +| columnPageSize | Integer | 否 | 列每页大小 | +| maskedFieldNames | String | 否 | 需要屏蔽的字段名 | +| truncateColumn | String | 否 | 是否允许截取超长字段 | + +**返回值**: +```json +{ + "method": "openFile", + "status": 0, + "message": "success", + "data": { + "metadata": [...], + "fileContent": [...], + "oversizedFields": [...], + "zh_msg": "结果集存在字段值字符数超过10000,如需查看全部数据请导出文件或确认截取展示数据内容", + "en_msg": "The result set contains field values exceeding 10000 characters. To view the full data, please export the file or confirm the displayed content is truncated" + } +} +``` + +## 5. 配置示例 + +### 5.1 linkis.properties + +```properties +# 字段查看最大长度 +linkis.storage.field.view.max.length=10000 + +# 启用字段截取功能 +linkis.storage.field.truncation.enabled=true + +# 字段导出下载最大长度 +linkis.storage.field.export.download.length=1000000 + +# 最大超长字段数量 +linkis.storage.oversized.field.max.count=10 +``` + +## 6. 兼容性说明 + +| 场景 | 行为 | +|------|------| +| 管理台请求(enableLimit=true) | 跳过结果集截取,返回完整结果 | +| 非管理台请求(enableLimit=false) | 按照配置阈值进行截取,提示信息显示配置的实际阈值 | +| 旧版本客户端请求(无enableLimit) | 按照非管理台请求处理,兼容旧逻辑 | +| 功能开关关闭 | 所有请求都返回完整结果,不进行截取 | + +## 7. 测试设计 + +### 7.1 单元测试 +1. 测试管理台请求是否跳过结果集限制 +2. 测试非管理台请求在不同enableLimit参数下的行为 +3. 测试提示信息中是否显示配置的实际阈值 +4. 测试不同配置阈值下的表现 + +### 7.2 集成测试 +1. 测试openFile接口的完整调用流程 +2. 测试管理台和非管理台请求的不同处理逻辑 +3. 测试超长字段检测和提示功能 + +### 7.3 系统测试 +1. 测试在高并发情况下的系统稳定性 +2. 测试在大数据量情况下的系统性能 +3. 测试配置变更后的系统表现 + +## 8. 风险评估和应对措施 + +### 8.1 风险评估 +1. **功能风险**:管理台请求识别逻辑错误,导致管理台请求被错误拦截 +2. **性能风险**:增加的请求判断逻辑可能影响系统性能 +3. **配置风险**:配置阈值过大可能导致系统资源消耗过高 + +### 8.2 应对措施 +1. **功能风险**:增加单元测试和集成测试,确保管理台请求识别逻辑正确 +2. **性能风险**:优化请求判断逻辑,确保其对系统性能影响最小 +3. **配置风险**:提供合理的默认配置,并建议用户根据实际情况进行调整 + +## 9. 监控和维护 + +### 9.1 监控指标 +1. openFile接口调用次数 +2. 结果集被截取的次数 +3. 管理台请求和非管理台请求的比例 +4. 超长字段检测次数 + +### 9.2 维护建议 +1. 定期检查配置的阈值是否合理 +2. 监控接口调用情况,及时发现异常 +3. 根据业务需求调整配置的阈值 +4. 定期检查日志,发现潜在问题 + +## 10. 总结 + +本设计方案通过优化openFile接口的逻辑,实现了管理台请求不进行结果集拦截,非管理台请求根据配置阈值进行拦截,并动态展示配置的阈值。该方案确保了系统的兼容性和稳定性,同时优化了用户体验,使提示信息更准确反映系统配置。 \ No newline at end of file diff --git "a/docs/dev-1.18.0-webank/requirements/Spark3\345\212\250\346\200\201\345\217\202\346\225\260\346\224\271\351\200\240_\351\234\200\346\261\202.md" "b/docs/dev-1.18.0-webank/requirements/Spark3\345\212\250\346\200\201\345\217\202\346\225\260\346\224\271\351\200\240_\351\234\200\346\261\202.md" new file mode 100644 index 0000000000..c66641ddf8 --- /dev/null +++ "b/docs/dev-1.18.0-webank/requirements/Spark3\345\212\250\346\200\201\345\217\202\346\225\260\346\224\271\351\200\240_\351\234\200\346\261\202.md" @@ -0,0 +1,128 @@ +# 阶段1:需求分析文档 + +## 1. 需求概述 + +### 1.1 背景 +1. 原dealsparkDynamicConf方法复杂,包含大量参数覆盖逻辑 +2. Spark启动时会自己读取管理台的参数,不需要在这里手动处理 +3. 只需要保留强制设置的spark.python.version +4. 代码维护成本高,需要简化 + +### 1.2 目标 +- 简化dealsparkDynamicConf方法,只保留spark.python.version的强制设置 +- 移除所有其他参数覆盖,包括动态资源规划开关 +- 信任Spark启动时会自己读取管理台的参数 +- 保留异常处理的兜底逻辑 +- 提高代码可读性和可维护性 + +## 2. 功能需求 + +### 2.1 方法简化 + +| 编号 | 功能点 | 描述 | 优先级 | +|------|--------|------|--------| +| FR-001 | 简化dealsparkDynamicConf方法 | 只保留spark.python.version的强制设置 | P0 | +| FR-002 | 移除参数覆盖 | 移除所有其他参数覆盖,包括动态资源规划开关 | P0 | +| FR-003 | 信任Spark参数 | 让Spark自己读取管理台的参数 | P0 | +| FR-004 | 保留异常处理 | 保留异常处理的兜底逻辑 | P0 | + +### 2.2 工具方法 + +| 编号 | 功能点 | 描述 | 优先级 | +|------|--------|------|--------| +| FR-005 | 添加isTargetEngine方法 | 用于检查引擎类型和版本 | P0 | +| FR-006 | 支持可选版本参数 | 不指定版本时只检查引擎类型 | P0 | + +### 2.3 参数处理 + +| 编号 | 功能点 | 描述 | 优先级 | +|------|--------|------|--------| +| FR-007 | 强制设置python版本 | 将spark.python.version强制设置为python3 | P0 | +| FR-008 | 移除动态资源规划参数 | 移除所有与动态资源规划相关的参数设置 | P0 | + +## 3. 非功能需求 + +### 3.1 兼容性 +- 兼容现有系统的功能和API +- 不影响现有任务的执行 +- 异常情况下仍能正常运行 + +### 3.2 性能 +- 简化后的方法执行效率更高 +- 减少不必要的参数处理逻辑 +- 不增加系统的延迟 + +### 3.3 可维护性 +- 代码逻辑清晰,易于理解和维护 +- 减少重复代码 +- 提高代码可读性 + +## 4. 数据字典 + +### 4.1 配置项 + +| 配置项 | 类型 | 默认值 | 说明 | +|--------|------|--------|------| +| spark.python.version | String | python3 | Spark3 Python版本配置 | +| linkis.entrance.spark.dynamic.allocation.enabled | Boolean | true | 是否启用Spark动态资源规划 | +| linkis.entrance.spark.executor.cores | Integer | 2 | Spark Executor核心数 | +| linkis.entrance.spark.executor.memory | String | 4G | Spark Executor内存 | + +### 4.2 方法参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| jobRequest | JobRequest | 是 | 作业请求对象 | +| logAppender | StringBuilder | 是 | 日志追加器 | +| params | Map[String, AnyRef] | 是 | 参数映射 | + +## 5. 用例分析 + +### 5.1 正常场景 + +#### UC-001: Spark3作业执行 +- **前置条件**: 作业请求包含Spark3引擎标签 +- **输入**: 作业请求,引擎类型为Spark,版本为3.x +- **预期**: 方法执行成功,只设置spark.python.version为python3,其他参数由Spark自己读取 + +#### UC-002: 非Spark3作业执行 +- **前置条件**: 作业请求不包含Spark3引擎标签 +- **输入**: 作业请求,引擎类型为Hive或其他非Spark3引擎 +- **预期**: 方法不执行任何参数设置,直接返回 + +### 5.2 异常场景 + +#### UC-003: 方法执行异常 +- **前置条件**: 作业请求包含Spark3引擎标签,但方法执行过程中出现异常 +- **输入**: 作业请求,引擎类型为Spark,版本为3.x +- **预期**: 方法捕获异常,使用兜底方案,统一由后台配置 + +### 5.3 边界场景 + +#### UC-004: 空参数处理 +- **前置条件**: 作业请求的labels为空 +- **输入**: 作业请求,labels为空 +- **预期**: 方法安全处理空参数,不抛出异常 + +#### UC-005: 无效引擎类型 +- **前置条件**: 作业请求包含无效的引擎类型标签 +- **输入**: 作业请求,引擎类型为无效值 +- **预期**: 方法安全处理无效引擎类型,不抛出异常 + +## 6. 影响范围分析 + +### 6.1 代码改动范围 + +| 文件 | 改动类型 | 改动内容 | +|------|---------|---------| +| EntranceUtils.scala | 修改 | 简化dealsparkDynamicConf方法,只强制设置spark.python.version | +| LabelUtil.scala | 修改 | 新增isTargetEngine方法,用于检查引擎类型和版本 | + +### 6.2 风险评估 + +| 风险 | 等级 | 缓解措施 | +|------|------|---------| +| Spark无法读取管理台参数 | 低 | 保留异常处理的兜底逻辑,确保系统稳定性 | +| 现有任务执行失败 | 低 | 兼容性测试,确保不影响现有任务 | +| 代码逻辑错误 | 低 | 单元测试,确保方法执行正确 | +| 性能影响 | 低 | 简化后的方法执行效率更高,不会影响性能 | \ No newline at end of file diff --git "a/docs/dev-1.18.0-webank/requirements/Spark\344\273\273\345\212\241\350\266\205\346\227\266\350\257\212\346\226\255\346\226\260\345\242\236_\351\234\200\346\261\202.md" "b/docs/dev-1.18.0-webank/requirements/Spark\344\273\273\345\212\241\350\266\205\346\227\266\350\257\212\346\226\255\346\226\260\345\242\236_\351\234\200\346\261\202.md" new file mode 100644 index 0000000000..077700b28c --- /dev/null +++ "b/docs/dev-1.18.0-webank/requirements/Spark\344\273\273\345\212\241\350\266\205\346\227\266\350\257\212\346\226\255\346\226\260\345\242\236_\351\234\200\346\261\202.md" @@ -0,0 +1,261 @@ +# 需求分析文档 + +## 1. 文档基本信息 + +| 项目 | 内容 | +|------|-----------------| +| 需求名称 | Spark任务诊断结果更新接口 | +| 需求类型 | 新增功能 | +| 分析日期 | 2025-12-25 | +| 状态 | 已完成 | +| 编写人 | claude-code | + +## 2. 需求背景与目标 + +### 2.1 需求背景 +在Linkis系统中,当Spark任务运行时间超过配置的阈值时,会触发任务诊断逻辑,调用doctoris诊断系统获取诊断结果。目前,诊断结果仅存储在日志中,无法持久化存储和查询。为了方便用户查看和分析任务诊断结果,需要将诊断信息持久化到数据库中。 + +### 2.2 需求目标 +- 实现诊断结果的持久化存储 +- 提供诊断结果的查询接口 +- 支持诊断结果的更新操作 +- 确保诊断信息的准确性和完整性 + +## 3. 功能需求分析 + +### 3.1 核心功能 + +| 功能点 | 描述 | 优先级 | +|--------|------|--------| +| 诊断结果更新接口 | 提供RPC接口,用于更新任务诊断结果 | P1 | +| 诊断记录创建 | 当不存在诊断记录时,创建新的诊断记录 | P1 | +| 诊断记录更新 | 当存在诊断记录时,更新现有诊断记录 | P1 | +| 诊断记录查询 | 支持根据任务ID和诊断来源查询诊断记录 | P2 | + +### 3.2 辅助功能 + +| 功能点 | 描述 | 优先级 | +|--------|------|--------| +| 接口异常处理 | 处理接口调用过程中的异常情况 | P1 | +| 日志记录 | 记录接口调用日志,便于问题排查 | P2 | +| 性能监控 | 监控接口响应时间和调用频率 | P3 | + +## 4. 非功能需求分析 + +| 需求类型 | 具体要求 | 优先级 | +|----------|----------|--------| +| 性能需求 | 接口响应时间 < 500ms | P1 | +| 可用性需求 | 接口可用性 ≥ 99.9% | P1 | +| 可靠性需求 | 诊断信息不丢失,确保数据一致性 | P1 | +| 安全性需求 | 接口调用需要进行身份验证 | P2 | +| 扩展性需求 | 支持多种诊断来源,便于后续扩展 | P2 | + +## 5. 业务流程分析 + +### 5.1 诊断结果更新流程 + +```mermaid +sequenceDiagram + participant Entrance as EntranceServer + participant Doctoris as Doctoris诊断系统 + participant JobHistory as JobHistory服务 + participant DB as 数据库 + + Entrance->>Entrance: 检测到超时任务 + Entrance->>Doctoris: 调用诊断API + Doctoris-->>Entrance: 返回诊断结果 + Entrance->>JobHistory: 调用updateDiagnosis接口 + JobHistory->>DB: 查询诊断记录 + alt 记录不存在 + DB-->>JobHistory: 返回null + JobHistory->>DB: 创建诊断记录 + else 记录存在 + DB-->>JobHistory: 返回诊断记录 + JobHistory->>DB: 更新诊断记录 + end + JobHistory-->>Entrance: 返回更新结果 +``` + +### 5.2 诊断记录查询流程 + +```mermaid +sequenceDiagram + participant Client as 客户端 + participant JobHistory as JobHistory服务 + participant DB as 数据库 + + Client->>JobHistory: 调用查询诊断接口 + JobHistory->>DB: 查询诊断记录 + DB-->>JobHistory: 返回诊断记录 + JobHistory-->>Client: 返回诊断结果 +``` + +## 6. 数据模型分析 + +### 6.1 现有数据模型 + +**表名**: linkis_ps_job_history_diagnosis + +| 字段名 | 数据类型 | 描述 | 约束 | +|--------|----------|------|------| +| id | BIGINT | 主键ID | 自增 | +| job_history_id | BIGINT | 任务历史ID | 非空 | +| diagnosis_content | TEXT | 诊断内容 | 非空 | +| created_time | DATETIME | 创建时间 | 非空 | +| updated_time | DATETIME | 更新时间 | 非空 | +| only_read | VARCHAR(1) | 是否只读 | 默认为'0' | +| diagnosis_source | VARCHAR(50) | 诊断来源 | 非空 | + +### 6.2 数据字典 + +| 字段名 | 取值范围 | 描述 | +|--------|----------|------| +| only_read | 0/1 | 0: 可编辑, 1: 只读 | +| diagnosis_source | doctoris/其他 | 诊断系统来源 | + +## 7. 接口设计 + +### 7.1 RPC接口定义 + +#### 7.1.1 JobReqDiagnosisUpdate + +**功能**: 更新任务诊断结果 + +**参数列表**: + +| 参数名 | 类型 | 描述 | 是否必填 | +|--------|------|------|----------| +| jobHistoryId | Long | 任务历史ID | 是 | +| diagnosisContent | String | 诊断内容 | 是 | +| diagnosisSource | String | 诊断来源 | 是 | + +**返回结果**: + +| 字段名 | 类型 | 描述 | +|--------|------|------| +| status | Int | 状态码,0: 成功, 非0: 失败 | +| msg | String | 响应消息 | + +### 7.2 内部接口 + +#### 7.2.1 JobHistoryDiagnosisService.selectByJobId + +**功能**: 根据任务ID和诊断来源查询诊断记录 + +**参数列表**: + +| 参数名 | 类型 | 描述 | 是否必填 | +|--------|------|------|----------| +| jobId | Long | 任务ID | 是 | +| diagnosisSource | String | 诊断来源 | 是 | + +**返回结果**: +- JobDiagnosis对象或null + +#### 7.2.2 JobHistoryDiagnosisService.insert + +**功能**: 创建诊断记录 + +**参数列表**: + +| 参数名 | 类型 | 描述 | 是否必填 | +|--------|------|------|----------| +| jobDiagnosis | JobDiagnosis | 诊断记录对象 | 是 | + +**返回结果**: +- 无 + +#### 7.2.3 JobHistoryDiagnosisService.update + +**功能**: 更新诊断记录 + +**参数列表**: + +| 参数名 | 类型 | 描述 | 是否必填 | +|--------|------|------|----------| +| jobDiagnosis | JobDiagnosis | 诊断记录对象 | 是 | + +**返回结果**: +- 无 + +## 8. 依赖与约束 + +### 8.1 技术依赖 + +| 依赖项 | 版本 | 用途 | +|--------|------|------| +| Linkis RPC | 1.18.0-wds | 提供RPC通信机制 | +| Spring Boot | 2.6.3 | 提供依赖注入和事务管理 | +| MyBatis | 3.5.9 | 数据库访问框架 | +| MySQL | 8.0+ | 数据库存储 | + +### 8.2 业务约束 + +- 诊断结果更新接口只能由EntranceServer调用 +- 诊断记录的jobHistoryId必须存在于linkis_ps_job_history表中 +- diagnosisSource字段目前固定为"doctoris" + +## 9. 风险与应对措施 + +| 风险点 | 影响程度 | 可能性 | 应对措施 | +|--------|----------|--------|----------| +| 诊断结果更新失败 | 低 | 中 | 记录错误日志,不影响主流程 | +| 数据库连接异常 | 中 | 低 | 使用连接池,设置合理的超时时间 | +| 高并发调用 | 中 | 中 | 优化数据库查询,添加索引 | +| 诊断信息过大 | 低 | 低 | 使用TEXT类型存储,支持大文本 | + +## 10. 验收标准 + +### 10.1 功能验收 + +| 验收项 | 验收标准 | +|--------|----------| +| 诊断记录创建 | 当调用更新接口且不存在诊断记录时,成功创建新记录 | +| 诊断记录更新 | 当调用更新接口且存在诊断记录时,成功更新现有记录 | +| 接口响应时间 | 接口响应时间 < 500ms | +| 幂等性 | 多次调用同一任务的更新接口,结果一致 | +| 错误处理 | 当参数无效时,返回明确的错误信息 | + +### 10.2 非功能验收 + +| 验收项 | 验收标准 | +|--------|----------| +| 可用性 | 接口可用性 ≥ 99.9% | +| 可靠性 | 诊断信息不丢失,数据一致性良好 | +| 扩展性 | 支持多种诊断来源的扩展 | + +## 11. 后续工作建议 + +1. **添加诊断结果查询接口**:提供RESTful API,方便前端查询诊断结果 +2. **支持多种诊断来源**:扩展diagnosisSource字段,支持多种诊断系统 +3. **添加诊断结果可视化**:在管理控制台添加诊断结果展示页面 +4. **优化诊断算法**:根据诊断结果,优化任务调度和资源分配 +5. **添加诊断结果告警**:当诊断结果为严重问题时,触发告警机制 + +## 12. 附录 + +### 12.1 术语定义 + +| 术语 | 解释 | +|------|------| +| Linkis | 基于Apache Linkis开发的大数据计算中间件 | +| doctoris | 任务诊断系统,用于分析任务运行问题 | +| RPC | 远程过程调用,用于系统间通信 | +| jobhistory | 任务历史服务,用于存储和查询任务历史信息 | +| EntranceServer | 入口服务,负责接收和处理任务请求 | + +### 12.2 参考文档 + +- [Apache Linkis官方文档](https://linkis.apache.org/) +- [MyBatis官方文档](https://mybatis.org/mybatis-3/zh/index.html) +- [Spring Boot官方文档](https://spring.io/projects/spring-boot) + +### 12.3 相关配置 + +| 配置项 | 默认值 | 描述 | +|--------|--------|------| +| linkis.task.diagnosis.enable | true | 任务诊断开关 | +| linkis.task.diagnosis.engine.type | spark | 任务诊断引擎类型 | +| linkis.task.diagnosis.timeout | 300000 | 任务诊断超时时间(毫秒) | +| linkis.doctor.url | 无 | Doctoris诊断系统URL | +| linkis.doctor.signature.token | 无 | Doctoris签名令牌 | \ No newline at end of file diff --git "a/docs/dev-1.18.0-webank/requirements/\346\227\245\345\277\227\346\224\257\346\214\201\347\273\206\347\262\222\345\272\246\350\277\224\345\233\236\346\224\271\351\200\240_\351\234\200\346\261\202.md" "b/docs/dev-1.18.0-webank/requirements/\346\227\245\345\277\227\346\224\257\346\214\201\347\273\206\347\262\222\345\272\246\350\277\224\345\233\236\346\224\271\351\200\240_\351\234\200\346\261\202.md" new file mode 100644 index 0000000000..d5ba14f796 --- /dev/null +++ "b/docs/dev-1.18.0-webank/requirements/\346\227\245\345\277\227\346\224\257\346\214\201\347\273\206\347\262\222\345\272\246\350\277\224\345\233\236\346\224\271\351\200\240_\351\234\200\346\261\202.md" @@ -0,0 +1,125 @@ +# 阶段1:需求分析文档 + +## 一、需求背景 + +在大模型分析场景中,当前获取用户任务日志接口会返回所有(info、error、warn)任务日志,导致大模型处理文件数量过多。为了优化大模型处理效率,需要对 filesystem 模块的 openLog 接口进行增强,支持根据指定的日志级别返回对应的日志内容。 + +## 二、需求描述 + +### 2.1 需求详细描述 + +| 模块 | 功能点 | 功能描述 | UI设计及细节 | 功能关注点 | +|-----|--------|----------|--------------|------------| +| filesystem | 日志级别过滤 | 在 openLog 接口中添加 logLevel 参数,支持指定返回的日志级别 | 不涉及 | 确保参数类型正确,默认值设置合理 | +| filesystem | 多种日志级别支持 | 支持 logLevel=all,info,error,warn 四种取值 | 不涉及 | 确保所有取值都能正确处理 | +| filesystem | 默认值处理 | 缺省情况下返回全部日志(相当于 logLevel=all) | 不涉及 | 确保向后兼容性 | +| filesystem | 向后兼容 | 不影响现有调用方的使用 | 不涉及 | 现有调用方无需修改代码即可继续使用 | + +### 2.2 需求交互步骤 + +1. 用户调用 `/openLog` 接口,指定 `path` 参数和可选的 `logLevel` 参数 +2. 系统解析请求参数,获取日志文件路径和日志级别 +3. 系统读取日志文件内容,根据指定的日志级别过滤日志 +4. 系统返回过滤后的日志内容给用户 + +### 2.3 模块交互步骤 + +``` +用户 → filesystem模块 → openLog接口 → 日志文件 → 日志过滤 → 返回结果 +``` + +**关键步骤说明**: +1. 用户调用 openLog 接口,传入 path 和 logLevel 参数 +2. openLog 接口验证参数合法性,解析日志级别 +3. 系统读取指定路径的日志文件 +4. 系统根据日志级别过滤日志内容 +5. 系统将过滤后的日志内容封装为响应对象返回给用户 + +**关注点**: +- 需关注无效 logLevel 参数的处理,应返回默认日志(全部日志) +- 需关注日志文件过大的情况,应返回合理的错误信息 +- 需关注权限控制,确保用户只能访问自己有权限的日志文件 + +## 三、接口文档 + +### 3.1 接口基本信息 + +| 项 | 说明 | +|----|------| +| 接口URL | /api/rest_j/v1/filesystem/openLog | +| 请求方法 | GET | +| 接口描述 | 获取指定路径的日志文件内容,支持按日志级别过滤 | + +### 3.2 请求参数 + +| 参数名 | 类型 | 必填 | 默认值 | 说明 | +|--------|------|------|--------|------| +| path | String | 是 | 无 | 日志文件路径 | +| proxyUser | String | 否 | 无 | 代理用户,仅管理员可使用 | +| logLevel | String | 否 | all | 日志级别,取值为 all,info,error,warn | + +### 3.3 响应参数 + +| 参数名 | 类型 | 说明 | +|--------|------|------| +| status | String | 响应状态,success 表示成功,error 表示失败 | +| message | String | 响应消息 | +| data | Object | 响应数据 | +| data.log | String[] | 日志内容数组,按以下顺序排列:
1. 第0位:ERROR 级别的日志
2. 第1位:WARN 级别的日志
3. 第2位:INFO 级别的日志
4. 第3位:ALL 级别的日志(所有日志) | + +### 3.4 请求示例 + +```bash +# 请求所有日志 +curl -X GET "http://localhost:8080/api/rest_j/v1/filesystem/openLog?path=/path/to/test.log" + +# 请求特定级别的日志 +curl -X GET "http://localhost:8080/api/rest_j/v1/filesystem/openLog?path=/path/to/test.log&logLevel=error" +``` + +### 3.5 响应示例 + +**请求所有日志的响应**: +```json +{ + "status": "success", + "message": "", + "data": { + "log": [ + "2025-12-26 10:00:02.000 ERROR This is an error log\n", + "2025-12-26 10:00:01.000 WARN This is a warn log\n", + "2025-12-26 10:00:00.000 INFO This is an info log\n", + "2025-12-26 10:00:00.000 INFO This is an info log\n2025-12-26 10:00:01.000 WARN This is a warn log\n2025-12-26 10:00:02.000 ERROR This is an error log\n" + ] + } +} +``` + +**请求 ERROR 级别日志的响应**: +```json +{ + "status": "success", + "message": "", + "data": { + "log": [ + "2025-12-26 10:00:02.000 ERROR This is an error log\n", + "", + "", + "" + ] + } +} +``` + +## 四、关联影响分析 + +- **对存量功能的影响**:无,该功能是对现有接口的增强,不会影响其他功能 +- **对第三方组件的影响**:无,该功能仅涉及 filesystem 模块内部逻辑 + +## 五、测试关注点 + +- 验证不同日志级别参数的处理是否正确 +- 验证缺省情况下是否返回全部日志 +- 验证无效日志级别参数的处理是否正确 +- 验证大小写不敏感是否正确 +- 验证权限控制是否有效 diff --git "a/docs/dev-1.18.0-webank/requirements/\347\263\273\347\273\237\347\224\250\346\210\267\347\246\201\346\255\242\347\231\273\345\275\225\346\224\271\351\200\240_\351\234\200\346\261\202.md" "b/docs/dev-1.18.0-webank/requirements/\347\263\273\347\273\237\347\224\250\346\210\267\347\246\201\346\255\242\347\231\273\345\275\225\346\224\271\351\200\240_\351\234\200\346\261\202.md" new file mode 100644 index 0000000000..5e5857394a --- /dev/null +++ "b/docs/dev-1.18.0-webank/requirements/\347\263\273\347\273\237\347\224\250\346\210\267\347\246\201\346\255\242\347\231\273\345\275\225\346\224\271\351\200\240_\351\234\200\346\261\202.md" @@ -0,0 +1,119 @@ +# 阶段1:需求分析文档 + +## 1. 需求概述 + +### 1.1 背景 +根据安全要求,Linkis管理台需要禁止系统用户(如hadoop、hduser、shduser等)通过Web页面登录,以降低安全风险。 + +### 1.2 目标 +- 拦截系统用户的Web页面登录请求 +- 不影响客户端(client)及其他渠道的登录 +- 提供配置开关和系统用户前缀配置 + +## 2. 功能需求 + +### 2.1 登录拦截逻辑 + +| 编号 | 功能点 | 描述 | 优先级 | +|------|--------|------|--------| +| FR-001 | webLogin标识传递 | 前端在HTTP header中传递`webLogin`标识 | P0 | +| FR-002 | webLogin标识获取 | 后端从header获取标识,默认值为`false` | P0 | +| FR-003 | 系统用户拦截 | 当webLogin=true时,拦截系统用户前缀匹配的用户 | P0 | +| FR-004 | 非Web渠道放行 | webLogin=false或未传时不进行拦截 | P0 | + +### 2.2 错误提示 + +| 编号 | 功能点 | 描述 | 优先级 | +|------|--------|------|--------| +| FR-005 | 统一错误信息 | 拦截时返回"系统用户禁止登录" | P0 | + +### 2.3 配置管理 + +| 编号 | 功能点 | 描述 | 优先级 | +|------|--------|------|--------| +| FR-006 | 功能开关 | `linkis.system.user.prohibit.login.switch` 控制功能开启/关闭 | P0 | +| FR-007 | 系统用户前缀 | `linkis.system.user.prohibit.login.prefix` 配置系统用户前缀列表 | P0 | + +## 3. 非功能需求 + +### 3.1 兼容性 +- 现有客户端登录方式不受影响 +- 配置项需向后兼容 + +### 3.2 安全性 +- 拦截逻辑不可绕过 +- webLogin标识仅用于识别登录来源,不用于认证 + +### 3.3 可配置性 +- 功能可通过配置开关完全关闭 +- 系统用户前缀列表可动态配置 + +## 4. 数据字典 + +### 4.1 配置项 + +| 配置项 | 类型 | 默认值 | 说明 | +|--------|------|--------|------| +| linkis.system.user.prohibit.login.switch | Boolean | false | 禁止系统用户登录功能开关 | +| linkis.system.user.prohibit.login.prefix | String | hadoop,hduser,shduser | 系统用户前缀列表,逗号分隔 | + +### 4.2 HTTP Header + +| Header名称 | 类型 | 默认值 | 说明 | +|------------|------|--------|------| +| webLogin | String | false | Web页面登录标识,true表示来自Web页面 | + +## 5. 用例分析 + +### 5.1 正常场景 + +#### UC-001: 普通用户Web登录 +- **前置条件**: 功能开关开启 +- **输入**: 用户名=testuser, webLogin=true +- **预期**: 登录成功 + +#### UC-002: 系统用户Client登录 +- **前置条件**: 功能开关开启 +- **输入**: 用户名=hadoop, webLogin=false +- **预期**: 登录成功 + +### 5.2 异常场景 + +#### UC-003: 系统用户Web登录 +- **前置条件**: 功能开关开启 +- **输入**: 用户名=hadoop, webLogin=true +- **预期**: 登录失败,返回"系统用户禁止登录" + +#### UC-004: hduser用户Web登录 +- **前置条件**: 功能开关开启 +- **输入**: 用户名=hduser01, webLogin=true +- **预期**: 登录失败,返回"系统用户禁止登录" + +### 5.3 边界场景 + +#### UC-005: 功能开关关闭 +- **前置条件**: 功能开关关闭 +- **输入**: 用户名=hadoop, webLogin=true +- **预期**: 登录成功(不进行拦截) + +#### UC-006: webLogin未传递 +- **前置条件**: 功能开关开启 +- **输入**: 用户名=hadoop, header中无webLogin +- **预期**: 登录成功(默认webLogin=false) + +## 6. 影响范围分析 + +### 6.1 代码改动范围 + +| 文件 | 改动类型 | 改动内容 | +|------|---------|---------| +| GatewayConfiguration.scala | 修改 | 更新PROHIBIT_LOGIN_PREFIX默认值 | +| UserRestful.scala | 修改 | 修改登录拦截逻辑,从header获取webLogin | + +### 6.2 风险评估 + +| 风险 | 等级 | 缓解措施 | +|------|------|---------| +| 影响正常用户登录 | 低 | 功能开关默认关闭 | +| 前端未传webLogin | 低 | 默认值为false,不拦截 | +| 配置错误导致无法登录 | 中 | 提供配置示例和文档 | diff --git "a/docs/dev-1.18.0-webank/requirements/\347\273\223\346\236\234\351\233\206\346\224\271\351\200\240_\351\234\200\346\261\202.md" "b/docs/dev-1.18.0-webank/requirements/\347\273\223\346\236\234\351\233\206\346\224\271\351\200\240_\351\234\200\346\261\202.md" new file mode 100644 index 0000000000..70acc231a8 --- /dev/null +++ "b/docs/dev-1.18.0-webank/requirements/\347\273\223\346\236\234\351\233\206\346\224\271\351\200\240_\351\234\200\346\261\202.md" @@ -0,0 +1,134 @@ +# 阶段1:需求分析文档 + +## 1. 需求概述 + +### 1.1 背景 +1. 在非管理台页面查询超过10000字符结果集,原逻辑不进行拦截,目前新截取功能打开的情况下,进行了拦截,需进行优化 + + 管理台接口:`/api/rest_j/v1/filesystem/openFile?path=hdfs:%2F%2F%2Fappcom%2Flogs%2Flinkis%2Fresult%2F2025-12-16%2F16%2FIDE%2Fhadoop%2F18326406%2F_0.dolphin&page=1&enableLimit=true&pageSize=5000` + + 非管理台接口:`/api/rest_j/v1/filesystem/openFile?path=hdfs:%2F%2F%2Fappcom%2Flogs%2Flinkis%2Fresult%2F2025-12-16%2F16%2FIDE%2Fhadoop%2F18326406%2F_0.dolphin&page=1&pageSize=5000` + 或者 + `/api/rest_j/v1/filesystem/openFile?path=hdfs:%2F%2F%2Fappcom%2Flogs%2Flinkis%2Fresult%2F2025-12-16%2F16%2FIDE%2Fhadoop%2F18326406%2F_0.dolphin&page=1&pageSize=5000&enableLimit=false` + +2. 拦截展示字段数字与配置信息不匹配需进行优化 + - 目前新截取功能打开的情况下,配置超长字段 20000时,有字段超过20000时,提示语句还是10000,需进行优化 + +### 1.2 目标 +- 兼容旧逻辑,历史管理台结果集展示不进行拦截 +- 拦截提示展示配置数字,与配置保持一致 +- 提高用户体验,使提示信息更准确反映系统配置 +- 确保系统稳定可靠,不影响现有功能 + +## 2. 功能需求 + +### 2.1 结果集查看优化 + +| 编号 | 功能点 | 描述 | 优先级 | +|------|--------|------|--------| +| FR-001 | 管理台请求识别 | 从请求参数enableLimit识别管理台请求 | P0 | +| FR-002 | 管理台请求处理 | 管理台请求(enableLimit=true)跳过结果集截取 | P0 | +| FR-003 | 非管理台请求处理 | 非管理台请求按照原有逻辑处理 | P0 | +| FR-004 | 动态提示信息 | 提示信息中显示配置的实际阈值 | P0 | + +### 2.2 错误提示 + +| 编号 | 功能点 | 描述 | 优先级 | +|------|--------|------|--------| +| FR-005 | 统一错误信息 | 超过阈值时返回统一的错误提示 | P0 | +| FR-006 | 动态阈值展示 | 错误提示中动态显示配置的阈值 | P0 | + +### 2.3 配置管理 + +| 编号 | 功能点 | 描述 | 优先级 | +|------|--------|------|--------| +| FR-007 | 字段长度配置 | 通过linkis.storage.field.view.max.length配置阈值 | P0 | +| FR-008 | 截取功能开关 | 通过linkis.storage.field.truncation.enabled控制功能开关 | P0 | + +## 3. 非功能需求 + +### 3.1 兼容性 +- 现有客户端调用方式不受影响 +- 配置项需向后兼容 + +### 3.2 性能 +- 新增的请求类型判断不应影响接口性能 +- 配置读取应高效,不增加明显延迟 + +### 3.3 可配置性 +- 功能可通过配置开关完全关闭 +- 字段长度阈值可动态配置 + +## 4. 数据字典 + +### 4.1 配置项 + +| 配置项 | 类型 | 默认值 | 说明 | +|--------|------|--------|------| +| linkis.storage.field.view.max.length | Integer | 10000 | 字段查看最大长度 | +| linkis.storage.field.truncation.enabled | Boolean | true | 是否启用字段截取功能 | + +### 4.2 请求参数 + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| enableLimit | String | 否 | 是否启用结果集限制,true表示管理台请求 | +| path | String | 是 | 文件路径 | +| page | Integer | 是 | 页码 | +| pageSize | Integer | 是 | 每页大小 | +| nullValue | String | 否 | 空值替换字符串 | +| truncateColumn | String | 否 | 是否允许截取超长字段 | + +## 5. 用例分析 + +### 5.1 正常场景 + +#### UC-001: 管理台请求查看大结果集 +- **前置条件**: 功能开关开启,配置阈值为10000 +- **输入**: enableLimit=true,文件内容包含超过10000字符的字段 +- **预期**: 接口返回完整结果,不进行截取 + +#### UC-002: 非管理台请求查看小结果集 +- **前置条件**: 功能开关开启,配置阈值为10000 +- **输入**: enableLimit=false,文件内容字段长度均小于10000 +- **预期**: 接口返回完整结果,不进行截取 + +### 5.2 异常场景 + +#### UC-003: 非管理台请求查看大结果集 +- **前置条件**: 功能开关开启,配置阈值为10000 +- **输入**: enableLimit=false,文件内容包含超过10000字符的字段 +- **预期**: 接口返回截取后的结果,提示信息中显示"超过10000字符" + +#### UC-004: 配置阈值为20000时的提示信息 +- **前置条件**: 功能开关开启,配置阈值为20000 +- **输入**: enableLimit=false,文件内容包含超过20000字符的字段 +- **预期**: 接口返回截取后的结果,提示信息中显示"超过20000字符" + +### 5.3 边界场景 + +#### UC-005: 功能开关关闭 +- **前置条件**: 功能开关关闭,配置阈值为10000 +- **输入**: enableLimit=false,文件内容包含超过10000字符的字段 +- **预期**: 接口返回完整结果,不进行截取 + +#### UC-006: enableLimit未指定 +- **前置条件**: 功能开关开启,配置阈值为10000 +- **输入**: 未指定enableLimit,文件内容包含超过10000字符的字段 +- **预期**: 接口返回截取后的结果,提示信息中显示"超过10000字符" + +## 6. 影响范围分析 + +### 6.1 代码改动范围 + +| 文件 | 改动类型 | 改动内容 | +|------|---------|---------| +| FsRestfulApi.java | 修改 | 修改openFile方法,增加管理台请求识别和处理逻辑 | + +### 6.2 风险评估 + +| 风险 | 等级 | 缓解措施 | +|------|------|---------| +| 影响管理台用户体验 | 低 | 管理台请求跳过截取,保持原有体验 | +| 配置错误导致提示信息不准确 | 低 | 从配置中动态获取阈值,确保一致性 | +| 性能影响 | 低 | 增加的逻辑简单,不影响接口性能 | \ No newline at end of file