大家好,我是 Snow Hide,作为《左耳听风》这个专栏的学员之一,这是我打卡的第 45 天,也是我第 54 次进行这种操作。
今天我温习了该专栏里叫《弹力设计篇之“补偿事务”》、《弹力设计篇之“重试设计”》、《弹力设计篇之“熔断设计”》、《弹力设计篇之“限流设计”》、《弹力设计篇之“降级设计”》、《弹力设计篇之“弹力设计总结”》的文章。
关键词总结:ACID、BASE、原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)、基本可用性(Basic Availability)、Soft-state(软状态)、Eventual Consistency(最终一致性)、业务补偿、靠谱的业务补偿机制、业务补偿的设计重点、重试的场景、重试的策略(Exponential Backoff / 指数级退避)、Spring 的重试策略(一般策略、Backoff 相关策略)、重试设计的重点(确定重试的触发、重试时间/次数、被调用方幂等性设计、重试代码无侵入、事务相关的重试)、熔断设计(闭合 / Closed、断开 / Open、半开 / Half-Open)、熔断器实现逻辑(是否存在熔断中?、调用成功和失败的次数?、是否进行熔断?、元素是否超过设置的时长?)、熔断设计的重点(错误类型、日志监控、测试服务是否可用、手动重置、并发问题、资源分区、重试错误的请求)、限流的策略(拒绝服务、服务降级、特权请求、延迟处理、弹性伸缩)、限流的实现方式(计数器方式、队列算法、漏斗算法 / Leaky Bucket、令牌桶算法 / Token Bucket、基于响应时间的动态限流)、限流的设计要点、降级的牺牲(降低一致性、停止次要功能、简化功能)、降低数据一致性(失效、命中、更新)、降级设计要点(梳理和分析业务、降级的关键条件、可简化业务流程设计、流水账记录法、降级开关、限流程度参数、降级标签、降级演练)、弹力设计概览(负载均衡、服务发现、动态路由、自动化运维、服务调度、服务伸缩、故障迁移)、服务界耦合拆分(bulkheads 模式、自包含系统、异步通讯、自动化运维)、容错设计(错误方面、一致性方面、流控方面、自动化运维方面)弹力设计开发和运维(比较 Spring Cloud 和 Kubernetes、服务涵盖面比较、服务的功能特性)。
所学总结:
弹力设计篇之“补偿事务”
ACID
- 原子性(Atomicity)
- 一致性(Consistency)
- 隔离性(Isolation)
- 持久性(Durability)
BASE
- 基本可用性(Basic Availability)
- Soft-state(软状态)
- Eventual Consistency(最终一致性)
业务补偿
需要将服务做成幂等性(无论怎么操作,其都不会产生意想不到的结果),可以进行无数次的尝试,直至服务完成工作。
靠谱的业务补偿机制
- 清晰地描述服务要达到的目标
- 可以在业务流程开始之后串行或并行的进行所有的操作
业务补偿的设计重点
两个最重要的事情
- 将业务流程执行完毕;
- 过程出错的话就要将所做操作产生的任何东西还原至操作之前的状态。
其余设计重点
- 服务需要支持幂等性;
- 让工作流引擎来管理操作过程的状态及步骤;
- 需要考虑到业务的反向补偿流程;
- 业务补偿的逻辑是与业务相关的;
- 提供临时的资源预留机制。
弹力设计篇之“重试设计”
重试的场景
重试的策略(Exponential Backoff / 指数级退避)
Spring 的重试策略
一般策略
- NeverRetryPolicy
- AlwaysRetryPolicy
- SimpleRetryPolicy
- TimeoutRetryPolicy
- CircuitBreakerRetryPolicy
- CompositeRetryPolicy
- 乐观组合
- 悲观组合
Backoff 相关策略
- NoBackOffPolicy
- FixedBackOffPolicy
- sleeper
- backOffPeriod
- UniformRandomBackOffPolicy
- minBackOffPeriod
- maxBackOffPeriod
- ExponentialBackOffPolicy
- initialInterval
- maxInterval
- multiplier
- ExponentialRandomBackOffPolicy
重试设计的重点
确定重试的触发
确定好需要重试的条件。
重试时间/次数
非关键问题的重试时间不需要太长也不需要太多次。而对于关键问题的重试则需要延长重试的时间以及重试的次数。
被调用方幂等性设计
重试里的被调用方必须要保证它自身的幂等性,一个变幻莫测的被调用方是危险的。
重试代码无侵入
- Java Annotation 方式实现;
- Service Mesh 方式实现。
事务相关的重试
将请求上下文暂存之服务本机或数据库中,而不是当请求不成功时通过业务补偿的机制来执行复杂的回退流程。
弹力设计篇之“熔断设计”
熔断设计
闭合 / Closed
- 没有问题时
- 请求失败时
- 错误计数器超时后
- 失败次数超过阈值
断开 / Open
- 断开后
- 直接返回错误
- 返回之前的缓存
- 缓存机制
- 重置超时后
半开 / Half-Open
- 一定的服务调用成功时
- 一定的服务调用失败时
熔断器实现逻辑
是否存在熔断中?
从 allowRequest() 函数开始,判断当前状态是否是断开(熔断中)以及成功失败超时拒绝时间是否已过时。
调用成功和失败的次数?
通过 markSuccess(duration) 和 markFailure(duration) 两个函数来进行成功和失败请求的计数。
是否进行熔断?
根据 failure / (success + failure) 这个计算公式来得出是否需要通过 isOpen() 函数来进行断开操作的结论。
元素是否超过设置的时长?
熔断器会创建一个新的桶,将旧桶数据迁移至新桶中,并删除旧桶以及最老的一个计数记录。
熔断设计的重点
错误类型
根据错误类型,来估计被调用方大概需要花费的恢复时长,进而作出合理的决定,是进入重试环节,还是直接做断开操作。
日志监控
记录所有失败和成功的请求。
测试服务是否可用
在半开状态下,让熔断器自己定期检测被调用方的服务是否已经恢复正常,如果已恢复,则切回至闭合状态,让用户的新请求可以直接被放行,从而提高用户体验。
手动重置
提供可以手动将熔断器状态切换至闭合或断开的功能。
并发问题
使用无锁或原子性的方式来实现熔断器。
资源分区
熔断器只对有问题的分区进行断开操作。
重试错误的请求
被调用端需要支持幂等性,以确保当熔断器对其进行多次重试操作后不会产生每次请求成功的结果都不一致的情况。
弹力设计篇之“限流设计”
限流的策略
限流,是对并发访问进行限速。
拒绝服务
将同一时间内发起请求数最多的客户端请求全部丢弃。
服务降级
- 保住关键服务
- 提高整体性能
特权请求
将流量让给大客户,对小客户做限流处理(就像那个坑爹地某度网盘)。
延迟处理
在请求量达到峰值时,只能将大量的请求积压在消息队列里,慢慢处理。
弹性伸缩
当非数据层相关的服务处理不过来的时候,监控系统需要能够感知到,并对其做水平扩充操作,当服务不那么繁忙时,再将扩充的资源回收。
限流的实现方式
计数器方式
如果未处理请求的数量大于等于配置的阈值(计数器可以存储的未处理请求数量上限)时,系统将自动丢弃新的请求。
队列算法
先进,但是未必先出。消息队列按照请求的优先级,从高往低依次处理每一个请求。如果队列被挤满,则丢弃新的请求。
漏斗算法 / Leaky Bucket
匀速处理没有被丢弃的请求,队列被挤满后,丢弃新的请求。
令牌桶算法 / Token Bucket
按照设定的速度收录请求,正常情况下按照设定的速度处理请求,但并非一直按匀速来处理请求,在请求激增时,处理的速度也会相应的有所增长。
基于响应时间的动态限流
很难设定限流值的原因
- 数据方面
- API 方面
- 平台方面
拥塞控制算法设计要点
- 不记录所有请求,只采样
- 蓄水池近似算法
- 记录当前 QPS,若后端 P90/P99 响应太慢,则将 QPS 减半并以慢启动方式进行处理,依此类推,每次减半
- 参考 TCP 的算法,实现过程比较复杂
限流的设计要点
限流的目的
- 在指定的速度下要保持一个范围内的响应时间以及可用性;
- 整体系统的资源不会被某个租户所用超;
- 是封闭现有管道,还是临时增添几条管道;
- 在尽可能节省成本的情况下将资源的利用率最大化。
需要考虑的地方
- 在一开始就做好限流的准备;
- 限流的实现不可以产生阻塞;
- 允许人工干预限流操作;
- 限流事件需要被监控;
- 定制限流产生的错误码;
- 让后端感知限流的等级。
弹力设计篇之“降级设计”
降级的牺牲
降低一致性
只能保证流程最终结果的一致性。
停止次要功能
将大部分资源分配给重要的服务。
简化功能
忽略非关键数据并只返回重要的数据。
降低数据一致性
通过缓存来降低数据库的访问量。
失效
当缓存中没有对应数据时才从数据库中读取并将其保存至缓存。
命中
当缓存中有对应数据时直接将其返回即可。
更新
将数据更新至数据库后让缓存失效。
降级设计要点
梳理和分析业务
需要对业务进行非常详尽的梳理和分析工作。
降级的关键条件
定义出关键的降级条件之后,做好相应的防护准备,可以通过代码来实现,做成能够全自动或半自动执行的方案。
可简化业务流程设计
需要对功能的重要程度作区分,在降级时,可以决定留住哪些服务,丢弃哪些服务。
流水账记录法
需要记录服务执行到达的每一步,当有遗漏或问题时,可以对照每一步的执行结果,或某个请求操作停留的步骤。
降级开关
可以做成推送或拉取的方式。
限流程度参数
网关自动给所请求的服务传递一个限流程度的参数,当服务接收到限流程度参数后,其根据限流的程度来判断是否需要进行降级操作。
降级标签
可以根据后端返回的协议头里的降级标签,来判断部分数据不可用的问题,是否是降级所导致的。
降级演练
需要定期进行降级演习操作。
弹力设计篇之“弹力设计总结”
弹力设计概览
负载均衡
包含负载均衡 & 服务健康检查,可以通过 Nginx 或 HAProxy 等技术实现。
服务发现
包含服务发现 & 动态路由 & 服务健康检查,可以通过 Consul 或 Zookeeper 等技术实现。
自动化运维
包含自动化运维 & 服务调度 & 服务伸缩以及故障迁移,服务调度可以通过 Docker 结合 Kubernetes 来实现。
服务界耦合拆分
bulkheads 模式
业务分片、用户分片、数据库拆分。
自包含系统
从单体到微服务的中间状态,拆分一组相关的微服务,保证没有外部的依赖。
异步通讯
服务发现、事件驱动、消息队列、物业工作流。
自动化运维
服务调用链和性能的监控系统。
容错设计
错误方面
调用重试、熔断以及服务幂等性涉及。
一致性方面
强一致和弱一致,强一致性使用 2PC,最终一致性使用异步通讯方式。
流控方面
限流以及降级的技术。
自动化运维方面
网关流量调度以及服务的监控。
弹力设计开发和运维
比较 Spring Cloud 和 Kubernetes
微服务考量 | Spring Cloud & Netflix OSS | Kubernetes |
---|---|---|
配置管理 | Config Server, Consul, Netflix Archalus | Kubernetes ConfigMap & Secrets |
服务发现 | Netflix Eureka, Hashicorp Consul | Kubernetes Service & Ingress Resources |
负载均衡 | Netflix Ribbon | Kubernetes Service |
API 网关 | Netflix Zuul | Kubernetes Services & Ingress Resources |
服务安全 | Spring Cloud Security | - |
日志中心 | ELK Stack (LogStash) | ELK Stack (Fluentd) |
指标中心 | Netflix Spectator & Atlas | Heapster, Promethues, Grafana |
分布追踪 | Spring Cloud Slueth, Zipkin | OpenTracing, Zipkin |
弹性容错 | Netflix Hystrix, Turbine & Ribbon | Kubernetes Health Check & resource isolation |
自伸缩自愈 | - | Kubernetes Health Check, Self Healing, Autoscaling |
打包发布调度 | Spring Boot | Docker/Rkt, Kubernetes Scheduler & Deployment |
作业管理 | Spring Batch | Kubernetes Jobs & Scheduled Jobs |
单例应用 | Spring Cloud Cluster | Kubernetes Pods |
服务涵盖面比较
关键技术 | Spring Cloud | Kubernetes | IaaS |
---|---|---|---|
DevOps 经验 | ☑️ | ||
自伸缩自愈 | ☑️ | ||
弹性容错 | ☑️ | ☑️ | |
分布追踪 | ☑️ | ☑️ | |
指标中心 | ☑️ | ☑️ | |
日志中心 | ☑️ | ☑️ | |
API 网关 | ☑️ | ☑️ | |
作业管理 | ☑️ | ☑️ | |
单例应用 | ☑️ | ☑️ | |
负载均衡 | ☑️ | ☑️ | |
服务发现 | ☑️ | ☑️ | |
配置管理 | ☑️ | ☑️ | |
应用打包 | ☑️ | ☑️ | |
发布调度 | ☑️ | ||
进程隔离 | ☑️ | ||
环境管理 | ☑️ | ||
资源管理 | ☑️ | ||
操作系统 | ☑️ | ||
虚拟化 | ☑️ | ||
硬件存储网络 | ☑️ |
服务的功能特性
能力 | Spring Cloud(SC) 结合 Kubernetes(K) |
---|---|
DevOps 经验 | 自服务(K)、多环境能力(K) |
自伸缩自愈 | Pod/Cluster Autoscaler(K)、HealthIndicator(SC)、Scheduler(K) |
弹性容错 | HealthIndicator(SC)、Hystrix(SC)、HealthCheck(K)、Process Check(K) |
分布追踪 | Zipkin |
指标中心 | Heapster、Promethues、Grafana |
日志中心 | EFK |
作业管理 | Spring Batch(SC)、Scheduled Job(K) |
负载均衡 | Ribbon(SC)、Service(k) |
服务发现 | Service(K) |
配置管理 | Externalized Configurations(SC)、ConfigMap(K)、Secret(K) |
服务逻辑 | Apache Camel(SC)、Spring Framework(SC) |
应用打包 | Spring Boot maven plugin(SC) |
发布调度 | Deployment strategy(K)、A/B(K)、Canary(K)、Scheduler strategy(K) |
进程隔离 | Docker(K)、Pods(K) |
环境管理 | Namespaces(K)、Authorizations(K) |
资源管理 | CPU 内存管控(K)、命名空间资源配额(K) |
IaaS | GEC、Azure、CenturyLink、VMware、Openstack |
末了
重新总结了一下文中提到的内容:ACID、BASE、一致性、强一致性、最终一致性、业务补偿逻辑、业务补偿设计重点、重试的场景、重试的策略、指数及退避策略、Spring 实现的多种策略、重试设计的重点、基于 Java Annotation 实现策略、基于 Service Mesh 实现策略、熔断设计、闭合、断开、半开、正常、故障、故障后检测、熔断器实现、熔断器设计、限流的目的、限流的策略、限流的算法、计数器、队列、漏斗、令牌桶、基于响应时间限流、限流的设计要点、降级设计本质、资源不足、访问量过大、降级的方法、降低一致性、停止次要功能、简化功能、降级设计要点、弹力设计、弹力设计总概览、开发运维实践。