分布式设计模式和方法论

发布时间 2023-10-13 13:05:34作者: sTruth

一、设计模式

1. 可用性

  可用性是系统运行和工作的时间比例,通常以正常运行时间的百分比来衡量,它可能受系统错误,基础架构问题,恶意攻击和系统负载的影响。

    • 健康检查:系统实现全链路功能检查,外部工具定期通过公开端点访问系统
    • 负载均衡:使用队列起到削峰作用,作为请求和服务之间的缓冲区,以平滑间歇性的重负载
    • 节流:限制应用级别、租户或整个服务所消耗资源的范围

2. 数据管理

  由于性能,可扩展性或可用性等原因,数据通常托管在不同位置和多个服务器上,这可能带来一系列挑战。例如,必须维护数据一致性,并且通常需要跨不同位置同步数据。

    • 缓存:根据需要将数据从数据存储层加载到缓存
    • CQRS(Command Query Responsibility Segregation): 命令查询职责分离
    • 事件溯源:仅使用追加方式记录域中完整的系列事件
    • 索引表:在经常查询引用的字段上创建索引
    • 物化视图:生成一个或多个数据预填充视图
    • 拆分:将数据拆分为水平的分区或分片

3. 设计与实现

  良好的设计包括诸如组件设计和部署的一致性,简化管理和开发的可维护性,以及允许组件和子系统用于其他应用程序和其他方案的可重用性等因素。

    • 代理:反向代理
    • 适配器: 在现代应用程序和遗留系统之间实现适配器层
    • 前后端分离: 后端服务提供接口供前端应用程序调用
    • 计算资源整合:将多个相关任务或操作合并到一个计算单元中
    • 配置分离:将配置信息从应用程序部署包中移出到配置中心
    • 网关聚合:使用网关将多个单独的请求聚合到一个请求中
    • 网关卸载:将共享或专用服务功能卸载到网关代理
    • 网关路由:使用单个端点将请求路由到多个服务
    • 领导人选举:通过选择一个实例作为负责管理其他实例管理员,协调分布式系统
    • 管道和过滤器:将复杂的任务分解为一系列可以重复使用的单独组件
    • 边车:将应用的监控组件部署到单独的进程或容器中,以提供隔离和封装
    • 静态内容托管:将静态内容部署到CDN,加速访问效率

4. 消息

  分布式系统需要一个连接组件和服务的消息传递中间件,理想情况是以松散耦合的方式,以便最大限度地提高可伸缩性。异步消息传递被广泛使用,并提供许多好处,但也带来了诸如消息排序,幂等性等挑战

    • 竞争消费者:多线程并发消费
    • 优先级队列: 消息队列分优先级,优先级高的先被消费

5. 管理与监控

  应用必须公开运行时信息,管理员可以使用这些信息来管理和监视系统,以及支持不断变化的业务需求和自定义,而无需停止或重新部署应用。

6. 性能与扩展

  性能表示系统在给定时间间隔内执行任何操作的响应性。

  可伸缩性是系统处理负载增加而不影响性能或容易增加可用资源的能力,可伸缩性不仅涉及计算实例,还涉及其他元素,如数据存储,消息队列等。

7. 弹性

  弹性是指系统能够优雅地处理故障并从故障中恢复。

    • 隔离:将应用程序的元素隔离到池中,以便在其中一个失败时,其他元素将继续运行。
    • 断路器:处理连接到远程服务或资源时可能需要不同时间修复的故障。
    • 补偿交易:撤消一系列步骤执行的工作,这些步骤共同定义最终一致的操作
    • 健康检查:系统实现全链路功能检查,外部工具定期通过公开端点访问系统
    • 重试:通过透明地重试先前失败的操作,使应用程序在尝试连接到服务或网络资源时处理预期的临时故障

