文章目录
- [1. 概述](#1. 概述)
- [2. 应用场景与核心价值](#2. 应用场景与核心价值)
-
- [2.1 典型应用场景](#2.1 典型应用场景)
- [2.2 核心价值](#2.2 核心价值)
- [3. 启用前置条件](#3. 启用前置条件)
- [4. 确定性与一致性重放规范](#4. 确定性与一致性重放规范)
-
- [4.1 重放运行逻辑](#4.1 重放运行逻辑)
- [4.2 强制封装操作类型](#4.2 强制封装操作类型)
- [4.3 编码设计准则](#4.3 编码设计准则)
- [5. 持久化配置方式](#5. 持久化配置方式)
-
- [5.1 编译阶段配置](#5.1 编译阶段配置)
- [5.2 执行阶段配置](#5.2 执行阶段配置)
- [6. 业务编码规范与示例优化](#6. 业务编码规范与示例优化)
- [6.1 不推荐写法](#6.1 不推荐写法)
- [6.2 规范优化写法](#6.2 规范优化写法)
- [7. 工作流恢复机制](#7. 工作流恢复机制)
-
- [7.1 三大恢复应用场景](#7.1 三大恢复应用场景)
- [7.2 异常恢复代码示例](#7.2 异常恢复代码示例)
- [7.3 恢复起始规则](#7.3 恢复起始规则)
- [8. 生产最佳实践](#8. 生产最佳实践)
- [9. 长耗时任务实战案例](#9. 长耗时任务实战案例)
- [10. 文档总结](#10. 文档总结)
1. 概述
持久化执行是工作流核心运行保障技术,流程运行至关键节点时自动留存执行进度、状态数据与节点位置信息,支持任务暂停、异常中断、服务重启后从断点位置接续运行,无需重复执行历史步骤。
Spring AI Alibaba Graph 依托内置持久化层与检查点机制,自动保存每一步运行状态。无论故障类型为系统宕机、网络波动、LLM 调用超时,或是人工介入暂停流程,均可依据历史快照精准恢复任务执行。
功能开启提示:项目中配置检查点器后,即可自动启用持久化执行能力,全程支持随时暂停、异常重试、断点续跑。
2. 应用场景与核心价值
2.1 典型应用场景
- 人在回路交互:流程暂停等待人工审核、数据修正、指令确认,完成干预后继续流转
- 长周期运行任务:批量数据处理、多轮智能问答、复杂业务编排等耗时任务
- 异常容错处理:第三方接口超时、程序报错、服务重启等意外中断场景
2.2 核心价值
- 断点续执:依托历史快照恢复流程,规避重复运算,节约系统资源
- 超长时效留存:间隔数日仍可正常恢复执行,适配离线业务场景
- 执行链路溯源:完整记录运行轨迹,支持状态回溯与问题排查
- 高可靠运行:抵御各类中断故障,保障业务流程最终闭环
- 灵活人工干预:预留操作入口,满足人工校验、业务调整需求
3. 启用前置条件
使用持久化执行功能,必须满足三项基础要求:
- 配置检查点器:注册状态存储实例,负责快照数据持久化保存
- 绑定唯一线程标识 :通过
threadId区分独立任务实例,隔离不同流程运行数据 - 遵循确定性重放规则:特殊业务操作统一封装至节点内部,保障恢复执行结果一致
4. 确定性与一致性重放规范
4.1 重放运行逻辑
任务恢复时不会从代码中断行继续,而是以中断节点起始位置为起点,依次重放后续所有流程步骤,因此业务代码必须保证可稳定重复执行。
4.2 强制封装操作类型
以下操作必须独立封装至专属节点,禁止零散编写:
- 非确定性操作:随机数、时间戳、唯一标识生成类逻辑
- 副作用操作:
API调用、文件读写、数据库增删改、消息推送等外部交互逻辑 - 高耗时操作:大模型调用、海量数据运算、第三方服务请求
4.3 编码设计准则
- 拆分独立职责:单个节点仅处理单一业务逻辑,多类副作用操作拆分为多个节点
- 隔离不确定逻辑:非固定结果代码统一收纳在节点内,执行结果存入全局状态
- 实现操作幂等性:重复执行操作不会产生脏数据、重复订单、冗余文件等异常结果
5. 持久化配置方式
5.1 编译阶段配置
构建编译图时注册检查点存储策略,全局定义持久化规则:
java
import com.alibaba.cloud.ai.graph.CompileConfig;
import com.alibaba.cloud.ai.graph.CompiledGraph;
import com.alibaba.cloud.ai.graph.checkpoint.config.SaverConfig;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
SaverConfig saverConfig = SaverConfig.builder()
.register(new MemorySaver())
.build();
CompiledGraph graph = stateGraph.compile(
CompileConfig.builder()
.saverConfig(saverConfig)
.build()
);
5.2 执行阶段配置
任务发起时指定唯一线程ID,绑定当前流程实例:
java
import com.alibaba.cloud.ai.graph.CompiledGraph;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import java.util.Map;
import java.util.UUID;
RunnableConfig config = RunnableConfig.builder()
.threadId("long-running-task-" + UUID.randomUUID())
.build();
graph.invoke(inputData, config);
6. 业务编码规范与示例优化
6.1 不推荐写法
副作用操作直接内嵌节点,任务恢复时会重复发起请求,引发业务异常:
java
var callApi = node_async(state -> {
String url = (String) state.value("url").orElse("");
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
return Map.of("result", response.body().substring(0,Math.min(100, response.body().length())));
});
6.2 规范优化写法
抽取通用业务服务,节点仅负责状态流转与逻辑编排,满足重放与幂等要求:
java
import com.alibaba.cloud.ai.graph.StateGraph;
import static com.alibaba.cloud.ai.graph.action.AsyncNodeAction.node_async;
import java.net.http.*;
import java.net.URI;
import java.util.*;
import java.util.stream.Collectors;
// 通用HTTP请求服务,统一管理外部调用
class HttpRequestService {
private final HttpClient client = HttpClient.newHttpClient();
public String makeRequest(String url) throws Exception {
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
return response.body().substring(0, Math.min(100, response.body().length()));
}
}
// 定义状态更新策略
KeyStrategyFactory keyStrategyFactory = () -> {
Map<String, KeyStrategy> keyStrategyMap = new HashMap<>();
keyStrategyMap.put("urls", new ReplaceStrategy());
keyStrategyMap.put("results", new AppendStrategy());
return keyStrategyMap;
};
// 标准化业务节点
var callApi = node_async(state -> {
List<String> urls = (List<String>) state.value("urls").orElse(List.of());
HttpRequestService httpService = new HttpRequestService();
List<String> results = urls.stream()
.map(httpService::makeRequest)
.collect(Collectors.toList());
return Map.of("results", results);
});
// 构建并运行状态图
StateGraph stateGraph = new StateGraph(keyStrategyFactory)
.addNode("call_api", callApi)
.addEdge(StateGraph.START, "call_api")
.addEdge("call_api", StateGraph.END);
7. 工作流恢复机制
7.1 三大恢复应用场景
- 人工暂停恢复:流程手动中断,修改状态参数后重启执行
- 故障异常恢复:接口报错、程序异常后,复用线程标识重试任务
- 服务重启恢复:进程关闭重启,依据历史快照接续业务流程
7.2 异常恢复代码示例
java
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.CompiledGraph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Logger logger = LoggerFactory.getLogger(RecoveryDemo.class);
String threadId = "error-recovery-thread";
RunnableConfig config = RunnableConfig.builder()
.threadId(threadId)
.build();
try {
// 首次执行任务
graph.invoke(inputData, config);
} catch (Exception e) {
logger.error("任务执行异常,启动断点恢复", e);
// 传入空参数,自动读取历史状态继续运行
graph.invoke(null, config);
}
7.3 恢复起始规则
- 普通节点中断:从当前异常节点起始位置恢复执行
- 子流程中断:回溯至调用子图的父节点重新运行
- 快照生成时机:单个节点全部逻辑执行完毕后,才会保存检查点
8. 生产最佳实践
- 节点单一职责:拆分复杂业务,精简单个节点逻辑,便于故障定位与重试
- 严格保障幂等性:外部交互接口、数据写入操作兼容重复执行,规避业务隐患
- 精简状态数据:仅存储序列化基础数据,剔除大文件、网络连接等无效对象
- 完善异常捕获:节点内部拦截处理报错,避免整体流程无故终止
- 分片处理海量任务:大批量数据拆分批次执行,每批次完成留存快照
- 区分存储环境:测试环境使用
MemorySaver,生产环境替换Redis、数据库持久化方案 - 完备日志监控:记录节点流转、状态变更信息,跟踪任务整体运行进度
9. 长耗时任务实战案例
适用于批量数据清洗、离线统计、文档批量解析等场景,支持中断后无缝续跑
java
import com.alibaba.cloud.ai.graph.*;
import com.alibaba.cloud.ai.graph.action.AsyncNodeAction;
import com.alibaba.cloud.ai.graph.action.EdgeAction;
import com.alibaba.cloud.ai.graph.checkpoint.config.SaverConfig;
import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver;
import java.util.*;
import java.util.stream.IntStream;
// 状态策略配置
KeyStrategyFactory keyStrategyFactory = () -> {
Map<String, KeyStrategy> keyStrategyMap = new HashMap<>();
keyStrategyMap.put("items", new ReplaceStrategy());
keyStrategyMap.put("processedCount", new ReplaceStrategy());
keyStrategyMap.put("results", new AppendStrategy());
return keyStrategyMap;
};
// 分批数据处理节点
var processData = AsyncNodeAction.node_async(state -> {
List<String> items = (List<String>) state.value("items").orElse(List.of());
int processedCount = (int) state.value("processedCount").orElse(0);
int batchSize = 100;
int end = Math.min(processedCount + batchSize, items.size());
List<String> batch = items.subList(processedCount, end);
List<String> processedResults = batch.stream()
.map(item -> "Processed: " + item)
.toList();
return Map.of("processedCount", end, "results", processedResults);
});
// 循环终止判断条件
var checkComplete = EdgeAction.edge_async(state -> {
int processedCount = (int) state.value("processedCount").orElse(0);
List<String> items = (List<String>) state.value("items").orElse(List.of());
return processedCount >= items.size() ? StateGraph.END : "process_data";
});
// 组装状态图
StateGraph stateGraph = new StateGraph(keyStrategyFactory)
.addNode("process_data", processData)
.addEdge(StateGraph.START, "process_data")
.addConditionalEdges("process_data", checkComplete,
Map.of(StateGraph.END, StateGraph.END, "process_data", "process_data"));
// 配置持久化与编译
SaverConfig saverConfig = SaverConfig.builder().register(new MemorySaver()).build();
CompiledGraph graph = stateGraph.compile(
CompileConfig.builder().saverConfig(saverConfig).build()
);
// 启动长任务
String taskThreadId = "long-running-task-" + UUID.randomUUID();
RunnableConfig config = RunnableConfig.builder().threadId(taskThreadId).build();
List<String> largeDataSet = IntStream.range(0, 10000)
.mapToObj(i -> "Item-" + i)
.collect(Collectors.toList());
graph.invoke(Map.of("items", largeDataSet, "processedCount", 0), config);
10. 文档总结
持久化执行是 Spring AI Alibaba Graph 实现企业级稳定业务编排的核心能力,以检查点快照为数据载体,依托线程 ID 实现任务隔离,配合确定性重放机制保障恢复结果无误。
该能力完美适配人工交互、异常容错、超长任务三类核心业务场景,开发过程中只需完成检查点配置、线程标识绑定、副作用节点封装三项基础操作,即可让工作流具备断点续跑、故障恢复、状态溯源的工业级运行能力。