为什么MQ 比写DB快,因为MQ99% 都是顺序写,DB 99% 都是随机写。
异步架构中,网关层不用接入MQ是因为网关层是用来过滤掉大量的请求,如果写入mq 则会有大量的脏数据
nginx 不要做web 容器,运维负责 主要做反向代理 和 简单的限流 根据ip限流
微服务使用场景
需求层面
微服务是需求比较频繁,像ERP、OA系统需求就不是很频繁,更新频率低
性能层面
吞吐量会变高,每一层都可以横向扩展,
数据一致性层面
一致性 1、强一致性 如果你要保证强一致性,基本上你的吞吐量就没有了。 2、最终一致性,用分布式事务解决的是最终一致性的问题。
最终一致性有两种解决方案,一种是异步的最终一致性,一种是同步的最终一致性
微服务架构的目的
1、快速迭代
微服务架构就是为了快速迭代,提高开发效率,并行开发,以前上线需要很多人支持,现在上线是谁相关谁上线
2、项目持续交付
拆分业务层 和数据层的意义?
比如商品业务层需要用户信息,那么就需要用商品业务层直接掉用户数据层,而不是商品业务层调用用户数据层。这样就可以避免循环调用,比如用户又要调用商品信息。
再比如:商品业务层、交易业务层,它们都是业务层,商品层需要调用交易数据层,交易业务层又需要调用商品数据层,都是从上往下调用,而不是左右调用。
![](https://img.haomeiwen.com/i1510669/d5e6bfddef142655.png)
公共逻辑层一定不能反向调用。
Service Mesh 多了两次protobuf 序列化/反序列化,每个消息也发了两次。
互联网高可用设计
为什么需要高可用
硬件
生命周期:x86 3年
硬件故障
网络划分
软件
Bugs 软件总是有bug的
性能极限
软件之间相互影响
传统高可用评估方式
一段时间(比如一年)的停机时间占比
停机时间/总时间
2个9:一年停机时间不能超过88小时 365天24小时1%=88;
3个9:一年停机的时间不能超过9个小时
4个9 :一年停机的时间不能超高跟53分钟
5个9:一年停机的时间不能超过6分钟
1个9,90%
微服务高可用手段
1、服务冗余 无状态化
服务冗余:服务部署在不同的机柜或者机架上,用来防止单个机柜停电宕机而造成的不可用。
无状态化:不同的服务,关系是对等的,用户请求到任何服务都是可以的,这就是无状态化,无状态也不是这么简单,比如用户的session,如果存在网关层,这样就不是幂等的了,这样就会出现一会登陆,一会没登陆。如果每台网关服务器都缓存全量的Session,就不能说网关是无状态的。因为当有两台网关服务器,其中一台网关服务器挂了,这时候重启一个备份网关服务器,此时备份网关服务器就没有全量的Session,此时备份服务器跟另外一台服务器就是不对等的,这种情况它就不是无状态的。当然我们做无状态的目的:1、快速扩容;2、快速的缩容;
2、幂等设计 负载均衡
负载均衡:比如网关对应两台业务逻辑服务器,如果有一台服务器挂了,网关会剔除故障的服务器,将流量切换到正常的服务器。
3、异步化设计 超时机制
异步化的目的就是为了提升吞吐量,提高并发。什么样的场景适合异步化呢?对实时性要求不高,不是核心链路,不太关注结果,减少主流程
4、服务限流降级熔断 数据复制/缓存/Sharding
数据复制是数据层的高可用,缓存减少数据库压力, sharding :
5、架构拆分、服务治理
服务实时监控
请求平均耗时,黄线是昨天耗时,红线是今天耗时
请求异常数:黄线是昨天异常数,红线是今天的异常数
5秒打一个点,请求耗时都是打在日志里,通过flume 采集到kafka里面去,通过大数据spark 实时计算频率耗时
请求异常会报警
服务分级
如何降低或者避免服务出现故障
服务级别 | 定义标准 |
---|---|
一级服务 | 1、服务每天PV达到5000w以上 |
2、收入达到公司在线收入1/10 | |
3、核心系统 | |
4、后端基础服务至少为1个一级服务提供主服务 | |
二级服务 | 1、服务每天PV达到1000w以上 |
2、收入达到公司在线收入1/20 | |
3、重要商务系统 | |
4、后端基础服务至少为1个二级服务提供主服务 | |
5、公司内部核心信息系统 | |
三级服务 | 其他 |
服务级别 | 服务名称 |
---|---|
一级服务 | 列表系统,详情页系统,发布系统 |
招聘业务服务,计费系统,支付系统,订单系统 | |
IDC网络,DNS | |
二级服务 | 消息系统,个人中心,呼转服务, |
交友系统,CRM,OA,权限系统,BI系统 | |
办公网络,VPN | |
三级服务 | 其他 |
事故定级 | 影响程度低 | 影响程度中 | 影响程度高 |
---|---|---|---|
一级服务 | 严重 | 重大 | 特大 |
二级服务 | 一般 | 严重 | 重大 |
三级服务 | N/A | 一般 | 严重 |
影响程度定级 | 影响程度低 | 影响程度中 | 影响程度高 |
---|---|---|---|
对外完全停止服务时间 | 2-7分钟 | 7-30分钟 | 30分钟以上 |
系统对用户产生拒绝,占当日预期流量的比例 | 0.1%-1% | 1%-6% | 6%以上 |
系统返回结果不符合预期占当日总流量的比例 | 0.5%-2% | 3%-15% | 15%以上 |
受影响的用户占系统用户比例 | 3%-20% | 20%-40% | 40%以上 |
影响数据程度 | 部分数据丢失但能恢复 | 部分数据丢失不能恢复 | 所有数据丢失但不能恢复 |
收入损失,占日平均营收(线上加线下)比例 | 2%-5% | 5%-30% | >30% |
网络丢包率 | 5%-10% | 10%-30% | >30% |
高可用案例--如何无缝停止线上服务
网关层已具备热切换能力
热开关切换
假设 APP->网关->业务逻辑层->数据访问层->DB
假设网关已经具备热切换功能,假设20:00以后开始切,此时将开关打开,此时拒绝20:00以后的请求,已经请求的要等请求完成之后再关机。
什么时候20:00左右的请求处理完成?有两种方案:
1、查看各个层有无访问日志,如果没有请求日志了,然后重启
2、根据前端超时时间来设置,比如APP超时时间是5秒,20:00访问 20:00:05秒开始重启。其实就算你不返回给前端用户,前端用户也已经超时了,所以再等服务器处理完毕已经没有意义了。
关机顺序:先关网关 再关业务逻辑层 再关数据访问层。
长连接也是这么关闭的。
假设网关层不具备热切换能力
可以通过防火墙来实现,防火墙限制只出不进
IPTABLES
互联网高并发设计手段
吞吐量(Throughput)
响应延迟(Response Delay)
性能优化目标
缩短响应时间
提高并发数(增加吞吐量)
让系统处于合理状态
优化手段
1、空间换时间
系统时间是瓶颈
例如:缓存复用计算结果,降低时间开销,因为CPU时间较内存容量更加昂贵
2、时间换空间
数据大小是瓶颈
例如一:网络传输是瓶颈,使用系统时间换取传输的空间,使用HTTP的gzip压缩算法
例如二:App的请求分类接口,使用版本号判断哪些数据更新,只下载更新的数据。比如IM 即时通讯的用户列表,如果每次登陆就去拉一次用户列表,这样就会耗费大量的流量,用户列表其实更新没有那么频繁,可以在App端缓存一个版本号,与服务器版本号对比,如果版本号一样,就代表没有更新,就无需去拉取用户列表,当版本号发生改变,则拉取用户列表。
3、找到系统瓶颈
分析系统业务流程,找到关键路径并分解优化;
一个服务集群4W的QPS,调用量前5的接口贡献了3.5万的QPS
对关键路径的代码优化收益最大,当然系统剩下的部分也不能忽视,比如剩下5K QPS接口若性能也有问题,也可能把整个服务性能拖垮。比如这5千都是慢查询。
整个解决思路:调用了多少RPC接口;载入多少数据;使用什么算法;非核心流程能否异步化,没有数据依赖的逻辑能否并行执行
优化层次
优化层次:整体到细节,从全局角度到局部视角
架构设计层次
关注系统控制、数据流程
如何拆分系统,如何使各部分系统整体负载更加均衡,充分发挥硬件设施性能优势,减少系统内部开销等。
算法逻辑层次
关注算法选择是否高效,算法逻辑优化,空间时间优化任务并行处理,使用无锁数据结构
空间换时间
ThreadLocal 线程共享的遍历,频繁的生成对象还是挺慢的,将一些要共享的东西放到ThreadLocal里面去做。
时间换空间
采用压缩算法压缩数据,更复杂的逻辑减少数据传输
代码优化层次
关注代码细节优化,代码实现是否合理,是否创建了过多的对象,循环遍历是否高效,cache使用的是否合理,是否重用计算结果等。
代码优化层次
1、循环遍历是否合理高效,不要在循环里调用RPC接口、查询分布式缓存、执行SQL等
比如一个列表100条记录需要用户信息,就调用100次RPC来获取用户
解决方案:先调用批量接口组装好数据,再循环处理。
2、代码逻辑避免生成过多对象或无效对象
解决方案:输出Log时候log级别判断,避免new 无效对象
if(LOG.isDebugEnabled()){
LOG.debug("debug log");
}
LOG.isDebugEnabled()会生成一个对象。
正确写法应该是:
LOG.debug("debug log");
3、ArrayList 、HashMap 初始容量设置是否合理
扩容的代价
如果知道数据的大小,初始化的时候最好赋值一下,免得触发扩容问题,这样就会有性能影响
4、对数据对象是否合理重用,比如通过RPC查到的数据能复用则必须复用
通过RPC调用查询到的对象,能不能缓存到本地缓存中。
5、根据数据访问特性选择合适数据结构,比如读多些少,考虑CopyOnWriteArrayList(写时Copy副本)
6、拼接字符串的时候是使用String相加还是使用StringBuilder进行append(在StringBuilder的容量预分配的情况下,StringBuilder的性能比String 相加性能高15倍左右)
7、是否正确初始化数据。有些全局共享的数据,饿汉式模式,在用户访问之前先初始化好。
比如京东 淘宝上 有很多 商品类目 包括一级类目、二级类目等。
数据库代码优化层次
1、数据库减表语句尽量小的数据结构
表示状态的字段,如果状态值在255以内使用unsigned tinyint,IP 使用int 而非varchar
2、使用enum的场景使用tinyint替代,enum扩展需要改表
3、避免使用select * 查询数据,只查询需要的字段,避免浪费数据IO、内存、CPU、网络传输
4、分析查询场景建立合适的索引,分析字段的可选择性,索引长度,对长的varchar使用前缀索引。
5、字段尽量为Not NULLL类型,MySQL手册说明允许NULL的字段需要额外的存储空间去处理NULL(见注解),并且很难查询优化
6、目的为了降低服务器CPU使用率、IO流量、内存占用、网络消耗,降低响应时间
局部性原理
哪个执行速度快?
long[][] a=new long[10000][10000];
第一段代码 按行赋值
for(int i=0;i<a.length;i++){
for(int j=0;j<a[i].length;j++){
a[i][j]=j;
}
}
第二段代码 按列赋值
for(int i=0;i<a.length;i++){
for(int j=0;j<a[i].length;j++){
a[j][i]=j;
}
}
第一段代码耗时140ms ,第二段代码耗时2700ms
CPU Cache结构
速度越来越高:内存->L3->L2->L1多级缓存
本质上内存是一个大的一维数组,二维数组在内存中按行排列,先存放a[0]行,再存放a[1]行。
第一种遍历方式,是行遍历,先遍历完一行再遍历第二行,符合局部性原理,Cache Hit(缓存命中率高)
第二种遍历方式,是列遍历,遍历完第一列遍历第二列,由于下一列和上一列的数组元素在内存中并不是连续的,很可能导致Cache Miss(缓存未命中)
第二种遍历方式,是列遍历,遍历完第一列遍历第二列,由于下一列和上一列的数组元素在内存中并不是连续的,很可能导致Cache Miss(缓存未命中),cPU 需要去内存载入数据,速度较CPU L1 Cache 的速度低很多(主存100ns,L1 cache 0.5ns)
![](https://img.haomeiwen.com/i1510669/b49c6ecd81815fe8.png)
其中 i-cache(instruction cache)是指令高速缓冲存储器。 Cache存储体:存放由主存调入的指令与数据块.
dcache(data cache):数据高速缓冲存储器
代码优化层次
扩大到一般场景,业务系统使用缓存降低响应时间提高性能,必须提高缓存命中率
很聚焦的高频访问,时效性要求不高很适合缓存提升性能,很聚焦的高频访问业务比如banner,广告位,时效性要求不是特别高,比如更新了可以不用实时体现,很适合使用缓存提升性能
如果对数据实时性要求很高,比如严格的时效性,需要慎重考虑更新缓存带来的一致性问题
时效性和缓存的冲突,比如商品服务队商品进行了缓存,由于更新缓存和更新商品不是同一个事物,则对数据时效性要求高的如交易,就只能直接从数据库查询商品信息。
算法逻辑优化层次
1、用更高效的算法替换现有算法,而不是改变其接口
2、增量式算法,复用之前的计算结果,比如一个报表服务,要从全量数据中生成报表数据量很大,但是每次增量的数据较少,则可以考虑只计算增量数据和之前计算结果合并,这样处理的数据量就小很多。
3、并发和锁的优化,读多写少的业务场景下,基于CAS的LockFree比mutex性能更好
4、当系统时间是瓶颈,采取空间换时间逻辑算法,分配更多空间节省系统时间,缓存复用计算结果,降低时间开销,CPU时间比较内存容量更加昂贵
5、当系统空间容量是瓶颈,采用时间换空间算法策略
网络传输是瓶颈,使用系统时间换取空间的压缩,HTTP的gzip压缩算法
APP的请求分类接口,使用版本号判断哪些数据更新,只下载更新的数据,使用更多的代码逻辑处理更细粒度的数据
6、并行执行,比如一段逻辑调用了多个RPC接口,而这些接口之间并没有数据依赖,则可以考虑并行调用,降低响应时间
7、异步执行,分析业务流程中的主次流程,把次要流程拆分出来异步执行,更进一步拆分到单独的模块去执行,比如使用消息队列,彻底和核心流程解耦,提高核心流程的稳定性以及降低响应时间。
架构设计优化层次
分布式系统微服务化
分库分表、读写分离、数据分片
无状态设计,动态水平弹性扩展
调用链路梳理,热点数据尽量靠近用户
分布式Cache、多级多类型缓存
容量规划
提前拒绝,保证柔性可用
电商秒杀系统
大量并发,在某一时刻99%的用户涌入;
有效请求数很低,可以认为有效请求和数据库库存数一致,可能99%以上的流量都是无效的。
库存数据一致性要求严格,不能超卖。
秒杀系统架构思路
数据分层次校验,上层尽量把无效请求过滤掉
上层可以是不精确的过滤
层层限流,最后一层做数据一致性校验,扣减库存。
秒杀系统架构设计
1、HTML 、JS、CSS等静态文件存放CDN,缓存到用户端(APP/浏览器)
2、非实时动态数据(秒杀期间如商品标题、商品描述、图片URL列表、店铺信息、秒杀活动信息等),这些数据缓存在用户访问链路中靠近用户的位置,粗过滤一部分流量,比如用户是否有秒杀资格、秒杀是否已经结束等,这些数据实时性要求不高
3、实时数据如用户营销数据(如红包、折扣)、商品库存等再过滤一批用户
4、经过多层过滤最终落到数据库的流量已经很小,最终在数据库层面使用事务保证扣减库存准确性。
Feed系统分级缓存
1、读多写少、冷热数据明显,热点数据缓存到调用链路更靠近用户的地方。
2、L1缓存容量小负责抗最热点的数据,L2 缓存考虑目标是容量,缓存更大范围的数据,比如一般用户的timeline,高热点数据单独缓存,比如设置白名单,大V的用户数据单独缓存。
3、feed(关注的feed、topic的feed,一些运营的feed)前几页的访问比例,像前三页占了97%,针对这种业务特性,把前面几页数据作为热点数据提到L1 cache
Feed 系统消息发布
1、基于写扩散消息统一推送通道
2、推送策略:拆分数据并行推,活跃用户先推,非活跃用户慢慢推
有1w个用户关注,发了一个feed,拆分成100分,每份100个并行推
1万个用户里活跃的可能有2000个,活跃用户先推,非活跃用户慢慢推,保证活跃用户体验,非活跃用户推了很大概率也不看
3、消息标准格式
4、统一数据数据流,职责明确。
微博都是pull 拉的方式,假如有1亿粉丝,活跃用户只有10%,微信朋友圈可以用推push的方式,因为微信的好友有限制最多5000;
数据类型 | 特点 | 存储解决方案 | 存储产品 |
---|---|---|---|
微博内容 | 类型简单、海量访问 | 关系型数据库、Key-Value存储 id-content | MySQL、TiDB、Pika |
微博列表 | 结构化列表数据、多维度查询 | 关系型数据库 | MySQL、TiDB、Pika |
关系 | 类型简单、高速访问 | 持久化Key-Value存储 | Redis、Pika |
长微博(图片/短视频) | 对象数据(小文件等) | 对象存储 | Ceph图片服务器(小公司fstdfs就行) |
计数(关注数、粉丝数) | 结构简单、数据及访问量大 | 内存Key-Value 存储 | Redis |
网友评论