高可用高并发系统设计概念学习 一
前言
在我们的技术生涯中,总是不断的针对新的需求研发新的系统,而很多的系统设计都是可以互通的。在设计系统时,要因场景、时间而异,在资源有限的条件下,一定是先解决当下最核心的问题,预测并发现未来可能出现的问题,提前部署然后一步步迭代完善。一个好的设计要做到,解决现有需求和问题,把控实现和进度奉先,预测和规划未来,不要过度设计,从迭代中演进和完善。
在设计系统时,要多考虑墨菲定律.
1.任何事都没有表面上看起来那么简单
2.所有的事做起来都会比你预计的时间长
3.可能出错的事总会出错
4.如果你单休某种情况发生,那么它就更有可能发生。
在划分时,也要思考康威定律。
1.应该按照业务闭环进行系统拆分/组织架构划分,实现闭环、高内聚、低耦合、减少沟通成本。
2.如果沟通出现问题,那么就应该考虑进行系统和组织架构的调整。
3.在合适时机进行系统划分,不要一开始就把系统/服务拆得非常细,虽然闭环,但系每个人维护的系统多,维护成本高。
下面是学习的大纲,今天只学习了一部分。
一、高并发原则
高并发是指系统同一时间内能正常处理的数量。比如像在大学时期,抢选修课时,选修系统经常回卡死。在淘宝双11时,上亿人去购买刷单都能正常进行。这就是并发设计的区别。可以从下面几点入手设计与优化。1.无状态
服务端不保存任何客户端请求者信息,客户端的每次请求必须具备自述信息,通过这些信息识别客户端身份。这样应用比较容易进行水平扩展。实际的生产环境通常都是应用无状态,配置文件有状态。通俗讲代码都一样,配置文件不一样,部署新服务器,复制代码改配置文件即可。
2.拆分
拆分需要根据项目的实际情况来考虑。但是主要是分为系统维度,功能维度,读写维度,AOP维度,模块维度。
2.1 系统维度
按照系统功能/业务拆分。比如商品系统,购物车,客服系统,订单系统。
2.2 功能维度
对一个系统进行功能拆分。比如客服系统可以拆分为用户管理,角色管理,实时消息通讯等。
2.3 读写维度
根据读写比例特征进行拆分。比如一个商品系统中,商品的读的信息比写的信息大很多时,商品读写服务可以拆分为读服务与写服务,读服务还可以采用缓存提升性能;写的量太大,可以考虑分库分表。
2.4 AOP维度
AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向方面编程。其设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。所以根据访问特征,按照AOP进行拆分。比如商品详情页可以分为CDN(服务器缓存),页面渲染系统。CDN就是一个AOP系统。
2.5 模块维度
比如Spring MVC 分为三种架构(Web、S而vice、Dao)进行划分。
3.服务化
有的系统访问量太大,导致把整个服务拖垮。因此要考虑为不同的调用方提供不同的服务,隔离访问。所以不同的对外服务接口都可独立开,互不影响,当整个服务中某一个服务出问题了,并不会影响到其他服务运行。
4.消息队列
消息队列用来解耦一些不需要同步调用的服务或者订阅一些自己系统关心的变化,使用消息队列可以实现服务解耦(一对多消费)、异步处理、流量削峰/缓冲等。但是使用消息队列的时候,也要注意处理生产消息失败,丢失,以及消息重复接收时的场景。需要进行数据校对和补偿机制。
5.数据异构
5.1 数据异构
数据异构可以理解为对数据进行异步构建(同步)。比如一个用户订单详情,需要从多个表查询数据组成这个订单详情,每次都要进行多次连表查询很消耗性能与资源,那么可以通过mq机制接收数据变更,然后异步构建一个用户订单表到存储引擎中,如redis或持久化KV存储(Key-Value形式)中。
5.1 数据闭环
个人理解为数据基本一样,但是展示的要求不一样,同时数据的来源也很多,影响服务稳定性的因素就非常多。所以需要使用数据闭环来实时同步数据的一致性。比如在测试环境中做测试时,清除某个表数据时,另外一个关联数据没清除,不知情的人按正常流程在测试,突然发现数据不对,就会以为是BUG,并跟踪排查,就会浪费一定的时间和精力。那么数据闭环之后不管做了什么异常的操作,数据状态都会一起同步。
实现基本步骤:
数据异构:将数据同步到存储引擎中。
数据聚合:这一步是可选的,数据异构的目的是将数据从多个数据源聚合到一起,前端一个调用就可以拿到所有数据。此步骤一般存储到KV存储中。
前端展示:一次或多次调用拿到所需要的数据。
这种方式的好处就是数据的闭环,任务依赖系统出了问题,还是能正常工作,只是更新会有挤压,但不影响前端展示。
6.缓存
6.1 浏览器缓存
缓存一些对实时性要求不高的数据,比如广告词,评价,商家评分等。
6.2 APP客户端缓存
一般APP访问时,都会讲图片,视频等静态文件下载下来,下次打开时如未更新,直接使用本地的缓存,减少用户流量与服务器压力。
6.3 CDN缓存
当用户第一次访问服务器时,需要下载静态文件资源,那么设置CDN缓存,可以筛选这部分静态文件缓存中是否存在,存在即返回回去,不存在再请求服务器资源,可以减少一部分服务器压力。
6.4 应用层缓存
应用缓存就是使用堆内缓存/堆外缓存,但是堆内缓存最大的问题就是重启时,内存中的缓存会丢失。所以需要借助其他分布式缓存来完善。
6.5 分布式缓存
分布式缓存即可以存放到其他服务器上,或者集中放置一个缓存专用服务器中,其他服务器重启并不会影响到数据,同时也能保持所有服务器缓存数据的一致性。
二、高可用原则
1.降级
1.1 业务降级
当高并发流量来袭时,需要保证此时核心业务的正常进行,并且保障数据最终一致性。这样就可以把一些同步调用改成异步调用,优先处理高优先级数据或特殊特征的数据。比如促销活动时,要保障用户下单和支付,其他模块可以优先级调底。
1.2 可降级的多级读服务
比如服务调用降级为只读本地缓存、只读分布式缓存、只读默认降级数据(如库存状态默认有货)。
这些都是出现异常情况时,不影响用户使用,但是同时只能对不是核心的数据进行降级读服务。比如服务器重启时,只允许请求读取缓存中的资源。
1.3 开关集中化管理
统一管理开关,并通过推送机制,把降级的开关推送给各个应用。
2.限流
限流的目的是防止恶意请求流量、恶意攻击,或者防止流量超出系统峰值。
1.恶意请求流量只访问到cache。
2.对于穿透到后端应用的流量可以考虑使用nginx的limit模块处理。
3.对于恶意IP可以直接拼比。
3.切流量
当一个云服务器机房挂了的时候,或者某个关键服务器挂了,需要马上切换流量到可以用的机房或者服务器上,不会造成线上产品很久无法使用。一般有以下几个方式进行切换。
1.DNS:切换DNS解析,解析到另一个备份可用的机房或者服务器。
2.HttpDNS:主要在APP场景下,在客户端分配好流量入口,绕过运营商LocalDNS并实现更精准的流量调度。就是基于 HTTP 协议,自己实现自己的域名解析,自己做一个自己的地址簿,而不使用统一的地址簿。但是默认的域名解析都是走 DNS 的,因而使用 HTTPDNS 需要绕过默认的 DNS 路径,就不能使用默认的客户端。使用 HTTPDNS 的,往往是手机应用,需要在手机端嵌入支持 HTTPDNS 的客户端 SDK。
3.LVS/HaProxy:切换谷中的nginx接入层。(个人理解为类似于修改负载均衡的IP地址)。
4.Nginx:切换故障的应用层(个人理解为切换其他应用网站文件路径)。
4.可回滚
当程序或数据出错时,能够复原成上一个稳定的程序或数据。
三、高可用原则
1.防重设计
比如在用户下单时,需要放置重复扣减库存或者支付时重复支付。可以考虑使用防重key或者防重表。
2.幂等设计
幂等就是一个操作,不论执行多少次,产生的效果和返回的结果都是一样的。比如我们发起一笔付款请求,应该只扣用户账户一次钱,当遇到网络重发或系统bug重发,也应该只扣一次钱。
3.流程可定义
比如不同表单每个需要提交的字段都不一样,但是我们可以配置他需要提交什么字段,然后由不同的字段组成不同的表单。流程可定义就是不同流程动作组成不同的流程。
4.状态与状态机
以订单系统为例,状态就是待付款,待发货,已发货,完成等。状态机就是状态转移图。描述两个状态转换的关系。主要有四个概念现态,条件,动作,次态。比如现态是待付款,动作是用户付款,条件是用户已付款,次态是待发货,描述了订单待付款状态是通过用户付完款转变成待发货状态的。
5.系统操作可反馈
所有系统,都会存在现在的功能有哪些功能需要优化,缺少什么功能,在设计系统时,都希望看到这些反馈。所以要设计成可反馈。
6.文档和注释
在系统的设计与开发过程中,都需要有相应的文档描述,系统代码中都需有注释,否则后续换人维护或接受会很痛苦或不敢动。
7.备份
备份包括代码备份和人员备份。代码主要提交到仓库进行管理与备份。人员备份是指一个系统至少应该有两名开发人员是了解情况的。即使一名离职了也不会出现新人接受之后手忙脚乱事故频发的状况。
总结
今天在这里学习了系统设计的一些原则与注意事项,同时还学到了做事要灵活运用墨菲定律与康威定律,做事多想想,不要怎么简单怎么来,也不要太细,折中最好。
引用《亿级流量网站架构核心技术-跟开涛学搭建高可用高并发系统》书籍