找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

216

积分

0

好友

30

主题
发表于 前天 05:17 | 查看: 8| 回复: 0

多云的现实

如果您仍在争论多云是否值得应对其复杂性,那么您可能问错了问题。事实是,86%的组织已经在多个云提供商环境中运营,问题不在于您是否要构建跨云边界的分布式系统,而在于您是要优雅地构建它,还是要在深夜排查级联故障。

Flexera 2025年云状态报告给出了严峻的现实:仅12%的组织使用单一云提供商,仅2%维持单一私有云环境。其余86%已采用多云策略,其中70%使用混合架构,将本地系统与多个云提供商编织在一起。

这并非趋势,而是我们架构系统方式的根本转变。但为什么多云已成为必然而非可选?

答案在于业务需求与技术现实的碰撞:

  • 监管要求通常规定数据在不同区域和提供商间的驻留
  • 最佳服务分散在不同提供商之间。例如,特定组织可能因其用例偏好AWS的安全性、Azure的AI能力和Google Cloud的分析能力
  • 集中风险缓解要求避免提供商级别的单点故障
  • 遗留系统现代化基于工作负载特性创建了到不同云的自然迁移路径
  • 当您不被锁定在单一提供商时,供应商谈判能力会增强

然而,大多数架构讨论仍将多云视为边缘情况而非默认场景。

案例研究:FinBank的多云之旅

让我们通过一个虚构银行(称为FinBank)来审视现实场景,这家百年传统银行正在现代化其基础设施,以与敏捷的金融科技初创公司竞争,同时保持严格的合规性。

FinBank的原始架构代表了数十年的演进:核心银行功能、信贷决策和卡服务被封装在微服务中,并通过API网关暴露。其本地事件驱动架构具有向平台服务发出事件和日志的微服务,数百个互连组件处理客户参与、分析、商业智能、监管报告和第三方集成。

他们的迁移策略遵循务实方法:

  • 由于监管要求和迁移复杂性,核心银行系统保留在本地
  • 风险管理组件迁移到AWS,以利用其全面的安全服务和合规认证
  • 高级分析和商业智能移至Azure,以获得强大的数据和AI能力
  • DevOps服务集中在Azure中,为所有环境提供统一的流水线部署

这种看似简单的迁移立即浮现出每个多云架构师必须应对的复杂挑战。真正的问题不在于这些挑战是否会出现,而在于您是否已做好准备。

多云事件驱动架构的挑战

延迟优化

多云延迟不仅关乎网络速度,更关乎跨云边界架构决策的复合效应。考虑一个需要从本地遍历到AWS进行风险评估,然后到Azure进行分析处理,再返回本地进行核心银行更新的交易。每次跳转都会引入延迟,但累积效应可以将低于100毫秒的交易转变为数秒操作。

不同云提供商有不同的机制来处理组件间的连接问题。例如,Azure提供ExpressRoute,AWS有Direct Connect。它们提供可靠、专用链接,帮助您绕过公共互联网,在不同组件间提供高性能、低延迟连接。

但仅靠网络改进无法解决代码级低效问题。您还需要确保在代码级别处理延迟考虑。

以下是忽略多云现实的典型交易服务实现:

// 发布者
public TransactionService(IKafkaProducer kafkaProducer)
{
    _kafkaProducer = kafkaProducer;
}

public async Task CreateTransaction(Transaction transaction)
{
    // 立即创建并发布事件
    var transactionEvent = new TransactionCreatedEvent
    {
        TransactionId = transaction.Id,
        AccountId = transaction.AccountId,
        Amount = transaction.Amount,
        Timestamp = DateTime.UtcNow
    };

    await _kafkaProducer.ProduceAsync("transactions", transactionEvent);
}
// 订阅者
private async Task ProcessTransactionEvent(TransactionCreatedEvent transactionEvent)
{
    try
    {
        // 执行风险分析
        var riskScore = await PerformRiskAnalysis(transactionEvent);

        // 基于风险评分采取行动
        if (riskScore > RiskThreshold)
        {
            await FlagForReview(transactionEvent);
        }
    }
    catch (Exception ex)
    {
        // 记录错误并继续
        _logger.LogError(ex, "Error processing transaction event");
    }
}

这个简单实现没有针对多云间延迟的特殊考虑。如何修复它?您可以实施几种优化:

优化包括:

  • 压缩配置以减少事件跨越云边界时的带宽使用,因为每个字节都会影响延迟
  • 批量优化,使用更大的批量大小,即使考虑较大尺寸的延迟,也能将端到端延迟减少40%到60%
  • 校准超时值以优化持续时间;默认超时会导致超时过长浪费资源或过短导致过早重试和级联故障
  • 基于账户的分区,确保相关交易遵循定义的路由,实现有效的缓存策略

