共计 1696 个字符,预计需要花费 5 分钟才能阅读完成。
为什么需要 Skill 脚本
在 Cadence 工作流开发中,我们经常遇到一些棘手的场景:

- 状态同步困难 :工作流和活动(Activity) 之间的状态管理往往需要大量样板代码
- 异常处理缺失:默认的重试机制可能不适合所有业务场景
- 代码冗余:相似的状态机逻辑在不同工作流中重复出现
这时,Skill 脚本 就像瑞士军刀一样派上用场了。它让我们可以把复杂的状态机和业务逻辑封装成可复用的组件。
Activity vs Skill 脚本:何时用哪个?
| 特性 | 原生 Activity | Skill 脚本 |
|---|---|---|
| 开发速度 | 较慢 | 更快 |
| 执行性能 | 更高 | 略低 |
| 状态管理 | 需要外部存储 | 内置状态机 |
| 适用场景 | CPU 密集型任务 | 复杂业务流程 |
简单说:需要高性能计算选 Activity,复杂业务流程选 Skill 脚本。
实战:订单处理 Skill 脚本
下面是一个完整的订单处理示例,包含了状态机、错误处理和信号处理:
// 订单状态机定义
const orderStateMachine = {
initialState: 'CREATED',
states: {
CREATED: {
on: {
PAYMENT_RECEIVED: 'PAID',
ORDER_CANCELLED: 'CANCELLED'
}
},
PAID: {
on: {
ITEM_SHIPPED: 'SHIPPED',
REFUND_REQUESTED: 'REFUNDING'
},
// 超时自动取消
timeout: '30m',
timeoutState: 'CANCELLED'
},
// 其他状态...
}
};
// 错误重试配置
const retryPolicy = {
initialInterval: '1s',
backoffCoefficient: 2,
maximumInterval: '1m',
maximumAttempts: 5,
// 特定错误不重试
nonRetryableErrors: ['InvalidOrderError']
};
// 信号处理器示例
async function handlePaymentSignal(paymentInfo) {
try {validatePayment(paymentInfo);
await updateOrderState('PAYMENT_RECEIVED');
return {success: true};
} catch (error) {
// 记录详细错误日志
logger.error(`Payment processing failed`, {
error,
orderId: this.orderId
});
throw new RetryableError('Payment processing failed');
}
}
关键点说明:
- 状态机使用显式定义,避免魔法字符串
- 错误区分可重试和不可重试类型
- 每个信号处理函数保持单一职责
性能优化实战技巧
超时设置的艺术:
- 短任务:设置 5 -30 秒超时
- 长任务:配合心跳机制,设置小时级超时
心跳示例:
async function processLargeFile() {
const chunkSize = 1024 * 1024; // 1MB
let offset = 0;
while (offset < file.size) {
// 处理文件分块
await processChunk(file, offset, chunkSize);
offset += chunkSize;
// 关键:记录心跳
await heartbeat({
progress: offset / file.size,
lastChunkTime: Date.now()});
}
}
经验值:心跳间隔建议为超时时间的 1 /3。比如 30 分钟超时,至少每 10 分钟发一次心跳。
生产环境避坑指南
- 信号丢失问题
- 现象:工作流收不到信号
-
解决:实现信号接收确认机制,超时未确认自动重发
-
幂等性破坏
- 现象:重复信号导致重复操作
-
解决:所有修改操作前检查状态是否允许变更
-
资源泄漏
- 现象:长期运行的工作流占用过多资源
- 解决:为所有长期工作流设置合理的超时时间
思考题
在实际项目中,我们经常遇到需要跨工作流通信的场景。比如订单工作流需要通知物流工作流。
你会如何设计这种跨工作流的 Skill 脚本通信机制?欢迎在评论区分享你的架构设计思路!
(提示:可以考虑信号转发、共享存储、事件总线等不同方案)
正文完
发表至: 未分类
近两天内
