2026/1/9 4:28:54
网站建设
项目流程
简历在线制作网站免费,html网站可以做访问统计吗,分析可口可乐网站建设的目的,企业管理培训课程机构有哪些saga指定的方法是spring bean的bean名称一、核心结论#xff1a;状态机配置的是 Service 层方法SAGA 状态机的核心是「步骤#xff08;Step#xff09;」#xff0c;每个 Step 对应一个 Service 层方法#xff0c;分为两类#xff1a;方法类型作用配置关键字#xff08;…saga指定的方法是spring bean的bean名称一、核心结论状态机配置的是 Service 层方法SAGA 状态机的核心是「步骤Step」每个 Step 对应一个 Service 层方法分为两类方法类型作用配置关键字JSON/YAML正向方法执行业务逻辑如扣款、加款、创建订单对应 SAGA 的 “前进” 步骤serviceName/methodName补偿逆向方法正向方法执行失败后回滚业务逻辑如退款、撤销订单对应 SAGA 的 “回退” 步骤compensateServiceName/compensateMethodName二、状态机配置示例JSON 格式以转账场景为例用户 A 扣款 → 用户 B 加款失败则反向补偿状态机配置直接指向 Service 层方法json{ name: transferSaga, comment: 转账SAGA状态机, startState: deductMoney, states: { // 第一步扣用户A的钱正向方法 deductMoney: { type: ServiceTask, serviceName: accountService, // Service 层 Bean 名称 methodName: deductMoney, // Service 层正向方法名 compensateServiceName: accountService, // 补偿用的 Service Bean compensateMethodName: refundMoney, // Service 层补偿方法名 next: addMoney // 执行成功后进入下一步 }, // 第二步加用户B的钱正向方法 addMoney: { type: ServiceTask, serviceName: accountService, methodName: addMoney, compensateServiceName: accountService, compensateMethodName: reduceMoney, end: true // 执行成功则状态机结束 } } }对应的 Service 层代码示例java运行Service(accountService) // 对应状态机中的 serviceName public class AccountServiceImpl implements AccountService { // 正向扣用户A的钱 public void deductMoney(TransferDTO dto) { // 业务逻辑update account set balance balance - dto.getAmount() where userId dto.getFromUserId() } // 补偿给用户A退款扣钱的逆向 public void refundMoney(TransferDTO dto) { // 业务逻辑update account set balance balance dto.getAmount() where userId dto.getFromUserId() } // 正向给用户B加钱 public void addMoney(TransferDTO dto) { // 业务逻辑update account set balance balance dto.getAmount() where userId dto.getToUserId() } // 补偿扣用户B的钱加钱的逆向 public void reduceMoney(TransferDTO dto) { // 业务逻辑update account set balance balance - dto.getAmount() where userId dto.getToUserId() } }三、核心要求Service 层方法需满足这些规则SAGA 状态机调用 Service 方法时有严格的约束否则会执行失败1. 方法参数 / 返回值规则参数建议统一使用 DTO 作为入参如上述TransferDTO状态机会将全局上下文参数透传给方法支持的参数类型基本类型、DTO需序列化、Map不支持多参数多参数需封装为一个 DTO。返回值建议返回void或Booleantrue表示成功false表示失败若返回非布尔类型状态机会默认判定为执行成功。2. 幂等性要求核心正向 / 补偿方法都必须保证幂等因为 SAGA 状态机可能重试失败的步骤示例给转账请求加transId唯一索引方法执行前先检查transId是否已处理。3. 无事务嵌套Service 层方法不要加Transactional本地事务除非是独立的本地逻辑SAGA 是 “无锁补偿” 模式本地事务会导致补偿方法无法回滚已提交的业务数据。4. 异常处理正向方法抛出异常 → 状态机触发该步骤的补偿方法然后按 “反向顺序” 执行前面步骤的补偿补偿方法建议捕获异常仅打印日志避免补偿失败导致状态机卡死。四、状态机调用 Service 方法的核心原理Bean 加载Seata 启动时会扫描 Spring 容器中的 Service Bean如accountService存入 Bean 工厂方法反射调用状态机执行 Step 时通过serviceName找到对应的 Bean通过methodName反射调用方法参数透传状态机上下文如transId、amount等会作为参数传入 Service 方法保证正向 / 补偿方法能拿到相同的业务参数补偿触发逻辑若addMoney执行失败 → 先触发addMoney的补偿方法reduceMoney→ 再触发deductMoney的补偿方法refundMoney补偿完成后状态机状态更新为 “失败”并写入saga_state_machine_inst表。五、进阶配置Service 方法的高级用法1. 多参数封装若 Service 方法需要多个参数需封装为一个 DTO状态机通过inputParams配置参数映射jsondeductMoney: { type: ServiceTask, serviceName: accountService, methodName: deductMoney, inputParams: { fromUserId: ${transContext.fromUserId}, // 从上下文取参数 amount: ${transContext.amount} }, compensateMethodName: refundMoney, compensateInputParams: { // 补偿方法的参数映射 fromUserId: ${transContext.fromUserId}, amount: ${transContext.amount} }, next: addMoney }2. 异步执行 Service 方法高并发场景下可配置 Service 方法异步执行避免状态机阻塞jsondeductMoney: { type: ServiceTask, serviceName: accountService, methodName: deductMoney, async: true, // 异步执行 asyncTimeout: 3000, // 异步超时时间 compensateMethodName: refundMoney }六、关键总结SAGA 状态机直接配置 Service 层的正向 / 补偿方法不建议配置 Controller 或 DAO 层Service 层是业务逻辑聚合层适合作为补偿粒度正向方法是 “业务推进逻辑”补偿方法是 “业务回滚逻辑”两者需一一对应Service 方法必须保证幂等、无强事务嵌套这是 SAGA 补偿成功的核心状态机通过 “反射 Bean 工厂” 调用 Service 方法参数通过上下文透传无需硬编码调用。saga状态机启动saga方法里是指定状态机json文件的name步骤 1状态机配置中定义nameJSON 示例json{ name: transferSaga, // 状态机唯一名称核心 comment: 用户转账SAGA状态机, startState: deductMoney, states: { deductMoney: { /* 扣钱步骤 */ }, addMoney: { /* 加钱步骤 */ } } }该配置文件通常放在resources/seata/saga目录下Seata 默认扫描路径Seata 启动时会自动加载并注册注册 Key 就是transferSaga。步骤 2Seata 启动时自动注册状态机无需手动指定Seata 内置的SagaStateMachineConfigProcessor会在 Spring 启动时扫描resources/seata/saga下的所有 JSON/YAML 配置解析每个配置的name字段作为唯一标识将状态机定义存入saga_state_machine表name字段对应表的name列同时加载到内存中的StateMachineFactory供业务代码调用。步骤 3业务代码中通过name触发状态机核心操作业务代码如 Controller/Service中通过SagaEngine触发状态机必须指定namejava运行RestController public class TransferController { // 注入 SAGA 引擎Seata 自动配置 Autowired private SagaEngine sagaEngine; PostMapping(/transfer) public String transfer(TransferDTO dto) { // 1. 构建状态机上下文传递业务参数 MapString, Object context new HashMap(); context.put(fromUserId, dto.getFromUserId()); context.put(toUserId, dto.getToUserId()); context.put(amount, dto.getAmount()); context.put(transId, dto.getTransId()); // 幂等标识 // 2. 触发状态机指定 name transferSaga匹配 JSON 中的 name SagaInstance sagaInstance sagaEngine.start( transferSaga, // 核心指定要执行的状态机名称 dto.getTransId(), // 全局事务ID建议用业务唯一ID便于追踪 context // 业务参数上下文 ); // 3. 检查执行结果 if (sagaInstance.getStatus() SagaInstanceStatus.SUCCEEDED) { return 转账成功; } else { return 转账失败 sagaInstance.getExMsg(); } } }三、关键细节name的约束与进阶用法1.name的唯一性约束同一 Seata 集群中状态机name必须唯一如不能同时有两个transferSaga若重复Seata 启动时会抛出StateMachineDuplicateException异常。2. 多环境 / 多租户name结合tenant_id若需多租户 / 多环境隔离可在配置中增加tenantId触发时指定json{ name: transferSaga, tenantId: tenant_01, // 租户ID startState: deductMoney }触发时指定租户java运行sagaEngine.start(transferSaga, tenant_01, dto.getTransId(), context);3. 动态加载状态机无需重启 Seata若需在运行时修改状态机配置可通过 Seata 提供的StateMachineRepository手动注册核心还是指定namejava运行Autowired private StateMachineRepository stateMachineRepository; public void reloadStateMachine(String jsonConfig) { // 解析 JSON 配置 StateMachineConfig config StateMachineConfigParser.parse(jsonConfig); // 注册name 作为唯一 Key stateMachineRepository.save(config); }四、状态机name与全局事务的关联触发状态机时Seata 会自动创建全局事务生成 XID并将name状态机名称写入saga_state_machine_inst表的machine_id字段可通过name查询某业务的所有状态机执行记录sqlSELECT * FROM saga_state_machine_inst WHERE machine_id transferSaga;核心总结状态机 JSON 中的name是唯一标识启动时 Seata 自动扫描并注册业务代码触发状态机时必须通过name指定要执行的状态机name关联了配置文件、内存中的状态机工厂、数据库中的执行实例是 SAGA 状态机的核心索引。saga状态机文件路径Seata SAGA 状态机配置文件的存放目录有默认约定路径和自定义路径两种核心遵循 “框架默认扫描 配置扩展” 的逻辑以下按优先级和使用场景详细说明一、默认扫描目录无需额外配置开箱即用Seata 官方默认约定的状态机配置文件存放路径为plaintext项目根目录/resources/seata/saga/支持的文件格式JSON推荐、YAML/YML扫描规则Seata 启动时Spring 环境下通过SagaStateMachineConfigProcessor自动扫描该目录下所有.json/.yml/.yaml文件解析并注册状态机按name字段作为唯一标识适用场景开发 / 测试环境、单租户 / 单业务场景无需自定义路径时优先使用。示例目录结构plaintextyour-project/ ├── src/ │ └── main/ │ ├── resources/ │ │ ├── seata/ │ │ │ └── saga/ # 默认SAGA配置目录 │ │ │ ├── transferSaga.json # 转账业务状态机 │ │ │ └── orderSaga.yaml # 下单业务状态机 │ │ ├── application.yml # 项目配置文件 │ │ └── logback.xml │ └── java/ └── pom.xml二、自定义目录通过配置指定适配多环境 / 多目录若默认目录不符合项目规范如想放在resources/saga/config/可通过以下方式自定义扫描路径方式 1Spring 配置文件application.yml/application.propertiesyaml# application.yml seata: saga: state-machine: config: location: classpath:saga/config/ # 自定义扫描目录支持classpath/文件绝对路径 suffix: json,yaml # 可选指定扫描的文件后缀默认json,yaml,yml路径支持classpath:类路径、file:本地绝对路径如file:/opt/seata/saga/多目录配置用英文逗号分隔如classpath:saga/transfer/,classpath:saga/order/。方式 2编程式指定动态加载若需在代码中动态指定配置目录如多租户场景可通过StateMachineConfigLoader手动加载java运行Configuration public class SagaConfig { Bean public StateMachineRepository stateMachineRepository() { // 自定义配置目录 String configLocation classpath:saga/custom/; // 加载配置文件并注册 StateMachineConfigLoader loader new StateMachineConfigLoader(); ListStateMachineConfig configs loader.load(configLocation); StateMachineRepository repository new InMemoryStateMachineRepository(); for (StateMachineConfig config : configs) { repository.save(config); // 按name注册状态机 } return repository; } }三、关键注意事项目录权限生产环境若配置文件放在服务器本地路径如file:/opt/seata/saga/需保证 Seata 进程有该目录的读权限否则会加载失败。配置文件命名规范无强制命名规则但建议按 “业务名 Saga 格式后缀” 命名如transferSaga.json便于维护避免同名文件否则会覆盖。热加载支持Seata 1.5.x高版本 Seata 支持状态机配置热加载修改配置文件后调用StateMachineRepository.refresh()即可重新加载无需重启应用核心还是基于配置目录扫描。集群环境配置同步若 Seata 集群部署建议将状态机配置文件放在 Nacos/Apollo 等配置中心而非本地目录通过以下配置读取yamlseata: saga: state-machine: config: type: nacos # 配置中心类型nacos/apollo/configmap data-id: seata-saga-config # 配置中心的dataId group: SEATA_GROUP # 配置分组四、核心总结场景推荐目录配置方式开发 / 测试环境resources/seata/saga/默认无需配置开箱即用自定义本地目录resources/saga/config/等seata.saga.state-machine.config.location集群 / 多环境Nacos/Apollo 配置中心seata.saga.state-machine.config.typesaga状态机文件实例判断类型核心作用配置关键字典型场景异常捕获catch步骤执行抛出异常时触发补偿catch/compensateMethod扣款失败→退款、加款失败→扣回条件分支判断根据业务参数选择不同的正向执行路径condition/next转账金额 1000→风控审核否则直接加款执行结果判断根据步骤返回值判定 “成功 / 失败”决定是否继续 / 补偿resultExpression加款返回false→触发补偿状态机终止判断手动 / 自动判定流程是否终止成功 / 失败 / 暂停end/terminate风控审核不通过→终止状态机循环判断满足条件时重复执行某步骤支持次数 / 条件终止repeat/repeatUntil接口调用失败→重试 3 次事件触发判断监听外部事件满足条件后触发步骤执行event/listener接收到 “风控通过” 事件→继续转账二、核心判断逻辑除 catch 外的关键能力1. 条件分支判断condition按业务参数走不同流程作用正向流程中根据上下文参数如金额、用户等级选择不同的执行步骤实现 “分支逻辑”。配置示例转账金额 1000 需风控审核json{ name: transferSaga, startState: deductMoney, states: { deductMoney: { type: ServiceTask, serviceName: accountService, methodName: deductMoney, compensateMethodName: refundMoney, // 条件分支金额1000→风控步骤否则直接加款 next: { condition: ${transContext.amount 1000}, // SpEL 表达式判断 true: riskCheck, false: addMoney } }, riskCheck: { // 风控审核步骤 type: ServiceTask, serviceName: riskService, methodName: check, compensateMethodName: cancelCheck, next: addMoney // 审核通过→加款 }, addMoney: { type: ServiceTask, serviceName: accountService, methodName: addMoney, compensateMethodName: reduceMoney, end: true // 执行完成→状态机结束 } } }核心使用 Spring ELSpEL表达式解析上下文参数支持所有 SpEL 语法如${transContext.userId 1001}、${transContext.status eq SUCCESS}。2. 执行结果判断resultExpression按方法返回值判定结果作用不依赖 “抛异常”而是根据 Service 方法的返回值判定步骤是否执行成功决定是否继续 / 补偿。配置示例加款返回 false 触发补偿jsonaddMoney: { type: ServiceTask, serviceName: accountService, methodName: addMoney, compensateMethodName: reduceMoney, // 结果判断返回 true 则成功否则失败触发补偿 resultExpression: ${returnObject true}, end: true }说明returnObject代表 Service 方法的返回值若表达式结果为false状态机会判定该步骤失败触发补偿流程相比 “抛异常”更适合 “非异常类失败”如业务规则校验不通过。3. 状态机终止判断end/terminate手动控制流程结束作用主动判定流程是否终止支持 “成功终止”“失败终止”“暂停终止”。1正常终止end: truejsonaddMoney: { type: ServiceTask, serviceName: accountService, methodName: addMoney, end: true // 执行成功→状态机正常终止标记为 SUCCEEDED }2条件终止terminatejsonriskCheck: { type: ServiceTask, serviceName: riskService, methodName: check, compensateMethodName: cancelCheck, // 风控不通过→直接终止状态机失败 next: { condition: ${returnObject PASS}, true: addMoney, false: { terminate: true, // 终止状态机 status: FAILED, // 标记为失败 message: 风控审核不通过 } } }4. 循环判断repeat/repeatUntil重复执行某步骤作用满足条件时重复执行某步骤如接口重试支持 “次数限制”“条件终止”。配置示例加款失败重试 3 次jsonaddMoney: { type: ServiceTask, serviceName: accountService, methodName: addMoney, compensateMethodName: reduceMoney, // 循环判断最多重试 3 次直到返回 true repeat: { times: 3, // 最大重试次数 interval: 1000, // 重试间隔毫秒 repeatUntil: ${returnObject true} // 满足条件则停止重试 }, end: true }注意重试的步骤仍需保证幂等性如通过transId防重复执行。5. 事件触发判断event监听外部事件执行步骤作用状态机暂停等待外部事件触发后继续执行如异步审核、MQ 消息触发属于 “事件驱动型判断”。配置示例等待风控审核事件jsonriskCheck: { type: EventTask, // 事件类型步骤 eventName: RISK_CHECK_PASS, // 监听的事件名 timeout: 30000, // 超时时间30秒 // 事件触发后执行下一步超时则触发补偿 onEvent: addMoney, onTimeout: { compensate: true, // 超时触发补偿 terminate: true, status: FAILED } }触发外部事件代码java运行// 风控审核通过后发送事件触发状态机继续执行 sagaEngine.triggerEvent(RISK_CHECK_PASS, transId, null);适用场景异步流程如人工审核、跨系统异步回调。三、进阶组合判断多规则叠加实际业务中可组合多种判断逻辑例如jsondeductMoney: { type: ServiceTask, serviceName: accountService, methodName: deductMoney, compensateMethodName: refundMoney, resultExpression: ${returnObject true}, // 结果判断 catch: [ // 异常捕获兜底 { exception: java.lang.Exception, // 捕获所有异常 compensate: true, terminate: true } ], next: { // 条件分支 condition: ${transContext.amount 1000}, true: riskCheck, false: addMoney } }逻辑优先级catch异常 resultExpression返回值 condition分支。四、核心总结除 catch 外的关键判断条件分支condition按业务参数走不同正向流程SpEL 表达式结果判断resultExpression按方法返回值判定成功 / 失败替代异常捕获终止判断end/terminate主动控制状态机结束成功 / 失败 / 暂停循环判断repeat重复执行步骤重试场景事件判断event监听外部事件触发流程异步场景。五、实操建议优先使用resultExpression替代 “抛异常”减少异常开销更贴合业务语义条件分支使用 SpEL 时避免复杂表达式可封装为 Service 方法调用如${riskService.checkAmount(transContext.amount)}循环重试必须加次数限制避免死循环事件触发需设置超时时间防止状态机永久暂停。