1.大网站的特点
- 高并发 高流量 亿级用户 Google IP日均3亿 QQ最大在线用户数1.4亿。
- 高可用 系统7x24不间断服务。
- 海量数据:用大量服务器管理海量数据,Facebook每周10亿照片,百度收录数百亿网页,Google有百万台服务器为全球用户提供服务
- 用户分布广泛 网络情况复杂:为全球用户提供服务
- 安全环境恶劣:互联网站容易受到攻击
- 需求快速变更 发布频繁:每周都有版本发布上线,中小型网站更频繁,一天几十次。
- 渐进式发展:慢慢运营,不是一开始就开发好的,和网站的架构发展演化对应。
2.大型网站架构演化发展过程
-
初始阶段:一台服务器,放应用程序、文件、数据库。
-
应用服务和数据服务分离:应用和数据分离,放应用服务器、文件服务器、数据服务器。这3台服务器对硬件资源的要求各不相同
- 应用服务器需要处理大量业务逻辑 CPU
- 数据库服务器 磁盘检索数据缓存 内存和磁盘
- 文件服务器 存储用户上传文件 更大磁盘
-
使用缓存改善网站性能:使用大内存服务器作为专门缓存服务器
-
使用应用服务器集群改善网站并发处理能力:增加多个应用服务器,结合负载均衡调度服务器进行请求调度。
-
数据库读写分离:使用主从数据库,主库写,从句读,通常会使用专门数据访问模块,使数据库读写分离对应用透明。
-
反向代理和CDN:使用反向代理服务器和CDN服务器进行内容缓存,尽早将请求
-
使用分布式文件系统和分布式数据库:数据库拆分的最后手段,单表规模巨大的时候使用。
-
实用NoSQL和搜索引擎:对于复杂的检索和存储使用非关系型数据库技术实现。
-
业务拆分:将网站业务分成不同产品线,分归不同业务团队负责。
-
分布式微服务:公用服务抽取出来,独立部署,由这些可复用的服务连接数据库,提供公用服务,应用系统只需要管理用户界面。
3.CAP和Base
-
C 强一致性
-
A 可用性
-
P 分区容错性
-
CA 传统Oracle数据库
-
AP 大多数网站架构选择
-
CP Redis Mongodb
-
BA基本可用
-
Soft state 软状态
-
Eventually consistent 最终一致性
一致性算法:paxos raft zab算法等
4.负载均衡策略
- 轮询法:每个请求按时间顺序逐一分配给不同的服务器,不关注服务器当前的实际连接数和系统负载。
- 指定权重轮询:根据后台服务器的性能,指定轮询的机率
- IP-hash:每个请求按访问的IP地址,hash到一个固定的服务器,可以解决session
- Fair:按服务器响应时间分配,响应时间短的优先
- Url-hash:按照url,hash到固定服务器,在缓存的场景下高效。
5.分布式锁
分布式环境下jvm的锁是无效的,必须使用分布式锁保证数据一致性(解决秒杀系统中的超卖问题使用了lua脚本,使商品数量减少操作以原子性方式进行)。
5.1.基于数据库实现:
悲观方法:比如可以建立一张表存储在使用的资源信息,对资源名称建立唯一索引。在想获得锁时向该表插入数据,由于资源名被设定了唯一性约束,如果表中已经有了一行数据在使用这个资源,那么就无法插入,那么认为是获取不到锁。如果需要释放锁就从该表中删除对应记录即可。
乐观方法:CAS,递增版本号
缺点:数据库的性能直接影响分布式锁的性能;没有锁失效机制(插入方法信息后宕机,则恢复后会找不到锁,可以新增一列记录失效时间)
5.2.基于缓存(redis):
使用命令进行加锁,锁释放和超时时间设置。(setnx,delete,expire)
加锁时会分配UUID,释放时根据该值进行判断是否为当前持有的锁。redis性能高,其操作命令可以很好的支持分布式锁。
5.3.基于zookeeper:
基于zookeeper的临时有序节点,对某个方法加锁时,在zookeeper上的该方法对应的指定节点目录下,生成一个唯一的瞬时有序节点。获得锁时监听比自己小的节点,当自身为线程顺序最小的节点时,获得锁。
6.分布式存储
-
中间控制节点架构 HDFS
- 从namenode取得文件位置(哪个datanode),然后从位置取得具体数据
- namenode通常主备部署,datanode为集群
- 通过横向扩展datanode的数量来增加承载能力
-
完全无中心架构—计算模式 Ceph
- 客户端通过设备映射关系计算出写入数据位置,客户端直接与存储节点通信
- 核心组件有Mon服务,OSD服务和MDS服务等,Mon服务负责存储系统硬件逻辑关系,OSD服务实现对磁盘的管理,实现真正数据读写
-
完全无中心架构—一致性哈希 Swift
- 将设备做成哈希环,根据数据名称计算映射到的哈希环的位置,从而实现数据定位
- 通过账户名/容器名/对象名三个名称组成一个位置的标识,通过该唯一标识可以计算出一个整型数来,计算过程在名为Proxy的服务中进行
7.分布式事务
7.1.2PC
XA协议中分为两端
- 准备阶段:事务管理器要求每个涉及到事务的数据库预提交(precommit),并反映是否可以提交
- 提交阶段:事务协调器要求每个数据库提交数据,或者回滚数据
优点:
- 尽量保证了数据的强一致,实现成本较低
缺点:
- 单点问题:事务管理器在整个流程中扮演重要角色,如果宕机可能会让资源管理器一直阻塞
- 同步阻塞:准备就绪之后,资源管理器中的资源一直处于阻塞,直到提交完成,释放资源
- 数据不一致:只有部分参与者收到并执行了commit,其他的没收到而阻塞,就会产生不一致
成本低但是由于单点问题不能支持高并发
7.2.3PC
协议分为三个阶段
- 询问阶段 协调者询问参与者是否可以完成指令,只需要回答是或者不是,该阶段不需要做真正操作,超时停止
- 准备阶段 类似2PC的第一阶段
- 提交阶段 类似2PC的第二阶段
和2PC的区别
- 增加询问阶段,确保尽早发现无法执行的操作,减少类似情况的发生
- 在准备阶段之后,协调者和参与者都增加了超时,超时之后则默认提交成功,好处是不会阻塞和永远锁定资源
7.3.TCC
相比XA,解决了几个缺点
- 解决了协调者单点,由主业务方发起并完成这个业务活动,变成多点,引入集群
- 阻塞同步:引入超时,超时后进行补偿,并且不会锁定整个资源,将资源转换为业务逻辑形式,粒度变小
- 数据一致性:有了补偿机制之后,由业务活动管理器控制一致性 脑裂问题
阶段
- Try阶段:尝试执行,完成所有业务检查,预留必须业务资源
- Confirm阶段:确认真正执行业务,不做任何业务检查,只使用Try阶段预留业务资源,Confirm需要满足幂等性,失败后可以重试
- Cancel阶段:取消执行,释放Try阶段预留业务资源,Cancel需要满足幂等性
适合如下场景
- 强隔离型,严格要求一致性要求的活动业务
- 执行时间较短的业务
7.4.本地消息表
发出消息时在本地消息表增加一条记录,当对方收到之后更新本地消息表。核心是把大事务转变为小事务,BASE理论
7.5.MQ事务
RocketMQ中实现的分布式事务,对本地消息表的封装,将本地消息表移动到MQ内部。
- 第一阶段Prepared消息,拿到消息地址
- 第二阶段执行本地事务
- 第三阶段通过第一阶段的地址访问消息,并修改地址,消息接受者就可以使用这个消息。
如果消费超时,则要一直重试,消息接收端要保证消息幂等。
7.6.Saga事务
将一个事务拆分成多个本地短事务,由Saga事务协调器协调,如果有步骤失败,则按照相反顺序调用补偿操作。但是没有保证隔离性还是可能造成补偿时发现无法实现,可以通过业务层面入手解决。
8.高并发
8.1.理解
- 不能只看数字,要看具体业务场景
- 使用高并发的处理方法,如架构设计、编码实现等,而不是一味升级硬件、加机器等
8.2.目标
8.2.1.宏观
- 高性能:体现了系统的并行处理能力
- 高可用:系统可以正常服务的时间
- 高扩展:是否能够在流量高峰时短时间内扩容
8.2.2.微观
- 性能指标 通常情况下,10000次请求,AVG控制在50ms以下,TP99控制在100ms以下,对高并发系统,TP99控制在200ms以内,TP999或TP9999控制在1s内。
- 平均响应时间 常用,但是对于慢请求不敏感
- TP90 TP99 响应事件按照从小到大排序,T90表示排在90分位的响应时间,分位值越大,对慢请求越敏感
- 吞吐量 和响应时间成反比
- 高可用指标 可用性=平均故障时间/系统总运行时间,用几个9描述可能性,对于高并发系统需要做到3个9或者4个9
- 可扩展性 扩展性=性能提升比例/机器增加比例,扩展能力需要维持在70%以上。但是对于高扩展性除了服务本身,还需要考虑服务集群、数据库、缓存和消息队列等。
8.3.实践方案
8.3.1.通用方案
- 纵向扩展 提高单机处理能力
- 提升单机硬件性能 增加内存、CPU核数存储容量等
- 提升单机软件性能 使用缓存减少IO次数,使用并发或者异步方式等
- 横向扩展 通过集群部署进一步提高并发处理能力
- 分层架构 使用分层简化复杂问题,更容易横向扩展,每层内部可以再选用对应技术,如反向代理层 LVS+Nginx
- 各层水平扩展 无状态水平扩展 有状态分片路由或主从同步读写分离等
8.3.2.具体方案
-
高性能
- 集群部署
- 多级缓存
- 限流
- 并发处理
- 预计算
- JVM优化
-
高可用
- 心跳检测主备切换
- 降级处理
- 限流处理
- 灰度发布
- 灾备演练
-
高扩展
- 合理分层架构 对微服务进行更细粒度的划分
- 存储层拆分 按照业务维度垂直拆分 按照数据特征维度进一步水平拆分
- 业务层拆分 按照业务维度拆 按照核心和非核心接口拆
9.单点问题
- 无状态服务的单点问题 进行平行扩展增加节点等
- 有状态服务的单点问题
- 可以去状态,去状态之后使用无状态服务单点问题
- 不可去状态,整体思路上使用主备方式,引入master节点和slave节点的概念,当master节点挂掉slave节点变成master,但是可能引起脑裂问题
- 引入zookeeper等第三方服务进行裁决
- 通过选举算法和租约方式进行master选举,如Raft和Paxos,业内有成熟框架,如微信的PhxPaxos,通过续租避免频繁切换master
10.RPC协议
远程过程调用协议,解决分布式系统服务间的通信问题,现有很多分布式框架都是基于RPC协议的,比如dubbo。
10.1.和HTTP的比较
RPC | HTTP | |
---|---|---|
协议 | 基于TCP协议或者HTTP协议 | HTTP协议 |
效率 | 可以使用二进制传输,效率更高 | 效率更低,带有无用信息 |
负载均衡等功能 | 自带负载均衡,注册中心等功能 | 自身没有 |
服务治理 | 能做到自动通知,不影响上有 | 需要事先通知,修改Nginx/HAProxy配置 |
场景 | 企业服务内部接口调用 | 多平台间调用 |
10.2.实现
实现RPC协议有很多方法,基于http client、java RMI等
- 客户端:通过调用本地服务的方式调用所需功能
- 服务端:提供服务
- 客户端stub:接收到请求后,将方法,参数等信息序列化,并找到远程服务的地址,基于通信协议发送给客户端(比如TCP长连接)
- 服务端stub:接收消息后,反序列化,让服务端程序执行,在得到结果后发送给客户端stub。
10.3.需要完成的基本功能
-
序列化协议(序列化后才能传输)
-
传输协议
-
服务注册发现中心(发现服务,寻址等操作,比如使用redis,zookeeper注册服务)
-
服务监控管理中心(监控服务的调用状态)
-
跨语言调用(不同服务可能是不同语言写的)