4.15. 分布式设计¶
4.15.1. 扩展性设计¶
- 
- 总结下来,通用的套路就是分布、缓存及异步处理。 
 
- 
- 水平切分+垂直切分 
- 利用中间件进行分片如,MySQL Proxy。 
- 利用分片策略进行切分,如按照ID取模。 
 
- 
- 分布式服务+消息队列。 
 
4.15.2. 稳定性 & 高可用¶
- 
- 可扩展:水平扩展、垂直扩展。 通过冗余部署,避免单点故障。 
- 隔离:避免单一业务占用全部资源。避免业务之间的相互影响 2. 机房隔离避免单点故障。 
- 解耦:降低维护成本,降低耦合风险。减少依赖,减少相互间的影响。 
- 限流:滑动窗口计数法、漏桶算法、令牌桶算法等算法。遇到突发流量时,保证系统稳定。 
- 降级:紧急情况下释放非核心功能的资源。牺牲非核心业务,保证核心业务的高可用。 
- 熔断:异常情况超出阈值进入熔断状态,快速失败。减少不稳定的外部依赖对核心服务的影响。 
- 自动化测试:通过完善的测试,减少发布引起的故障。 
- 灰度发布:灰度发布是速度与安全性作为妥协,能够有效减少发布故障。 
 
- 
- 设计原则:数据不丢(持久化);服务高可用(服务副本);绝对的100%高可用很难,目标是做到尽可能多的9,如99.999%(全年累计只有5分钟)。 
 
硬件负载均衡¶
- 
- 主要是和F5对比。 
 
软件负载均衡¶
- 《几种负载均衡算法》 轮寻、权重、负载、最少连接、QoS 
- 
- 配置简单,更新速度慢。 
 
- 
- 简单轻量、学习成本低;主要适用于web应用。 
 
- 
- 配置比较负载、只支持到4层,性能较高。 
 
- 
- 支持到七层(比如HTTP)、功能比较全面,性能也不错。 
 
- 《Haproxy+Keepalived+MySQL实现读均衡负载》 - 主要是用户读请求的负载均衡。 
 
限流¶
- 
- 计数器:通过滑动窗口计数器,控制单位时间内的请求次数,简单粗暴。 
- 漏桶算法:固定容量的漏桶,漏桶满了就丢弃请求,比较常用。 
- 令牌桶算法:固定容量的令牌桶,按照一定速率添加令牌,处理请求前需要拿到令牌,拿不到令牌则丢弃请求,或进入丢队列,可以通过控制添加令牌的速率,来控制整体速度。Guava 中的 RateLimiter 是令牌桶的实现。 
- Nginx 限流:通过 - limit_req等模块限制并发连接数。
 
应用层容灾¶
- 
- 雪崩效应原因:硬件故障、硬件故障、程序Bug、重试加大流量、用户大量请求。 
- 雪崩的对策:限流、改进缓存模式(缓存预加载、同步调用改异步)、自动扩容、降级。 
- Hystrix设计原则: - 资源隔离:Hystrix通过将每个依赖服务分配独立的线程池进行资源隔离, 从而避免服务雪崩。 
- 熔断开关:服务的健康状况 = 请求失败数 / 请求总数,通过阈值设定和滑动窗口控制开关。 
- 命令模式:通过继承 HystrixCommand 来包装服务调用逻辑。 
 
 
- 
- 主要策略:失效瞬间:单机使用锁;使用分布式锁;不过期; 
- 热点数据:热点数据单独存储;使用本地缓存;分成多个子key; 
 
跨机房容灾¶
- 
- 通过自研中间件进行数据同步。 
 
- 
- 注意延迟问题,多次跨机房调用会将延时放大数倍。 
- 建房间专线很大概率会出现问题,做好运维和程序层面的容错。 
- 不能依赖于程序端数据双写,要有自动同步方案。 
- 数据永不在高延迟和较差网络质量下,考虑同步质量问题。 
- 核心业务和次要业务分而治之,甚至只考虑核心业务。 
- 异地多活监控部署、测试也要跟上。 
- 业务允许的情况下考虑用户分区,尤其是游戏、邮箱业务。 
- 控制跨机房消息体大小,越小越好。 
- 考虑使用docker容器虚拟化技术,提高动态调度能力。 
 
容灾演练流程¶
- 《依赖治理、灰度发布、故障演练,阿里电商故障演练系统的设计与实战经验》 - 常见故障画像 
- 案例:预案有效性、预案有效性、故障复现、架构容灾测试、参数调优、参数调优、故障突袭、联合演练。 
 