发布者修复示例:

public async Task CreateTransaction(Transaction transaction)
{
    #region 创建事件...

    // 为多云配置适当的消息设置
    var producerConfig = new ProducerConfig
    {
        // 压缩以减少带宽使用
        CompressionType = CompressionType.Snappy,

        // 跨云传输的批量优化
        BatchSize = 32768,   // 更大的批次以提高传输效率
        LingerMs = 20,       // 小延迟以改进批处理

        // 跨云延迟的网络优化
        SocketTimeoutMs = 30000,        // 更长的套接字超时用于跨云
        DeliveryTimeoutMs = 30000,      // 扩展超时用于跨云
        SocketNagleDisable = true       // 禁用Nagle算法
    };

    // 使用基于账户的分区实现一致路由
    string topic = $"transactions-{transaction.AccountId % 10}";
    string key = transaction.AccountId.ToString();

    // 使用配置的消息设置发布到Kafka
    await _kafkaProducer.ProduceAsync(topic, transactionEvent, key, producerConfig);
}

这些延迟优化不是微优化,而是决定系统是否优雅扩展或负载下失败的基本架构决策。

弹性:超越即时可用性

这是一个令人不适的真相:大多数弹性策略关注了错误的问题。作为工程师,我们通常将精力投入到处理中断期间或服务组件宕机时发生的故障。同样重要的是,在中断结束后如何从这些故障中恢复。这种恢复方法创建了"快速失败"但"永不恢复"的系统。

考虑典型多云中断期间发生的情况:

没有适当弹性设计时:

  • 失败的事件永远丢失
  • 服务在没有断路器的情况下继续冲击失败的依赖项
  • 中断后,缺少重放能力意味着数据不一致无限期持续

问题在多云环境中加剧,不同提供商有不同的故障模式、恢复时间和SLA保证。您的系统弹性仅与其最弱的跨云依赖一样强。

构建全面弹性需要系统方法:

  • 事件存储,如使用发件箱模式的持久存储、专用存储或Kafka保留
  • 断路器通过在依赖项不健康时快速失败来防止级联故障
  • 系统重放,包括在中断后恢复丢失事件的自动恢复机制

以下代码示例显示了上述修复的示例实现:

public async Task CreateTransaction(Transaction transaction)
{
    // 首先保存到数据库
    await _transactionRepository.SaveAsync(transaction);

    // 事件创建 - 与之前相同

    // 来自先前示例的延迟设置

    // 使用重试模式的弹性策略进行发布
    await _resiliencePolicy
        .WaitAndRetryAsync(
            5, // 可配置,在应用级别重试5次
            attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)), // 指数退避
            onRetry: (ex, timeSpan, attempt, ctx) =>
            {
                _logger.LogWarning(ex, "重试 {Attempt} 发布交易事件 {TransactionId}", 
                                   attempt, transaction.Id);
            })
        .ExecuteAsync(async () =>
        {
            // 一致路由的分区策略
            var topic = $"transactions-{transaction.AccountId % 10}";
            var key = transaction.AccountId.ToString();

            // 使用交付处理程序确认交付
            var deliveryResult = await _kafkaProducer.ProduceAsync(
                topic,
                transactionEvent,
                key,
                producerConfig);

            if (deliveryResult.Status != PersistenceStatus.Persisted)
            {
                throw new KafkaDeliveryException(
                    $"消息交付失败: {deliveryResult.Status}");
            }

            // 记录成功交付
            _logger.LogInformation(
                "交易事件成功发布: {TransactionId}, 分区: {Partition}, 偏移: {Offset}",
                transaction.Id, deliveryResult.Partition, deliveryResult.Offset);
        });
}

事件存储、弹性策略和系统事件重放能力的组合创建了不仅能在故障中存活,还能自动恢复的分布式系统,这是多云架构的关键要求。

事件排序

在单节点系统中,事件排序是简单的;事件按发生顺序处理。在分布式系统中,尤其是在具有不同网络特征的跨云提供商中,事件排序成为复杂的协调问题。

考虑此场景:本地系统同时向AWS和Azure发送"交易"事件。由于网络延迟差异,Azure首先接收并处理事件,执行欺诈分析,并将结果发送到AWS,所有这些都在AWS接收原始创建事件之前完成。

后果严重:

  • 风险管理系统处理乱序事件
  • 欺诈检查在交易验证之前完成
  • 监管报告包含不一致数据
  • 由于时间不一致,财务审计失败

由于延迟和可用性权衡,分布式锁或共识算法等传统解决方案在跨云边界时效果不佳。相反,成功的多云架构使用多层方法。