8. 安全

  安全性是系统能够防止在设计使用之外的恶意或意外行为,并防止泄露或丢失信息,保护敏感数据。

    • 联合身份:将身份验证委派给外部身份提供商
    • 看门人: 通过使用专用主机实例来保护应用程序和服务,该实例充当客户端与应用程序或服务之间的代理,验证和清理请求,并在它们之间传递请求和数据
    • 代客钥匙:使用为客户端提供对特定资源或服务的受限直接访问的令牌或密钥。

二、方法论

1. 资源调度

  弹性伸缩:微服务分布式无需人肉增加物理机器,在容器化技术的支撑下,我们只需要申请云资源,然后执行容器脚本即可。

    • 应用扩容:用户激增需要对服务进行扩展,包括自动化扩容,峰值过后的自动缩容

    • 机器下线:对于过时应用,进行应用下线,云平台收回容器宿主资源

    • 机器置换:对于故障机器,可供置换容器宿主资源,服务自动启动,无缝切换

  网络管理:在现有云化背景下,我们几乎不会直接接触到物理的带宽资源,而是直接由云平台统一管理带宽资源,我们需要的是对网络资源的最大化应用和有效的管理。

    • 域名申请:应用申请配套域名资源的申请,多套域名映射规则的规范

    • 域名变更:域名变更统一平台管理

    • 负载管理:多机应用的访问策略设定

    • 安全外联:基础访问鉴权,拦截非法请求

    • 统一接入:提供统一接入的权限申请平台,提供统一的登录管理

  故障快照:在系统故障时第一要务是系统恢复,同时保留案发现场也是非常重要的,资源调度平台则需要有统一的机制保存好故障现场。

    • 现场保留:内存分布,线程数等资源现象的保存,如JavaDump钩子接入

    • 调试接入:采用字节码技术无需入侵业务代码,可以供生产环境现场日志打点调试

 

2. 流量调度

  分布式系统中,最先受到考验的关口是网关,因此需要关注与管理系统流量,在系统可容纳的流量上限内,把资源留给最优质的流量使用,把非法恶意的流量挡在门外。

  负载均衡:是对服务如何消化流量的通用设计,通常分为物理层的底层协议分流的硬负载均衡和软件层的软负载。

    • 交换机
    • F5
    • LVS/ALI-LVS
    • Nginx/Tengine
    • VIPServer/ConfigServer

  网关设计:负载均衡首当其冲的就是网关,因为中心化集群流量最先打到的地方就是网关了。

    • 高性能:网关设计第一需要考虑的是高性能的流量转发,网关单节点通常能达到上百万的并发流量

    • 分布式:流量压力分担和灾备

    • 业务筛选:简单规则过滤掉大部分的恶意流量

  流量管理

    • 请求鉴权:拦截非法请求

    • 数据缓存:多数无状态的请求存在数据热点,采用CDN解决

  流控控制:剩下的真实流量采用不同的算法来分流请求

    • 流量分配

      • 计数器
      • 队列
      • 漏斗
      • 令牌桶
      • 动态流控
    • 流量限制:预估系统流量上限,当流量超过阈值后,通过牺牲部分流量来保全系统的可用性。

      • 限流策略
      • QPS粒度
      • 线程数粒度
      • RT阈值

      • 限流工具 Sentinel

 

