feat(SMB): 重构SMB文件服务支持多服务器配置和连接池优化
重构SmbFileService以支持多服务器配置,引入连接池和会话池管理机制。主要变更包括: 1. 实现基于主机的多服务器认证配置 2. 新增连接池和会话池管理,提高连接复用率 3. 添加定时清理空闲连接和会话的功能 4. 优化异常处理和重试机制 5. 改进日志记录和资源释放 同时更新相关配置文件和应用属性以支持新功能: 1. 修改application.properties支持多服务器SMB配置 2. 增强SmbConfig类以管理多服务器配置 3. 添加任务映射到tasker_mapper.json 4. 新增客户端和服务端任务规则文档
This commit is contained in:
@@ -0,0 +1,419 @@
|
||||
# 客户端Tasker实现WebSocketClientTasker接口规范
|
||||
|
||||
## 概述
|
||||
|
||||
本文档基于 `ContractRepairAllTasker` 实现 `WebSocketClientTasker` 接口的经验,总结了客户端Tasker类升级为支持WebSocket通信的最佳实践和规范。
|
||||
|
||||
## WebSocketClientTasker接口介绍
|
||||
|
||||
`WebSocketClientTasker` 接口定义了通过WebSocket与服务器通信的任务的通用方法,包括任务名称、消息更新、进度更新等核心功能。
|
||||
|
||||
### 核心方法
|
||||
|
||||
1. **getTaskName()** - 获取任务名称,用于在WebSocket通信中标识任务
|
||||
2. **updateMessage(Level, String)** - 更新任务执行过程中的消息
|
||||
3. **updateTitle(String)** - 更新任务标题
|
||||
4. **updateProgress(long, long)** - 更新任务进度
|
||||
5. **cancelTask()** - 取消任务执行(默认实现为空)
|
||||
6. **callRemoteTask(MessageHolder, Locale, Object...)** - 调用远程WebSocket任务
|
||||
7. **callRemoteTaskAsync(MessageHolder, Locale, Object...)** - 异步调用远程WebSocket任务
|
||||
8. **generateTaskId()** - 生成唯一的任务ID
|
||||
|
||||
### 典型实现模式概览
|
||||
|
||||
通过分析项目中的17个实现类,我们发现了以下典型实现模式:
|
||||
|
||||
1. **标准实现**:继承Tasker<Object>并实现WebSocketClientTasker
|
||||
2. **属性注入**:使用@Setter注解或手动设置属性传递任务参数
|
||||
3. **Spring Bean获取**:通过SpringApp.getBean()获取服务实例
|
||||
4. **消息更新**:简洁的消息更新方式
|
||||
5. **参数传递**:通过callRemoteTask的可变参数传递任务所需数据
|
||||
|
||||
## Tasker实现WebSocketClientTasker最佳实践
|
||||
|
||||
### 1. 类定义和继承
|
||||
|
||||
```java
|
||||
/**
|
||||
* 任务类描述
|
||||
* 用于通过WebSocket与服务器通信执行具体操作
|
||||
*/
|
||||
public class 任务类名 extends Tasker<Object> implements WebSocketClientTasker {
|
||||
private static final Logger logger = LoggerFactory.getLogger(任务类名.class);
|
||||
|
||||
// 实现方法
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 参数传递模式
|
||||
|
||||
#### 2.1 使用@Setter注解注入参数
|
||||
|
||||
```java
|
||||
/**
|
||||
* 更新供应商评价表任务
|
||||
*/
|
||||
public class CompanyVendorEvaluationFormUpdateTask extends Tasker<Object> implements WebSocketClientTasker {
|
||||
private static final Logger logger = LoggerFactory.getLogger(CompanyVendorEvaluationFormUpdateTask.class);
|
||||
|
||||
@Setter
|
||||
private VendorVo vendor;
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("更新供应商评价表");
|
||||
return callRemoteTask(holder, getLocale(), vendor.getId());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 使用@Getter和@Setter注解
|
||||
|
||||
```java
|
||||
/**
|
||||
* 客户文件重建任务类
|
||||
*/
|
||||
public class CustomerRebuildFilesTasker extends Tasker<Object> implements WebSocketClientTasker {
|
||||
@Getter
|
||||
@Setter
|
||||
private CustomerVo companyCustomer;
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("重建客户文件");
|
||||
return callRemoteTask(holder, getLocale(), companyCustomer.getId());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 必要方法实现
|
||||
|
||||
#### 2.1 getTaskName()
|
||||
|
||||
```java
|
||||
@Override
|
||||
public String getTaskName() {
|
||||
return "Task名称"; // 必须与服务器端对应Tasker类名匹配
|
||||
}
|
||||
```
|
||||
|
||||
**注意事项**:
|
||||
- 任务名称必须与服务器端对应的Tasker注册名(tasker_mapper.json中的key)保持一致
|
||||
- 名称应简洁明了,反映任务的核心功能
|
||||
|
||||
#### 2.2 updateProgress()
|
||||
updateProgress 方法重载为public,用于外部调用更新进度
|
||||
|
||||
```java
|
||||
@Override
|
||||
public void updateProgress(long current, long total) {
|
||||
super.updateProgress(current, total); // 调用父类方法更新进度
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3 updateTitle()
|
||||
|
||||
```java
|
||||
@Override
|
||||
public void updateTitle(String title) {
|
||||
super.updateTitle(title); // 使用Tasker的updateTitle方法更新标题
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.4 execute() 方法重写
|
||||
|
||||
**标准实现**:
|
||||
```java
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
logger.info("开始执行任务描述");
|
||||
updateTitle("任务标题");
|
||||
|
||||
// 调用远程任务,可选传入参数
|
||||
Object result = callRemoteTask(holder, getLocale(), 可选参数...);
|
||||
|
||||
logger.info("任务执行完成");
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
**简洁实现**(适用于简单任务):
|
||||
```java
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("更新供应商评价表");
|
||||
return callRemoteTask(holder, getLocale(), vendor.getId());
|
||||
}
|
||||
```
|
||||
|
||||
**带消息更新的实现**:
|
||||
```java
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
// 设置任务标题
|
||||
updateTitle("全量库存同步任务");
|
||||
|
||||
// 更新任务消息
|
||||
updateMessage("开始执行全量库存同步...");
|
||||
|
||||
// 调用远程WebSocket任务
|
||||
return callRemoteTask(holder, getLocale());
|
||||
}
|
||||
```
|
||||
|
||||
**关键步骤**:
|
||||
1. 记录任务开始日志(可选)
|
||||
2. 设置任务标题
|
||||
3. 可选:添加任务开始消息
|
||||
4. 调用远程任务执行核心逻辑,传入必要参数
|
||||
5. 记录任务完成日志(可选)
|
||||
6. 返回执行结果
|
||||
|
||||
### 3. 日志记录
|
||||
|
||||
```java
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
// 在类中定义
|
||||
private static final Logger logger = LoggerFactory.getLogger(任务类名.class);
|
||||
|
||||
// 在关键位置使用日志
|
||||
logger.info("任务开始");
|
||||
logger.warn("警告信息");
|
||||
logger.error("错误信息", exception);
|
||||
```
|
||||
|
||||
**日志使用建议**:
|
||||
- 复杂任务建议记录详细日志
|
||||
- 简单任务可以简化或省略日志记录
|
||||
- 确保异常情况下有适当的错误日志记录
|
||||
|
||||
### 4. 异常处理
|
||||
|
||||
在`execute`方法中应妥善处理可能的异常,并通过MessageHolder通知用户:
|
||||
|
||||
```java
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
try {
|
||||
// 任务执行逻辑
|
||||
} catch (Exception e) {
|
||||
logger.error("任务执行失败", e);
|
||||
holder.addMessage(Level.SEVERE, "任务执行失败: " + e.getMessage());
|
||||
throw e; // 向上抛出异常,让框架处理
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**异常处理策略**:
|
||||
- 对于简单任务,可以依赖框架的异常处理机制
|
||||
- 对于复杂任务,建议添加自定义的异常处理逻辑
|
||||
- 确保异常信息对用户友好且具有足够的调试信息
|
||||
|
||||
## 完整实现示例
|
||||
|
||||
### 示例1:简单任务实现
|
||||
|
||||
```java
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
import com.ecep.contract.WebSocketClientTasker;
|
||||
|
||||
/**
|
||||
* 合同修复任务类
|
||||
* 用于通过WebSocket与服务器通信执行合同数据修复操作
|
||||
*/
|
||||
public class ContractRepairAllTasker extends Tasker<Object> implements WebSocketClientTasker {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ContractRepairAllTasker.class);
|
||||
|
||||
@Override
|
||||
public void updateProgress(long current, long total) {
|
||||
super.updateProgress(current, total);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTitle(String title) {
|
||||
// 使用Tasker的updateTitle方法更新标题
|
||||
super.updateTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTaskName() {
|
||||
return "ContractRepairAllTask"; // 与服务器端对应Tasker类名匹配
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
logger.info("开始执行合同修复任务");
|
||||
updateTitle("合同数据修复");
|
||||
Object result = callRemoteTask(holder, getLocale());
|
||||
logger.info("合同修复任务执行完成");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 示例2:带参数的任务实现
|
||||
|
||||
```java
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
import com.ecep.contract.WebSocketClientTasker;
|
||||
import com.ecep.contract.vo.VendorVo;
|
||||
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 更新供应商评价表
|
||||
*/
|
||||
public class CompanyVendorEvaluationFormUpdateTask extends Tasker<Object> implements WebSocketClientTasker {
|
||||
private static final Logger logger = LoggerFactory.getLogger(CompanyVendorEvaluationFormUpdateTask.class);
|
||||
@Setter
|
||||
private VendorVo vendor;
|
||||
|
||||
@Override
|
||||
public void updateProgress(long current, long total) {
|
||||
super.updateProgress(current, total);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTaskName() {
|
||||
return "CompanyVendorEvaluationFormUpdateTask"; // 与服务器端对应Tasker类名匹配
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("更新供应商评价表");
|
||||
return callRemoteTask(holder, getLocale(), vendor.getId());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 示例3:使用Spring Bean的任务实现
|
||||
|
||||
```java
|
||||
package com.ecep.contract.task;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
import com.ecep.contract.SpringApp;
|
||||
import com.ecep.contract.WebSocketClientTasker;
|
||||
import com.ecep.contract.service.YongYouU8Service;
|
||||
|
||||
/**
|
||||
* 合同同步任务
|
||||
*/
|
||||
public class ContractSyncTask extends Tasker<Object> implements WebSocketClientTasker {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ContractSyncTask.class);
|
||||
|
||||
private YongYouU8Service yongYouU8Service;
|
||||
|
||||
private YongYouU8Service getYongYouU8Service() {
|
||||
if (yongYouU8Service == null) {
|
||||
yongYouU8Service = SpringApp.getBean(YongYouU8Service.class);
|
||||
}
|
||||
return yongYouU8Service;
|
||||
}
|
||||
|
||||
public String getTaskName() {
|
||||
return "ContractSyncTask"; // 与服务器端对应Tasker类名匹配
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProgress(long current, long total) {
|
||||
super.updateProgress(current, total);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("用友U8系统-同步合同");
|
||||
return callRemoteTask(holder, getLocale());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项和最佳实践
|
||||
|
||||
### 1. 命名规范
|
||||
- 任务类名应采用驼峰命名法,以`Tasker`结尾或描述性名称如`Task`
|
||||
- getTaskName()返回的名称应与服务器端对应Tasker类名完全匹配
|
||||
- 类注释应清晰描述任务的功能和用途
|
||||
|
||||
### 2. 继承关系
|
||||
- 必须同时继承Tasker类并实现WebSocketClientTasker接口
|
||||
- Tasker泛型参数通常为Object
|
||||
- 确保正确导入所有必要的包
|
||||
|
||||
### 3. 参数处理
|
||||
- 对于需要参数的任务,使用@Setter注解简化属性设置
|
||||
- 对于需要在多处使用的参数,考虑添加@Getter注解
|
||||
- 确保参数验证(如果必要)
|
||||
|
||||
### 4. Spring Bean获取
|
||||
- 使用SpringApp.getBean()获取所需的服务实例
|
||||
- 考虑使用懒加载模式,避免不必要的Bean初始化
|
||||
|
||||
### 5. 消息和进度更新
|
||||
- 使用updateTitle()设置有意义的任务标题
|
||||
- 通过MessageHolder或updateMessage()记录详细的执行消息
|
||||
- 确保进度更新反映真实的执行进度
|
||||
|
||||
### 6. 异常处理
|
||||
- 在关键操作处添加try-catch块
|
||||
- 记录异常日志并通知用户
|
||||
- 适当向上抛出异常以确保框架能正确处理
|
||||
|
||||
### 7. 日志级别使用
|
||||
- INFO: 记录正常的操作流程
|
||||
- WARNING: 记录可能的问题,但不影响继续执行
|
||||
- ERROR: 记录严重错误,通常会终止执行
|
||||
|
||||
### 8. 远程调用参数
|
||||
- 确保传入的参数类型与服务器端Tasker期望的一致
|
||||
- 对于不需要参数的任务,可以不传入额外参数
|
||||
- 对于需要多个参数的任务,确保参数顺序正确
|
||||
|
||||
### 9. 代码风格
|
||||
- 保持代码简洁明了
|
||||
- 遵循项目的代码格式化规范
|
||||
- 添加必要的注释说明核心逻辑
|
||||
|
||||
### 10. 实现策略选择
|
||||
- 简单任务:使用简洁的实现方式,省略不必要的日志
|
||||
- 复杂任务:添加详细的日志记录和异常处理
|
||||
- 有特定需求的任务:根据需要重写接口中的其他方法
|
||||
|
||||
## 与服务器端交互流程
|
||||
|
||||
1. 客户端Tasker通过callRemoteTask()方法提交任务
|
||||
2. WebSocketClientService负责建立与服务器的连接并发送任务信息
|
||||
3. 服务器接收到任务后,创建对应的Tasker实例并执行
|
||||
4. 执行过程中的消息、进度等通过WebSocket实时返回给客户端
|
||||
5. 客户端Tasker通过updateMessage()、updateProgress()等方法更新UI
|
||||
|
||||
## 扩展和自定义
|
||||
|
||||
如需为特定任务提供自定义功能,可以:
|
||||
|
||||
1. 重写cancelTask()方法实现任务取消逻辑
|
||||
2. 根据需要添加额外的字段和方法
|
||||
3. 扩展execute()方法实现更复杂的任务流程
|
||||
|
||||
## 总结
|
||||
|
||||
通过分析项目中的17个WebSocketClientTasker实现类,我们总结了客户端Tasker实现的多种模式和最佳实践。这些实现从简单到复杂,涵盖了各种使用场景,为后续Tasker的编写提供了全面的参考。
|
||||
|
||||
客户端Tasker类实现WebSocketClientTasker接口是实现与服务器实时通信的关键步骤。通过遵循本文档中的规范和最佳实践,可以确保任务执行的可靠性、进度的实时更新和良好的用户体验。
|
||||
|
||||
在实际开发中,应根据任务的复杂度和具体需求,选择合适的实现模式和策略,同时保持代码的一致性和可维护性。
|
||||
452
.trae/rules/server_task_rules.md
Normal file
452
.trae/rules/server_task_rules.md
Normal file
@@ -0,0 +1,452 @@
|
||||
# WebSocketServerTasker 接口实现规范
|
||||
|
||||
## 1. 概述
|
||||
|
||||
本文档总结了实现 `WebSocketServerTasker` 接口的标准做法和最佳实践,基于对项目中已有实现类的分析。该接口用于服务器端实现支持WebSocket通信的任务处理器,实现异步任务的执行和状态监控。
|
||||
|
||||
## 2. WebSocketServerTasker 接口介绍
|
||||
|
||||
`WebSocketServerTasker` 接口位于 `com.ecep.contract.service.tasker` 包下,继承自 `java.util.concurrent.Callable<Object>`,定义了以下核心方法:
|
||||
|
||||
```java
|
||||
public interface WebSocketServerTasker extends Callable<Object> {
|
||||
// 初始化任务,处理传入的参数
|
||||
void init(JsonNode argsNode);
|
||||
|
||||
// 设置会话信息(默认实现为空)
|
||||
default void setSession(SessionInfo session) {}
|
||||
|
||||
// 设置消息处理函数
|
||||
void setMessageHandler(Predicate<Message> messageHandler);
|
||||
|
||||
// 设置标题处理函数
|
||||
void setTitleHandler(Predicate<String> titleHandler);
|
||||
|
||||
// 设置属性处理函数
|
||||
void setPropertyHandler(BiConsumer<String, Object> propertyHandler);
|
||||
|
||||
// 设置进度处理函数
|
||||
void setProgressHandler(BiConsumer<Long, Long> progressHandler);
|
||||
}
|
||||
```
|
||||
|
||||
## 3. 基础实现模式
|
||||
|
||||
### 3.1 推荐实现方式
|
||||
|
||||
通过分析项目中的实现类,推荐以下标准实现模式:
|
||||
|
||||
1. **继承 Tasker<Object> 并实现 WebSocketServerTasker 接口**
|
||||
|
||||
```java
|
||||
public class YourTasker extends Tasker<Object> implements WebSocketServerTasker {
|
||||
// 实现代码
|
||||
}
|
||||
```
|
||||
|
||||
2. **在 Tasker 基类中已实现的方法**
|
||||
|
||||
`Tasker` 类已经提供了 `WebSocketServerTasker` 接口中大部分方法的实现,包括:
|
||||
- `setMessageHandler`
|
||||
- `setTitleHandler`
|
||||
- `setPropertyHandler`
|
||||
- `setProgressHandler`
|
||||
- `call()` 方法(实现了 `Callable` 接口)
|
||||
|
||||
这使得实现类只需要关注特定业务逻辑的实现。
|
||||
|
||||
## 3.2 WebSocketServerTasker 注册配置
|
||||
|
||||
要使WebSocketServerTasker能够被客户端通过WebSocket调用,必须在`tasker_mapper.json`配置文件中进行注册。
|
||||
|
||||
### 3.2.1 注册配置文件
|
||||
|
||||
配置文件位置:`server/src/main/resources/tasker_mapper.json`
|
||||
|
||||
### 3.2.2 注册格式
|
||||
|
||||
注册格式为JSON对象,包含一个`tasks`字段,该字段是一个键值对映射,其中:
|
||||
- **键**:任务名称(客户端通过此名称调用任务)
|
||||
- **值**:任务类的完全限定名
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"任务名称": "任务类的完全限定名",
|
||||
"ContractVerifyTasker": "com.ecep.contract.service.tasker.ContractVerifyTasker"
|
||||
},
|
||||
"descriptions": "任务注册信息, 客户端的任务可以通过 WebSocket 调用"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2.3 注册示例
|
||||
|
||||
假设我们创建了一个名为`CustomTasker`的新任务类,其完全限定名为`com.ecep.contract.service.tasker.CustomTasker`,则注册方式如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"CustomTasker": "com.ecep.contract.service.tasker.CustomTasker"
|
||||
// 其他已注册任务...
|
||||
},
|
||||
"descriptions": "任务注册信息, 客户端的任务可以通过 WebSocket 调用"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2.4 注册机制说明
|
||||
|
||||
WebSocketServerTaskManager类在启动时会读取`tasker_mapper.json`文件,初始化任务类映射表。当客户端请求执行任务时,系统会:
|
||||
|
||||
1. 根据客户端提供的任务名称从映射表中查找对应的任务类
|
||||
2. 使用反射机制实例化任务对象
|
||||
3. 设置WebSocket会话和各类处理器
|
||||
4. 执行任务并通过WebSocket返回结果
|
||||
|
||||
## 4. ContractRepairAllTasker 升级经验
|
||||
|
||||
通过分析 `ContractRepairAllTasker` 的实现,我们总结了以下升级经验:
|
||||
|
||||
### 4.1 升级步骤
|
||||
|
||||
1. **继承适当的基础类**:根据业务需求选择继承 `Tasker<Object>` 或其他特定的抽象类(如 `AbstContractRepairTasker`)
|
||||
|
||||
2. **实现 WebSocketServerTasker 接口**:声明实现该接口,自动继承接口定义的方法
|
||||
|
||||
3. **实现 init 方法**:处理任务初始化和参数解析
|
||||
|
||||
```java
|
||||
@Override
|
||||
public void init(JsonNode argsNode) {
|
||||
// 解析参数或初始化任务状态
|
||||
// ContractRepairAllTasker 不需要参数,所以此方法为空实现
|
||||
}
|
||||
```
|
||||
|
||||
4. **实现或重写 execute 方法**:提供具体的业务逻辑实现
|
||||
|
||||
```java
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
// 调用父类初始化
|
||||
super.execute(holder);
|
||||
// 执行具体业务逻辑
|
||||
repair(holder);
|
||||
return null; // 通常返回 null 或处理结果
|
||||
}
|
||||
```
|
||||
|
||||
5. **使用更新方法提供状态反馈**:在执行过程中使用 `updateTitle`、`updateProgress` 等方法提供实时反馈
|
||||
|
||||
```java
|
||||
updateTitle("同步修复所有合同");
|
||||
updateProgress(counter.incrementAndGet(), total);
|
||||
```
|
||||
|
||||
6. **支持任务取消**:定期检查 `isCancelled()` 方法,支持任务的取消操作
|
||||
|
||||
```java
|
||||
if (isCancelled()) {
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 关键经验
|
||||
|
||||
1. **消息处理**:通过 `MessageHolder` 处理和记录执行过程中的消息,方便调试和错误追踪
|
||||
|
||||
2. **进度更新**:对于批量操作,使用计数器和总数定期更新进度,提供良好的用户体验
|
||||
|
||||
3. **异常处理**:在循环处理中捕获异常并记录,确保单个项目的失败不会导致整个任务失败
|
||||
|
||||
4. **分页处理**:对于大量数据的处理,使用分页查询避免内存溢出
|
||||
|
||||
## 5. 共同模式和最佳实践
|
||||
|
||||
通过分析项目中的17个实现类,我们总结了以下共同模式和最佳实践:
|
||||
|
||||
### 5.1 共同模式
|
||||
|
||||
1. **继承结构**:所有实现类都继承 `Tasker<Object>` 并实现 `WebSocketServerTasker` 接口
|
||||
|
||||
2. **核心方法实现**:
|
||||
- 实现 `init(JsonNode argsNode)` 方法处理参数
|
||||
- 实现或重写 `execute(MessageHolder holder)` 方法实现业务逻辑
|
||||
|
||||
3. **状态更新**:使用 `updateTitle`、`updateMessage`、`updateProgress` 等方法更新任务状态
|
||||
|
||||
4. **Bean获取**:使用 `getCachedBean` 方法获取Spring管理的Bean
|
||||
|
||||
```java
|
||||
ContractService contractService = getCachedBean(ContractService.class);
|
||||
```
|
||||
|
||||
### 5.2 最佳实践
|
||||
|
||||
1. **参数验证**:在 `init` 方法中验证和转换输入参数
|
||||
|
||||
2. **进度反馈**:对于长时间运行的任务,提供合理的进度更新
|
||||
|
||||
3. **异常处理**:捕获并处理特定异常,提供有意义的错误信息
|
||||
|
||||
4. **取消支持**:实现任务取消机制,定期检查 `isCancelled()` 状态
|
||||
|
||||
5. **资源清理**:在任务结束或取消时清理资源
|
||||
|
||||
6. **日志记录**:使用 `slf4j` 记录关键操作和异常信息
|
||||
|
||||
7. **属性传递**:使用 `updateProperty` 方法传递任务执行结果或状态
|
||||
|
||||
```java
|
||||
updateProperty("passed", passed);
|
||||
```
|
||||
|
||||
## 6. 完整实现示例
|
||||
|
||||
### 6.1 简单任务实现示例
|
||||
|
||||
```java
|
||||
package com.ecep.contract.service.tasker;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
import com.ecep.contract.ds.contract.service.ContractService;
|
||||
import com.ecep.contract.ui.Tasker;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class SimpleTasker extends Tasker<Object> implements WebSocketServerTasker {
|
||||
@Getter
|
||||
@Setter
|
||||
private int entityId;
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean success = false;
|
||||
|
||||
@Override
|
||||
public void init(JsonNode argsNode) {
|
||||
// 解析参数
|
||||
this.entityId = argsNode.get(0).asInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
// 更新标题
|
||||
updateTitle("执行简单任务:" + entityId);
|
||||
|
||||
try {
|
||||
// 更新进度
|
||||
updateProgress(1, 3);
|
||||
|
||||
// 执行业务逻辑
|
||||
ContractService service = getCachedBean(ContractService.class);
|
||||
updateProgress(2, 3);
|
||||
|
||||
// 处理结果
|
||||
success = true;
|
||||
holder.info("任务执行成功");
|
||||
|
||||
// 更新最终进度
|
||||
updateProgress(3, 3);
|
||||
|
||||
// 更新结果属性
|
||||
updateProperty("success", success);
|
||||
} catch (Exception e) {
|
||||
holder.error("任务执行失败: " + e.getMessage());
|
||||
success = false;
|
||||
updateProperty("success", success);
|
||||
throw e;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 批量处理任务实现示例
|
||||
|
||||
```java
|
||||
package com.ecep.contract.service.tasker;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
import com.ecep.contract.ds.contract.service.ContractService;
|
||||
import com.ecep.contract.ds.contract.model.Contract;
|
||||
import com.ecep.contract.ui.Tasker;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
public class BatchProcessTasker extends Tasker<Object> implements WebSocketServerTasker {
|
||||
private ContractService contractService;
|
||||
private List<Integer> entityIds;
|
||||
|
||||
@Override
|
||||
public void init(JsonNode argsNode) {
|
||||
// 初始化服务引用
|
||||
this.contractService = getCachedBean(ContractService.class);
|
||||
|
||||
// 解析实体ID列表
|
||||
entityIds = new ArrayList<>();
|
||||
for (JsonNode node : argsNode) {
|
||||
entityIds.add(node.asInt());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("批量处理实体任务");
|
||||
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
long total = entityIds.size();
|
||||
|
||||
for (Integer id : entityIds) {
|
||||
// 检查是否取消
|
||||
if (isCancelled()) {
|
||||
holder.info("任务已取消");
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
MessageHolder subHolder = holder.sub("处理 ID: " + id + " > ");
|
||||
|
||||
// 执行业务逻辑
|
||||
Contract entity = contractService.getById(id);
|
||||
// 处理实体...
|
||||
|
||||
subHolder.info("处理成功");
|
||||
} catch (Exception e) {
|
||||
holder.error("处理 ID: " + id + " 失败: " + e.getMessage());
|
||||
// 记录异常但继续处理下一个
|
||||
}
|
||||
|
||||
// 更新进度
|
||||
updateProgress(counter.incrementAndGet(), total);
|
||||
}
|
||||
|
||||
updateTitle("批量处理完成");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 带有依赖的任务实现示例
|
||||
|
||||
```java
|
||||
package com.ecep.contract.service.tasker;
|
||||
|
||||
import com.ecep.contract.MessageHolder;
|
||||
import com.ecep.contract.ds.contract.service.ContractService;
|
||||
import com.ecep.contract.ds.customer.service.CustomerService;
|
||||
import com.ecep.contract.ui.Tasker;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class DependentTasker extends Tasker<Object> implements WebSocketServerTasker {
|
||||
@Getter
|
||||
@Setter
|
||||
private int contractId;
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean processed = false;
|
||||
|
||||
private ContractService contractService;
|
||||
private CustomerService customerService;
|
||||
|
||||
@Override
|
||||
public void init(JsonNode argsNode) {
|
||||
// 解析参数
|
||||
this.contractId = argsNode.get(0).asInt();
|
||||
|
||||
// 获取服务依赖
|
||||
this.contractService = getCachedBean(ContractService.class);
|
||||
this.customerService = getCachedBean(CustomerService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object execute(MessageHolder holder) throws Exception {
|
||||
updateTitle("执行带有服务依赖的任务");
|
||||
|
||||
updateProgress(1, 5);
|
||||
|
||||
// 使用多个服务协同完成任务
|
||||
try {
|
||||
// 步骤1: 获取合同信息
|
||||
var contract = contractService.getById(contractId);
|
||||
updateProgress(2, 5);
|
||||
|
||||
// 步骤2: 获取相关客户信息
|
||||
var customer = customerService.getById(contract.getCustomerId());
|
||||
updateProgress(3, 5);
|
||||
|
||||
// 步骤3: 执行业务逻辑
|
||||
// ...
|
||||
updateProgress(4, 5);
|
||||
|
||||
processed = true;
|
||||
holder.info("任务处理成功");
|
||||
|
||||
// 更新结果属性
|
||||
updateProperty("processed", processed);
|
||||
} catch (Exception e) {
|
||||
holder.error("任务处理失败: " + e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
|
||||
updateProgress(5, 5);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 最佳实践总结
|
||||
|
||||
1. **继承 Tasker<Object> 并实现 WebSocketServerTasker 接口**:利用已有的基础实现
|
||||
|
||||
2. **合理实现 init 方法**:处理参数初始化,避免在 execute 中重复解析
|
||||
|
||||
3. **实现清晰的 execute 逻辑**:
|
||||
- 开始时设置任务标题
|
||||
- 过程中定期更新进度
|
||||
- 提供详细的消息反馈
|
||||
- 结束时更新最终状态
|
||||
|
||||
4. **使用 Spring Bean**:通过 `getCachedBean` 获取需要的服务依赖
|
||||
|
||||
5. **支持任务取消**:在关键循环中检查 `isCancelled()` 状态
|
||||
|
||||
6. **异常处理**:捕获异常并记录,提供有意义的错误信息
|
||||
|
||||
7. **进度反馈**:对于长时间运行的任务,提供合理的进度更新频率
|
||||
|
||||
8. **属性传递**:使用 `updateProperty` 方法传递任务结果给调用方
|
||||
|
||||
9. **日志记录**:使用 slf4j 记录关键操作和异常
|
||||
|
||||
10. **资源管理**:确保在任务完成或取消时释放相关资源
|
||||
|
||||
## 8. 注意事项
|
||||
|
||||
1. **不要在构造函数中获取 Spring Bean**:使用 `init` 方法或懒加载方式获取
|
||||
|
||||
2. **避免阻塞操作**:长时间阻塞的操作应考虑异步处理
|
||||
|
||||
3. **线程安全**:注意多线程环境下的并发访问问题
|
||||
|
||||
4. **内存管理**:对于处理大量数据的任务,注意内存使用,避免OOM
|
||||
|
||||
5. **超时处理**:考虑设置合理的超时机制
|
||||
|
||||
6. **事务管理**:根据需要考虑事务的范围和传播行为
|
||||
|
||||
7. **任务注册**:创建新的WebSocketServerTasker后,必须在`tasker_mapper.json`文件中注册,否则客户端将无法调用
|
||||
|
||||
8. **任务名称唯一性**:确保任务名称在`tasker_mapper.json`中唯一,避免名称冲突
|
||||
|
||||
9. **类路径正确**:注册时确保使用正确的类完全限定名,否则会导致实例化失败
|
||||
|
||||
10. **重启服务**:修改`tasker_mapper.json`后,需要重启服务才能使新的注册生效
|
||||
|
||||
通过遵循这些规范和最佳实践,可以确保实现的 `WebSocketServerTasker` 类具有良好的可维护性、可扩展性和用户体验。
|
||||
Reference in New Issue
Block a user