本文包括:高可用设计、无状态化与冗余设计、负载均衡设计、幂等设计、分布式锁设计
高可用针对的对象是服务与架构,高可用是什么?它表示任何人、任何时间、任何地点、任何方式、访问服务、返回正确结果
。
传统评估方式是用停机时间,如满足几个9的时间不停机,不合理,因为时间分为高峰期与低峰期,高可用的目的是产生最大效益,如果服务在高峰期不可用(如天!!!猫双11)可能停机时间不长,但会对企业造成严重的损失,所以不能用停机时间来评估高可用,那问题来了用什么?用流量
上面说了那么多都是针对服务的,那为什么架构要做到高可用呢?
因为如果高可用从架构方面得到解决,避免code body自己思考,写高可用,不然影响code body开发效率,不合适,另外高可用在每个系统都可以用,所以高可用这样的公用的东西抽出来,可以进行复用,成为高可用架构。
场景:14年M鹿M发了一条微博,然后微!!!博挂了!
分析下我们需要处理的问题:
5.1.鹿晗这个信息(大家好,给大家介绍一下,这是我女朋友@关晓彤),粉丝数量大,要将这条信息短时间内展示给粉丝,对系统压力很大
5.2.另外,评论、点赞、转发,这几个按钮短时间内点击率大。
首先第一个问题,微博是一个feed流系统,好友是单向好友,这条信息是推给粉丝,还是粉丝自己拉?推是tps,拉是qps,针对此场景肯定要采用拉的手段。但是拉影响及时性,所以针对粉丝量极其少的小散户为了让粉丝及时看到消息,则采用推的手段。(推特也是使用推拉结合的手段)
那么针对M鹿M这条信息,采用拉的手段,抗qps一般思路就两条,第一增加抗的能力,第二限流;下面我们层层加码,优化系统,达到高可用
针对上面的架构,最先扛不住的是哪里,是DB,因为DB最接近磁盘IO,越往下性能越差。除了db以外,每个微服务对外提供的也有瓶颈,据说,网易考拉大概每个微服务抗1500qps左右。
流量控制,流量只出不进;
加载热点数据进缓存、静态数据可能造成服务的短暂不一致;无伤大雅,影响不大,还是无状态化。做无状态化是为了快速扩容,弹性缩容,如原来10个用户服务、40个订单、50个商品,有一天做用户活动,这用户扩到30,订单缩到30,商品缩到40即可。
且现在都上云,如果不无状态化,怎么上云?
登录,网关存session,影响效率且无法扩容,我们扩容一般会数据分治,1-1000存gw1,1001-2000存gw2,有了状态。
解决:
session外置出去,存缓存,如redis
session存app,每次访问带着session,jwt(java web token)
mysql天生有状态化,我们将mysql的存储外置,外挂磁盘,外挂存储
负载均衡是服务治理的一部分
1.1负载均衡的技术有哪些:
所谓的四到七层负载均衡,就是在对后台的服务器进行负载均衡时,依据osi协议第四层(传输层)或第七层(应用层)的信息来决定怎么样转发流量。
所谓四层负载均衡,也就是主要通过报文中的ip+端口,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。
所谓七层负载均衡,也称为“内容交换”,除了ip+端口外还有应用层的信息,如url、浏览器、语言类别等,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。
目前主流的负载均衡是:lvs+nginx,nginx可能是多层,其中nginx大约10万并发,lvs大约10-50w并发
,有钱人直接用硬件如f5,2G的f5单机400万并发,是lvs的10倍。偏运维与网络规划
2.1广义负载均衡与狭义负载均衡的区别:
广义上的负载均衡除了负载以外还会进行故障的发现、摘机,重试与恢复自动发现等操作。
2.1.1故障自动发现:
故障自动发现可能有些注册中心支持,有的不支持。
微服务需要进项业务探活操作,如果使用心跳的话可能存在假死状态。
如何实现?写一个默认的简单查询,在配置中心进行配置,然后探活线程定时去注册中心拉取此服务所有的实例进行探活,偶尔单次异常不能直接判断其异常,如一分钟内连续探活失败才能判定其失活。
2.1.2故障服务的摘除:
服务摘机,注册中心是否支持自动摘机,如果不支持就加上自动摘机的功能;
保留事故现场,dump jvm以后,sleep 3/10/30秒,再dump一次(dump两次是为了对比);
优雅停机 kill -15(kill -9是操作系统强制关闭应用,kill -15是通知应用自行关闭,会释放资源后再关闭);
重启服务;
注:问题? 有人说除了注册中心以外,k8s不是也有自动恢复么,为什么还要在负载均衡这里做呢?因为k8s的自动恢复是根据pod的硬件资源来判断的,当服务假死时,资源使用正常,k8s是发现不了的,另外k8s也不会保留事故现场。
2.2广义负载均衡每一层都需要做:
nginx探活谁来做?可以用keepalive
业务逻辑层探活谁来做?注册中心,gateway来做
水平分层以后,请求链路很长,由于网络通信不可靠,请求可能延迟/丢失,所以会用请求重试来进行网络容错。
幂等就是无论请求多少次,产生的结果都是一样的。请求幂等与服务幂等的区别,请求幂等是一次请求,重试多少,结果一致;业务幂等多次请求,结果一致。
@单体时代处理幂等的方式:利用token来处理 [删除、查询天然幂等不必处理;对于新增与修改操作,用打开form表单时,获取一个唯一token放入redis中,并放入请求的header中,传给后端,后端收到请求首先去redis删除此token,如果删除成功判定为正常请求,如果删除失败,则提示不要重复提交],[新增,也可以预生成唯一键;非计算型修改天然幂等,避免计算型修改,在业务代码里处理]
@微服务时代的请求幂等:利用token就处理不了了【因为有请求间的retry】
我们来看一下SQL相关业务是否幂等?
a、查询,不会对数据产生任何变化,具备幂等性。
b、新增,
如唯一主键,即重复操作上面的业务,只会插入一条用户数据,具备幂等性。
如不是唯一主键,可以重复,那上面业务多次操作,数据都会新增多条,不具备幂等性。
c、修改,区分直接赋值和计算赋值。
1、直接赋值,update user set point = 20 where userid=1,不管执行多少次,point都一样,具备幂等性。
2、计算赋值,update user set point = point + 20 where userid=1,每次操作point数据都不一样,不具备幂等性。
d、删除,delete from user where userid=1,多次操作,结果一样,具备幂等性。
上面场景中,我们发现新增没有唯一主键约束的数据,和修改计算赋值型操作都不具备幂等性,因此需要单独处理此两种情况:
1.针对计算赋值的修改:避免sql计算,计算放在业务中处理;
2.针对新增:处理insert操作幂等,最简单的是前端提交表单时通过业务逻辑层进行id预生成,form表单带id提交;
5.1. 对于避免短时间内避免重复提交的问题,前端临时限制即可。
5.2. 对于用户一天内只能下一单的问题,可以使用分布式锁处理。
cap只可同时满足两者,但不代表要舍弃另一个,在CP\AP\CA中,对于分布式系统应取CP\AP,CA放弃了网络分区容错性,在分布式系统中是不可以的,网络不分区,那就是单体了。
C:即更新操作成功后,所有节点在同一时间的数据完全一致。
A:系统是否能在正常响应时间返回结果。
P:分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务。
案例1:如茅台一周一个人只能抢一次
锁的本质,是对共享资源的竞争,共享资源如用户、订单;
解决方案,共享资源互斥,共享资源串行化;
问题落地,锁;本地锁or分布式锁?本地锁只是单线程的锁,我们分布式系统,需要用分布式锁。