3. 服务调度

  做好流量调度后,剩下的就是服务自身的健壮性了。

  注册中心:网关是流量的集散地,而注册中心则是服务的根据地

    • 状态类型:通过注册中心检测服务是否可用

    • 生命周期:应用服务不同的状态组成了应用的生命周期

  版本管理

    • 集群版本:集群不使用应用有自身对应的版本号,由不同服务组成的集群也需要定义大的版本号

    • 版本回滚:在部署异常时候可以根据大的集群版本进行回滚管理

  服务编排:通过消息的交互序列来控制各个部分资源的交互,微服务环境下服务众多我们需要有一个总的协调器来协议服务之间的依赖,调用关系。

    • K8S
    • Spring Cloud
      • HSF
      • ZK+Dubbo

  服务控制

    • 发现:从云平台申请了容器宿主资源后,通过自动化脚本就可以启动应用服务,启动后服务则需要发现注册中心,并且把自身的服务信息注册到服务网关,也即是网关接入。注册中心则会监控服务的不同状态,做健康检查,把不可用的服务归类标记。

      • 网关接入
      • 健康检查
    • 降级:当流量激增时,首先需要对客户端限流,然后对服务本身做降级,事后再做一些人工补救。

      • 降低一致性约束
      • 关闭非核心服务
      • 简化功能
    • 熔断:是对过载的一种自身保护。比如当我们的服务因为业务问题导致不断对数据库进行查询时,这时数据库本身需要熔断来保证不被应用拖垮,并且返回信息告诉该服务不要再盲目调用了。

      • 闭合状态
      • 半开状态
      • 断开状态
      • 熔断工具- Hystrix
    • 幂等:任意多次执行所产生的影响均与一次执行的影响相同。

      • 全局一致性ID
      • Snowflake

 

4. 数据调度

  数据存储最大的挑战就是对数据冗余的管理,冗余多了会导致效率变低且占用资源,副本少了起不到灾备的作用,我们通常的做法是把有转态的请求,通过转态分离,转化为无状态请求。

  状态转移:分离状态至全局存储,请求转换为无状态流量,比如我们通常会将登陆信息缓存至全局redis中间件,而不需要在多个应用中去冗余用户的登陆数据。

  分库分表:数据横向扩展

  分片分区:多副本冗余

 

5. 自动化运维

  配置中心:全局配置中心按环境来区分,统一管理,减少多处配置的混乱局面

    • switch
    • diamend

  部署策略

    • 停机部署
    • 滚动部署
    • 蓝绿部署
    • 灰度部署
    • A/B测试

  作业调度:即任务调度,传统的方式是在Linux机器上配置crond定时任务或者直接在业务代码里面完成调度业务,现在则是成熟的中间件来代替。

    • SchedulerX
    • Spring定时任务

  应用管理:应用重启、上下线操作、日志清理。

    • 应用重启
    • 应用下线
    • 日志清理

 

6. 容错处理

  通常有主动和被动的方式来处理。主动方式是在错误出现时主动进行多次重试。被动方式是错误的事情已经发生,通过处理将负面影响降到最小。

  重试设计:关键在于重试时间和次数的设计。

  事务补偿:符合最终一致性理念。它是补偿操作失败前由已成功完成的步骤所执行的工作。补偿事务中步骤的顺序不一定与原始操作中步骤的顺序完全相反。

 

7. 全栈监控

  基础层:是对容器资源的监测,包含各个硬件指标的负载情况

    • CPU,IO,内存,线程,吞吐

  中间件:是对中间件本身的健康情况进行监控

  应用层

    • 性能监控:对每个应用服务的实时指标(qps,rt),上下游依赖等进行监控

    • 业务监控:对业务异常的情况做报警

  监控链路

    • zipkin/eagleeye
    • sls
    • goc
    • Alimonitor

 

8. 故障恢复

  故障已经发生后,第一要做的就是马上消除故障,确保系统服务正常可用。

  应用回滚

    回滚之前需要保存好故障现场,以便排查原因。

  基线回退

    应用服务回滚后,代码基线也需要revert到前一版本。

  版本回滚

    整体回滚需要服务编排,通过大版本号对集群进行回滚。

 

9. 性能调优

  分布式锁

    缓存是解决性能问题的一大利器,理想情况下,每个请求不需要额外计算立刻能获取到结果返回时最快的。小到CPU的三级缓存,大到分布式缓存,缓存无处不在,分布式缓存需要解决的就是数据的一致性,这个时候我们引入了分布式锁的概念,如何处理分布式锁的问题将决定我们获取缓存数据的效率。

  高并发

    多线程编程模式提升了系统的吞吐量,但也同时带来了业务的复杂度。

  异步

    事件驱动的异步编程是一种新的编程模式,摒弃了多线程的复杂业务处理问题,同时能够提升系统的响应效率。