时序图说明
以下时序图展示了 Orleans 流系统中握手机制的完整流程,包括正常情况和异常情况的处理。
1. 正常握手流程
流代理(PullingAgent) 消费者(Consumer) 缓存(QueueCache) 存储(Storage) GetSequenceToken(subscriptionId) 查询上次处理位置 返回 lastProcessedToken 返回 StreamHandshakeToken GetCacheCursor(streamId, token) 返回 Cursor Cursor.MoveNext() (移动到下一个未处理消息) 保存 LastToken = requestedHandshakeToken 握手成功,开始消息投递 流代理(PullingAgent) 消费者(Consumer) 缓存(QueueCache) 存储(Storage)
2. 新订阅握手流程
流代理(PullingAgent) 新消费者(NewConsumer) 缓存(QueueCache) GetSequenceToken(subscriptionId) 返回 null (新订阅,无历史状态) GetCacheCursor(streamId, cacheToken) 返回 Cursor (从缓存位置开始) 保存 LastToken = null 握手成功,从缓存位置开始投递 流代理(PullingAgent) 新消费者(NewConsumer) 缓存(QueueCache)
3. 异常处理流程
流代理(PullingAgent) 消费者(Consumer) 错误协议(ErrorProtocol) GetSequenceToken(subscriptionId) 抛出异常 (网络问题/消费者不可用) 检查 IsShutdown return false (停止处理) ErrorProtocol(consumerData, exception) 记录错误,决定是否标记为故障 返回 faultedSubscription 状态 return false (停止处理该消费者) GetCacheCursor(streamId, cacheToken) (备用方案) 握手完成,使用备用位置 alt [订阅被标记为故障] [订阅正常] alt [代理正在关闭] [代理正常运行] 流代理(PullingAgent) 消费者(Consumer) 错误协议(ErrorProtocol)
4. 重试机制流程
流代理(PullingAgent) 消费者(Consumer) 重试执行器(RetryExecutor) ExecuteWithRetries(GetSequenceToken, INFINITE_RETRIES) GetSequenceToken(subscriptionId) 返回 StreamHandshakeToken 返回结果 抛出异常 检查重试条件 等待退避时间 GetSequenceToken(subscriptionId) (重试) 抛出异常 alt [满足重试条件] [不满足重试条件] alt [成功] [失败] 流代理(PullingAgent) 消费者(Consumer) 重试执行器(RetryExecutor)
5. 消息投递流程
流代理(PullingAgent) 消费者(Consumer) 缓存(QueueCache) Cursor.GetCurrent() (获取当前消息) 返回 StreamMessage DeliverMessage(message, sequenceToken) 处理消息 返回处理结果 Cursor.MoveNext() (移动到下一个消息) 返回是否还有更多消息 继续投递下一条消息 等待新消息或定期握手 alt [还有更多消息] [没有更多消息] 流代理(PullingAgent) 消费者(Consumer) 缓存(QueueCache)
6. 故障恢复流程
流代理(PullingAgent) 重启消费者(RestartedConsumer) 存储(Storage) 恢复上次处理状态 返回 lastProcessedToken GetSequenceToken(subscriptionId) 返回恢复的 StreamHandshakeToken GetCacheCursor(streamId, recoveredToken) Cursor.MoveNext() (从恢复位置的下一个开始) 保存恢复状态 握手成功,从恢复位置继续投递 流代理(PullingAgent) 重启消费者(RestartedConsumer) 存储(Storage)
7. 状态同步流程
流代理(PullingAgent) 消费者(Consumer) 存储(Storage) 处理消息完成 更新 lastProcessedToken 确认状态已保存 定期握手检查 GetSequenceToken(subscriptionId) 返回当前状态 比较当前状态与 LastToken 继续正常投递 调整投递位置 从新位置开始投递 alt [状态一致] [状态不一致] 流代理(PullingAgent) 消费者(Consumer) 存储(Storage)
8. 关键设计决策说明
8.1 为什么使用无限重试?
- 可靠性优先:握手失败意味着消息投递失败
- 网络不稳定:分布式环境中网络问题很常见
- 消费者重启:消费者可能暂时不可用
8.2 为什么需要 MoveNext()?
- 避免重复:令牌指向已处理的消息
- 连续性保证:确保从下一个未处理消息开始
- 状态一致性:保持消费者和代理状态同步
8.3 为什么需要备用方案?
- 容错性:握手失败时系统仍能工作
- 降级策略:从已知安全位置开始
- 系统可用性:避免因握手失败导致整个系统停止
8.4 为什么保存 LastToken?
- 下次握手参考:为下次握手提供基准
- 状态追踪:监控消费者状态变化
- 性能优化:减少重复的状态查询
9. 性能优化点
- 异步处理:所有操作都是异步的,避免阻塞
- 状态缓存:缓存常用状态信息,减少存储访问
- 批量操作:支持批量消息投递
- 智能重试:使用退避算法避免过度重试
- 资源管理:及时释放不需要的资源
10. 监控指标
- 握手成功率:成功握手次数 / 总握手次数
- 握手延迟:从开始握手到完成的时间
- 重试次数:平均重试次数
- 错误率:握手失败的比例
- 消息投递延迟:从握手完成到开始投递的时间
这个握手机制确保了 Orleans 流系统在复杂的分布式环境中能够可靠、高效地处理消息投递。