在发布者级别,创建交易的任何发布者应确保每个事件获得严格递增的序列号。发布者有责任确保任何发出的事件具有严格递增的序列号。

如果您已经使用基于账户的分区,那也有帮助,因为类似交易通过类似服务,为您提供内部固有的消息排序。

在订阅者级别,您需要做验证。每个订阅者需要做序列验证,确保事件以正确的预期序列处理。事件处理也需要延迟。在我们的示例中,AWS从Azure接收消息早于从本地接收时,它需要延迟处理消息2,直到它接收并完成处理消息1。

分布式系统中的一致性在这些场景中也非常重要。您希望系统强一致,还是可以接受最终一致?这个选择带来真实妥协:更强的一致性保证通常意味着较慢的性能(由于协调开销)和更高成本(更复杂的基础设施、额外网络调用和资源使用)。同时,最终一致可以提供更好性能和更低成本,但要求您的应用处理临时不一致。不同组件基于这些权衡可能有不同的一致性要求。解决方案取决于您的特定应用需求以及您愿意做出的妥协。

关键洞察:一致性不是二元的,它在频谱上。例如,Azure Cosmos DB有五个一致性级别,广泛频谱取决于您的应用需求以及您希望在分布式设置中如何处理它们。

重复事件

网络故障、重试和跨云通信固有地创建重复事件。虽然重复风险处理仅浪费资源,但重复金融交易创建监管噩梦和审计失败。

挑战在多云环境中倍增,不同提供商有不同的重试策略、超时行为和故障模式。单个业务交易可能触发跨云边界的多个技术事件,其中每个事件都容易重复。

成功的重复处理需要四层防御策略:

首先,从您的发布者开始。生成事件的代码需要确保它创建唯一事件;例如,使用云事件模式,跨不同提供商使用的描述云事件的开放规范。第二,在您的生产者配置级别。假设您使用Kafka作为发布的消息代理。Kafka有一个称为"幂等"的生产者配置设置,确保跨网络您不会有重复事件。第三,在您的订阅者级别,在您的实现中处理重复事件。当您接收事件时,首先检查您的进程表。如果您的交易存在那里,它是重复的,因此您忽略它。如果它不存在,继续处理并在该交易日志表中为未来重复事件检查添加行。最后,确保您的事件处理程序实现本身是幂等的,意味着如果您为相同事件重新运行,它不应对事件产生负面影响。这种深度防御方法认识到重复在分布式系统中是不可避免的——目标是优雅地处理它们而非完全防止它们。

可操作见解:DEPOSITS框架

成功的多云事件驱动架构遵循这些原则:

设计失败。假设组件将在最糟糕时间失败。从第一天起将故障处理构建到系统的每个方面。

拥抱事件存储。持久事件存储自然地解决许多分布式系统挑战,并启用强大的恢复场景。

优先定期审查。持续审计您的架构以寻找优化机会。多云系统快速演进,昨天的最优配置可能是今天的瓶颈。

可观察性优先。您无法调试您看不到的内容。大力投资跨所有云边界的分布式跟踪、指标和日志记录。

从小开始,逐渐扩展。同时迁移整个架构是灾难的配方。从隔离、充分理解的工作负载开始。

投资健壮的事件骨干。您的消息基础设施是分布式架构的神经系统。不要在可靠性、性能或操作工具上节省。

团队教育。分布式系统需要专业知识。云提供商快速创新,您的团队需要持续技能提升以跟上步伐。

成功。遵循这些原则导致在多云环境中蓬勃发展的系统,而非仅仅存活。

前进之路:拥抱复杂性

多云事件驱动架构代表我们设计和操作分布式系统方式的根本转变。挑战是真实、复杂且通常违反直觉的。但替代方案,避免多云,在当今技术格局中不可行。

成功的组织是那些将多云复杂性视为设计约束而非操作后考虑的组织。他们投资正确的抽象,从一开始构建全面的故障处理,并创建具有深度分布式系统专业知识的团队。

问题不在于多云是否值得复杂性,而在于您是否在复杂性控制您之前控制它。选择是您的,但凌晨3点的电话不会等待您的决定。

从全面可观察性开始,将失败视为学习机会,并投资健壮的技术基础设施和团队能力。最重要的是,记住成功的多云架构不是构建的,它们是在火中演化、测试和持续精炼的。

未来属于能够跨云边界蓬勃发展的系统。开始构建它们的时间就是现在。

您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区(YunPan.Plus) ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-1 12:48 , Processed in 0.055215 second(s), 37 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 CloudStack.

快速回复 返回顶部 返回列表