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区间,用完再取,使用乐观锁机制控制并发。