平滑启动¶
- 平滑重启应用思路 1.端流量(如vip层)、2. flush 数据(如果有)、3, 重启应用 
- 《JVM安全退出(如何优雅的关闭java服务)》 推荐推出方式:System.exit,Kill SIGTERM;不推荐 kill-9;用 Runtime.addShutdownHook 注册钩子。 
- 《常见Java应用如何优雅关闭》 Java、Spring、Dubbo 优雅关闭方式。 
4.15.3. 数据库扩展¶
读写分离模式¶
分片模式¶
- 
- 中间件: 轻量级:sharding-jdbc、TSharding;重量级:Atlas、MyCAT、Vitess等。 
- 问题:事务、Join、迁移、扩容、ID、分页等。 
- 事务补偿:对数据进行对帐检查;基于日志进行比对;定期同标准数据来源进行同步等。 
- 分库策略:数值范围;取模;日期等。 
- 分库数量:通常 MySQL 单库 5千万条、Oracle 单库一亿条需要分库。 
 
- 
- 分区:是MySQL内部机制,对客户端透明,数据存储在不同文件中,表面上看是同一个表。 
- 分表:物理上创建不同的表、客户端需要管理分表路由。 
 
4.15.4. 服务治理¶
服务注册与发现¶
- 
- 客户端服务发现模式:客户端直接查询注册表,同时自己负责负载均衡。Eureka 采用这种方式。 
- 服务器端服务发现模式:客户端通过负载均衡查询服务实例。 
 
- 《SpringCloud服务注册中心比较:Consul vs Zookeeper vs Etcd vs Eureka》 - CAP支持:Consul(CA)、zookeeper(cp)、etcd(cp) 、euerka(ap) 
- 作者认为目前 Consul 对 Spring cloud 的支持比较好。 
 
- 
- 优点:API简单、Pinterest,Airbnb 在用、多语言、通过watcher机制来实现配置PUSH,能快速响应配置变化。 
 
服务路由控制¶
- 
- 原则:透明化路由 
- 负载均衡策略:随机、轮询、服务调用延迟、一致性哈希、粘滞连接 
- 本地路由优先策略:injvm(优先调用jvm内部的服务),innative(优先使用相同物理机的服务),原则上找距离最近的服务。 
- 配置方式:统一注册表;本地配置;动态下发。 
 
4.15.5. 分布式一致¶
CAP 与 BASE 理论¶
- 
- 一致性分类:强一致(立即一致);弱一致(可在单位时间内实现一致,比如秒级);最终一致(弱一致的一种,一定时间内最终一致) 
- CAP:一致性、可用性、分区容错性(网络故障引起) 
- BASE:Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性) 
- BASE理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。 
 
分布式锁¶
- 
- 基于数据库的分布式锁:优点:操作简单、容易理解。缺点:存在单点问题、数据库性能够开销较大、不可重入; 
- 基于缓存的分布式锁:优点:非阻塞、性能好。缺点:操作不好容易造成锁无法释放的情况。 
- Zookeeper 分布式锁:通过有序临时节点实现锁机制,自己对应的节点需要最小,则被认为是获得了锁。优点:集群可以透明解决单点问题,避免锁不被释放问题,同时锁可以重入。缺点:性能不如缓存方式,吞吐量会随着zk集群规模变大而下降。 
 
- 
- 清楚的原理描述 + Java 代码示例。 
 
- 
- 基于 setnx(set if ont exists),有则返回false,否则返回true。并支持过期时间。 
 
- 
- 利用 memcached 的 add(有别于set)操作,当key存在时,返回false。 
 
分布式一致性算法¶
PAXOS¶
Zab¶
Raft¶
- 
- 三种角色:Leader(领袖)、Follower(群众)、Candidate(候选人) 
- 通过随机等待的方式发出投票,得票多的获胜。 
 
Gossip¶
两阶段提交、多阶段提交¶
幂等¶
- 
- 幂等特性的作用:该资源具备幂等性,请求方无需担心重复调用会产生错误。 
- 常见保证幂等的手段:MVCC(类似于乐观锁)、去重表(唯一索引)、悲观锁、一次性token、序列号方式。 
 
分布式一致方案¶
分布式 Leader 节点选举¶
TCC(Try/Confirm/Cancel) 柔性事务¶
- 
- 基于BASE理论:基本可用、柔性状态、最终一致。 
- 解决方案:记录日志+补偿(正向补充或者回滚)、消息重试(要求程序要幂等);“无锁设计”、采用乐观锁机制。 
 
4.15.6. 分布式文件系统¶
- 
- HDFS:大批量数据读写,用于高吞吐量的场景,不适合小文件。 
- FastDFS:轻量级、适合小文件。 
 
4.15.7. 唯一ID 生成¶
全局唯一ID¶
- 
- Twitter 方案(Snowflake 算法):41位时间戳+10位机器标识(比如IP,服务器名称等)+12位序列号(本地计数器) 
- Flicker 方案:MySQL自增ID + “REPLACE INTO XXX:SELECT LAST_INSERT_ID();” 
- UUID:缺点,无序,字符串过长,占用空间,影响检索性能。 
- MongoDB 方案:利用 ObjectId。缺点:不能自增。 
 
- 
- 在数据库中创建 sequence 表,用于记录,当前已被占用的id最大值。 
- 每台客户端主机取一个id区间(比如 1000~2000)缓存在本地,并更新 sequence 表中的id最大值记录。 
- 客户端主机之间取不同的id区间,用完再取,使用乐观锁机制控制并发。