「读」架构真经 | 互联网技术架构的设计原则
https://www.cnblogs.com/cx2016/p/12077393.html
架构的思想是非常宝贵的,设计的基本原理不会因为新技术的层出不穷而过时。怎样以最低成本最大化系统的扩展性?怎样达到风险利益的平衡点?答案尽在本书中。
第一章 大道至简
本章围绕着简化这个主题,从需求到设计、实施、部署再到网络设备。
需求方面的过度设计比较容易避免,控制好项目范围和需求范围就可以了。
想要避免设计方面的过度设计,最简单的方法是把设计的解决方案展示给其他技术团队,要求其他团队能够快速轻松地理解,如果任何一个团队不理解这个解决方案,就应该思考下,是否设计得过于复杂。所以归根结底,我们在设计解决方案时,首要考虑的是目前业务的增长速度和需求,只做当下最好的设计。
我想起自己一个实例,一个小需求——登录注册页底部加广告位。遗憾的是在兼容性测试中发现,Android 4.4 以下设备,底部广告位不能和底部的软键盘友好兼容,出现了广告位变形的情况。(且实践后发现,需要针对 Android 4.2 和 4.3 做不同的兼容),采取了一系列解决方案后,我意识到不应该为低版本用户做这种可能会影响性能的特殊监听,方案的复杂度也已经超出控制了。立马和产品协商,最简单干脆的方案是低版本用户不显示该广告位,问题至此解决。
那是不是简单的设计,就意味着不需要考虑系统未来的扩展性?当然不是!我们可以选择一开始就设计好扩展方案,等待需求规模的增长,再来实施这个扩展方案。虽然一开始就设计付出的智力成本比较大,但我们没有付出更大的技术和资产成本,智力成本处于可接受的成本范围。并且在现有的方案下,我们可以随时快速参考,随时快速实施扩展。
在实施过程中,推荐使用被广泛采用的开源或第三方解决方案,最简单的实施几乎总是那些有过实践经历并通过实践证明了的可扩展方案。想想如果采用开源库,成千上万的开发者一起使用,同时找bug,且有专人维护和更新。这些都是自己创建方案所不能做到的。当然,如果有商业库,最好还是采用商业方案,任何一个人都不可能是各方面的专家,商业供应商拥有该方案领域的专家(如
OCR 或者推送),我们通常能得到一个高质量的方案,这个方案不单单是高质量的编程,而且是该领域的高性能。
第二章 分而治之
本章围绕着扩展这个主题,提出了三个简单规则:x 轴扩展、y 轴拆分、z 轴拆分。
你可以注意到,x 轴是扩展,y 轴和 z 轴都属于拆分,进而达到扩展的目的。
成本最低的就是 x 轴扩展,主要手段是复制数据库和服务,来分散高频事务处理带来的负载,CDN 负载均衡就是这种规则的体现。
随着数据规模逐渐扩大,数据复制可能会出现瓶颈,这个时候,我们可以着手于扩展 y 轴,也就是把数据或服务按名词(以资源为导向,如:用户信息、产品信息)或动词(以服务为导向,如:注册、登陆、搜索)标识拆分。随着拆分,我们庞大的系统拆分成为子系统,团队也可以随之拆分,类似于目前公司的垂直化分组,每个小团队专门负责不同的子系统,更加专业的同时也提高了生产力。
当数据集的用户基数(此处以用户举例,实际上什么都可以进行拆分,思路一致,找到共同点与不同点即可)越来越大,且用户属性有明显不同时(如:地区、行业等),可以考虑 z 轴拆分。书中介绍到:可以根据用户属性分块,在应用发布时,可以通过先发布到含有少量客户的一小块来控制风险,没有问题后,再发布给其他大块或者全量。这就有点类似于 Android 灰度包的做法,只不过灰度包中,是用注册渠道(小米渠道、华为渠道)来标识用户块。我们可以把几个非常小的渠道合并为一个分块,减少数据块的碎片化。说到底,z 轴可以把数据分割成容易命中的多块数据,避免数据集过大,需要长时间遍历的弊端。
第三章 水平拆分
向外扩展(复制或拆分数据,分散负载),而不是向上扩展(购买更大的硬件来支撑)。
通俗讲,就是小、简单、多优于大、复杂、少。硬件再怎么大,都有个上限,采购成本也随之指数上升。如果是向外扩展,设备可以随时被替换或丢弃,扩展可以无限。
当我们做容灾时,通常会考虑双机备份。本文提出一个非常棒的设计方案——三活数据中心。如果想快速扩展现有的双数据中心,可以加个云数据中心来作为第三个数据中心,最小化了硬件支出。但是这个方案也不是全是优点,考虑下数据的同步频率,额外的连接(N中心就有(N*(N-1))/条双向连接),当数据中心不断扩展时,这个复杂度还是很可怕的。
三活数据中心
三活数据中心 + 三个热数据中心
最后,利用云技术来处理意外、临时、突发或偶发的需求。可以有效降低硬件成本,提高我们的响应速度,也减少了我们改变产品需求的风险成本。
第四章 先利其器
本章主要讲的是开发中工具使用的思考,包括:怎样选择数据库?哪种数据库更适合?数据存储是不是非数据库不可?防火墙的意义是什么?是否实施防火墙的决定因素有哪些?日志文件怎么有效发挥作用?
在了解这些开始前,我们需要避免陷入工具法则,技术解决方案可以尝试多方案,平时和不同技术栈的同事多交流,花时间做技术调研,学习新事物,主动分析、实验、采用新工具同时不断革新工具,使用最适合的工具,避免被自己只熟悉的东西困住。
日志文件这块,我想以客户端的角度详细讲讲。
使用日志的第一步,是要解决收集日志的问题。收集可以用 AOP 来插入日志调用语句,也可以结合业务逻辑手动来调用。那么什么时候上报?我们可以间隔一段时间,就上报,如果考虑到服务器的负载,也可以下次启动时统一上报。这种情况下,考虑下如果日志文件太大,上传过程中进程被杀,我们需要支持断点续传。上报的具体时机,按实时性的需求选择。
第二步上报之后,服务器需要把日志聚合到日志服务器上统一存储。服务端需要提供完整的日志分类、即时报表统计工具、监控支持和支持全文检索日志。
下一步是日志分类,是按用户维度?还是按日志等级维度?日志中的错误,可以按机型或者人数来统计,报错多的类似日志,我们优先排查解决。
每天产生这么多日志,维护和存储成本逐渐上升,最新的数据价值最高,我们考虑对旧数据进行归档或删除。
最后书中写道:引进每项新技术都需要另外一种技能来支持。尽管工作中使用合适的工具很重要,但是不要过度强调专业化,以至于没有足够深度的技能来支持。
共勉。
第五章 画龙点睛
本章围绕着系统扩展性主题,罗列出限制了系统扩展能力的错误设计。
首先是不要复查刚插入的数据——这种成本翻倍又难以维护的操作。
其次是滥用重定向,这会降低用户体验,影响页面搜索引擎排行。
最后,因为大多数关系型数据库不擅长保持节点之间数据的一致性,所以没有必要为了场景的一致性,影响了数据库的分布式扩展。合理地放宽时间约束,找到一个系统方便扩展,用户又容易接受的时间点。
不管做什么,首要考虑都是最小成本产生最大效益。我们允许某些不同步的小错误换取扩展性的最大化。
第六章 缓存为王
想提高扩展性,缓存是个很好的手段。从浏览器到网络、知道应用和数据库每个层次,缓存有无数选项可以考虑:
- 通过 CDN 缓存来平缓请求高峰和增长,提高服务器负载。
- Ajax 提供了丰富的异步动态交互,但需要注意频繁的废请求。
- 在网络服务器前面实施页面缓存。
- 根据数据读取的边界或者相关性,进行 y 轴 z 轴拆分,从而提高缓存的命中率。
- 推荐对 Sql 数据集建立对象缓存层,既不影响服务器性能,又方便独立地扩展缓存池。
- 推荐通过 HTTP 头来控制缓存,如果数据结果没有包含用户的隐私数据的话,指定 Cache-Control 头为 public有利于数据结果可以缓存在从客户端到服务器之间的任何代理及缓存。(如:浏览器、CDN、页面缓存、应用缓存)
第七章 前车之鉴
本章提出了一个有趣的观点:构建高可用性和高可扩展性的系统,目的就是防止频繁失败,因此可以学习的机会也比较少。经常失败的组织往往有更好的学习和成长机会,如果他们能抓住机会并从中学习。
引申开来,在敏捷软件开发实践中,每个 Sprint 结束后有回顾总结会,主要议题就是:在这个 Sprint 里,我们哪些做的比较好的,哪些是需要提高的,下个 Sprint 要采取哪些措施。这个回顾总结的本质就是复盘,目的是让团队从过去学习,来提升团队的整体交付能力。
最后本章还指出,需要确保所有版本的代码都有回滚能力,回滚的成本远远低于发布引起的线上故障,也能把风险降低到可控范围。客户端的话,效果类似于热修复,都是适用于线上紧急情况下实时高效把损失降到最低。
第八章 重中之重
本章深有体会的一点:实体间的关系影响了我们如何存储、检索、更新数据和扩展拆分数据。数据的完整性和规范程度越高,关系越紧密,就越难以扩展和拆分,我们需要折中考虑。
第九章 有备无患
本章重点在于提高系统的高可用性,系统的合理故障处理与隔离。
可用性和可扩展性具有同等重要性,可用性不高的系统不需要扩展,不能扩展的系统也不会是高可用性的。通过减少故障和频率和影响范围,我们可以提高系统的整体可用性。
减少故障的手段有以下几种:
- 用泳道隔离故障,各泳道之间不共享资源特别是数据库和服务器,并禁止不同泳道之间同步调用,避免故障阻塞,如果需要调用,推荐采用异步。异步的调用非常类似于观察者的模式,仅仅把事件传递出去,而观察者有没有收到,是否处理了,被观察者并不关心。我们可以沿 y 轴或 z 轴进行拆分,这有利于故障隔离,不同服务间彼此独立,有助于我们快速定位问题,缩小排查范围。
- 当使用单例模式时,就要慎重小心了。如果多个系统都需要共享这个单例,一旦单例失败,则会引起系统范围的故障。
- 并联而不是串联系统(除非有多版本子系统可以随时取代),避免累积影响。
- 增加启用/禁用框架,可以智能或人为干涉开启/关闭服务。这类似于开关的概念,在功能上加开关来做到一款上线/下线,防范风险。
第十章 超然物外
本章论证了引入状态会给我们系统带来多大的麻烦:耗费内存和处理能力、依赖增加、故障时状态无法恢复。
所以我们不惜一切代价避免状态。如果状态是必要的,建议把数据存储在用户端(如浏览器中的 Cookie 机制、客户端的 Token)因为存储在用户端,所以需要做好加密,防止中间人窃取凭证伪装用户。服务端虽然可以减少存储、检索成本,但保险起见,需要有校验的步骤,不可完全相信用户端传来的凭证。
如果有些设计必须要把状态数据存在服务端,就用分布式缓存来作为单层中间层处理。这里需要考虑下分布式中更新状态数据和读取状态数据的冲突问题,以及数据需不需要持久化。
第十一章 异步通信
本章讨论了异步通信的准则和处理。可以说在开发中特别是客户端开发中,由于主线程的特性,异步思想是无处不在。
异步一般可以借助回调或者事件总线(本质上就是观察者模式)来完成,如果嵌套层次太深,或者需要解耦,或者遇到跨服务的情况,就要考虑用事件总线了。
本章引发我对事件总线扩展性的一些思考(毕竟单用户的客户端只需要一条总线就足够了):
- 这么多的服务和系统,用同一条总线,高频的读写会引起堵塞,怎么扩展总线?最直接的方式就是按照面向的服务和属性拆分(y 轴),这会牺牲一些灵活性,你不能够跨服务去通知;按照客户边界拆分成泳道(z 轴),泳道之间也可异步通信。
- 总线是否可以扩展下优先级属性,在同时发生事件时,优先级高的事件优先通知。
- 不需要再处理的观察者,需要增加自动解绑机制。
第十二章 意犹未尽
我们怎么应对突发的流量?怎么提高系统高峰时的可用性?
有两方面可以解决:
- 利用云或者应急的容量,来负载突发请求,同时系统要做好这方面的扩展;
- 建立监控体系,存在异常波动,及时报警。
需要说明的是,虽然我们反复强调不要造轮子,优先使用被广泛采用的开源库或第三方解决方案,但是供应商永远都不会像你自己一样,在第一时间处理方案中的 bug 。所以这需要我们做好解耦,评估扩展性和风险点。第三方方案的缺陷我们是否能接受?是否在可控范围?自己的团队是否可以方便地扩展和修改以适应自己的需求?最差的情况下,第三方方案是否容易随时替换?这些都是我们在使用第三方前需要考虑的。
在扩展时,不要依赖供应商的产品、服务或系统功能来扩展,如果依赖于供应商的专有方案,我们将失去主动权和竞争力。就像携程没有开源的React Native优化框架,就是客户端的竞争力之一。
上面第 2 点的监控,架构设计之初就应该纳入考虑,监控系统的状态比如 CPU、内存使用率固然重要,更重要的是从业务指标做监控如注册成功率、下单转换率、搜索频率等,这样才更直观地看到业务的异常情况,具体受到什么的影响。文中提出监控系统一个有意思的展望:使用控制图或者机器学习,来预测是否会发生异常,并支持系统自我修复,这种自动化自愈系统,将是未来的方向。
最后,文章提出了对数据进行梯级存储策略,毕竟处于大数据时代,庞大的数据无疑增加了我们的成本。结合业务,评估数据的价值和访问频率,使用不同的存储介质和策略,比如低价值低频访问率的数据,是删除还是迁移到低速存储中?
我不知道你有没有留意过,跨年的快递单号已经查询不了物流信息了,因为已经签收了的数据,随着时间推移慢慢老化,失去价值,这部分数据完全可以删除掉。
第十三章 谋定而动
本章用风险收益分析方法,评估了全文提出的50条规则。
这个评估方法,也可以运用在我们平时的解决方案选定中:这个方案能降低多少风险,解决什么问题?风险出现的频率怎么样?降低的风险点,对系统和业务的影响面多少,损失会是多少?如果采用该方案,我们的成本怎么样?成本和风险比起来,我们能得到多少收益?这些都是我们需要思考的,毫无疑问优先采用降低风险多又低成本的方案。
可扩展性与可用性风险分解
总结
本书的思想和规则非常宝贵,大概总结了 12 条设计过程中需要遵循的架构原则。
- N+1设计。永远不少于两个,通常为三个,当故障发生时,至少保证有一个冗余的实例。
- 回滚设计。确保系统可以回滚到以前发布过的任何版本。
- 禁用设计。能够关闭任何发布的功能。
- 监控设计。在设计阶段就必须考虑监控,而不是在实施完成之后补充。
- 使用成熟的技术。只用确实好用的技术,谨慎使用最新技术。
- 异步设计。只有在绝对必要的时候才进行同步调用。
- 无状态系统。只有当业务确实需要的时候,才使用状态。
- 设计至少要有两个步骤的前瞻性。在扩张性问题发生前考虑好下一步的行动计划。
- 非核心则购买。如果不是你最擅长的,也提供不了差异化的竞争优势则直接购买。
- 小构建,小发布,快试错。全部研发要小构建,不断迭代,让系统不断地成长。
- 隔离故障。实现故障隔离设计,通过断路保护避免故障传播和交叉影响。
- 自动化。设计和构建自动化的过程。如果机器可以做,就不要依赖于人。
参与了读书计划,所以本书感悟按章分解。
《架构即未来》&《架构真经》读书笔记
写在前面
时节如流,转瞬间2018已经过去,之前太忙于奔跑,一直说的读书笔记整理,也搁置了4个多月。回顾这一年给我惊喜很多的书有很多,后面会专门整理下。计划从这两本架构相关的书开始,惊喜的其实并不是书中有什么高深的套路,而是一种思维的冲击,用DD大神的话总结就是
《架构即未来》&《架构真经》 其实就是道与术的较量,道看似浅显却很高深,术看似实用却受限于道。
架构之道
架构之道在于人
可扩展性的关键是人
- 人增加越多,每个成员的单位沟通和协调成本就越大。尤其个性解放的年代,每个小伙伴的性情各异,成长背景,工作经历都不尽相同,所以导致成员间的性格差别,做事风格迥异,要大家统一遵守一个规则,显得相对困难,所以耗费在沟通和协调的成本不断变大。
- 合适的人,在合适的时间,做合适的事,都是一种美好的设想。所以招人时候,偏向于招味道相近的人。这样的话期盼沟通成本,理解成本和默契程度更高,所以才会有些通用的评价人标准,聪明要性皮实等。
组织的重要性
人是系统扩展中最重要的因素,那么如何把人组织起来完成工作也就同样重要。
- 团队规模 ,过大过小都会出问题。
- 团队过小 成员会过度劳累,陷入加班循环 (比如少于6人)
- 团队过大 沟通不畅,上产效率低下,士气低落(比如多于15人)
规模过大团队生产率低下的原因:两个极端,因为高级工程师没有足够精力指导导致新人上手慢,反过来如果高级工程师花费大量时间指导新人,那么会导致老人效率低下。
- 团队分工,警惕情感型冲突
分工代表责任划分,一旦职责划分不清,就会出现情感型冲突,比如这个需求该谁做,这块问题该谁负责。而不是认知型冲突,比如这个问题该怎么做。
可扩展性和可用性失败的共同原因是责任不清。
- 团队结构
- 职能型组织:按照主要目的或职能设置部门
- 优势:职责划分清楚,容易分工,便于专注
- 缺点:跨部门沟通 出奇难
- 矩阵型组织:引入多种职能角色在团队中
- 优势:专人解决沟通问题,分而治之
- 缺点:个人精力分散,汇报多人,成员压力大
- 敏捷型组织:自我管制,自我组织。
- 优势:提升团队的创造力
- 缺点:闭环的成本高
- 职能型组织:按照主要目的或职能设置部门
最好的架构、需求和设计源于自组织的团队
- 团队管理
- 团队管理者 应该以人为本
- 以己为本 把员工当成向上爬的梯子,踩着他们往上走
- 以人为本 为所有一流员工建立好向上爬的梯子
- 团队建设
- 建设团队 (类比球队) 不断寻找负担的起的最好的人才
- 优化团队 (类比花园)
- 播种 增加新的,更好的人才
- 施肥 培养和发展要保留的人才
- 除草 尽早淘汰表现差的人,就能越快找到合适的替代者,让团队向前发展
- 团队管理者 应该以人为本
架构之术
大道至简
避免过度设计
- 如何界定过度:设计是否浅显易懂,是否让其他同学可以快速理解
- 表现为把一件事做的过于复杂和以复杂的方式去完成一个任务
在设计中要警惕复杂的解决方案
在这一块儿的感触还是比较深的,订单导出的早期跑的比较快,没有清楚自己的定位复用底层详情能力,而开始疯狂造轮子,为了获取一个字段而专门设计了一个client,然后再设计一个组件来专门做这两个数据聚合,由于都是并发取,发现新增的一个字段会导致老的导出出问题,用这种非常复杂的设计去获取一个字段。而换个角度,详情把字段能力补齐,而导出复用能力,会来的更加方便,维护起来架构也异常简单,过度设计的成本很高。
方案中带有扩展DID
设计方案时候,要想的远些,比如应对是现有流量的10倍流量时候,这套设计是否会出现重大问题,而真正部署的时候如果是无状态的可以动态扩展的最好,比如核心应用最少要预留五倍容量。
- Design 设计应对20-无限容量的挑战
- Implement 实施3-20倍容量
- Deploy 部署1.5-3倍容量
三次简化方案
- 简化方案范围 比如砍掉些不必要的功能,释放更多资源投入到更有价值的事情上
- 简化方案设计 易于理解、低成本、高效益和可扩展的方式来完成工作
- 简化方案实施 最简单的实施是可扩展的衡量标准
帕累托原则: 收益的80%来自于20%的工作
分而治之
AKF扩展立方体
这里重点介绍下AKF扩展立方体,受益匪浅。之前组内分享了下订单搜索冷热数据分离的分片维度及依据,这个订单搜索的AKF架构演进之路,后面会专门整理出来。
-
X轴: 关注水平的数据和服务克隆,比如主备集群,数据完全一样复制。克隆多个系统(加机器)负载均衡分配请求
- 优点:成本最低,实施简单
- 缺点:当个产品过大时,服务响应变慢
- 场景:发展初期,业务复杂度低,需要增加系统容量
-
Y轴: 关注应用中职责的划分,比如数据业务维度拆分。比如交易库,商品库,会员库拆分。
- 优点:故障隔离,提高响应时间,更聚焦
- 缺点:成本相对较高
- 场景:业务复杂,数据量大,代码耦合度高,团队规模大
-
Z轴: 关注服务和数据的优先级划分,数据用户维度拆分。比如上面按用户维度买卖家切分 数据分片
- 优点:降低故障风险,影响范围可控,可以带来更大的扩展性
- 缺点:成本最高
- 场景:用户指数级快速增长
水平扩展
- 向上扩展 购买更大的软件而实现的扩展
- 向外扩展 通过复制或拆分服务或数据库而分散事务负载
- 异地多活 灾备
配置 | 网络 | 服务器 | 数据库 | 存储 | 节点互连 | 总成本 |
---|---|---|---|---|---|---|
单数据中心 | 100% | 100% | 100% | 100% | 0 | 100% |
冷热双数据中心 | 200% | 200% | 200% | 200% | 1 | 200% |
双活双数据中心 | 200% | 200% | 200% | 200% | 1 | 200% |
三活三数据中心 | 150% | 150% | 150% | 200% | 3 | ~166% |
先利其器
适当使用数据库
- 关系型数据库
- 适用 当需要ACID属性来保证数据之间的关系和一致性时
- 优势 提供了高度的事务完整性,支持sql,可用于发杂查询
- 缺点 难以扩展 成本高 海量数据效率低
- 非关系型数据库
- 适用 不需要关联其他数据,也不需要事务完整性
- 优势 无需sql层解析,读写性能快,存储格式多样
- 缺点 无事务处理
马斯诺锤子:当你只有一个锤子时,任何东西看起来都像个钉子
前车之鉴
失败乃成功之母
抓住每个机会,尤其是失败的机会,学习经验并吸取教训,不断的从错误和成功中学习。
最好的经验来自于失败的错误,而不是成功。
之前订单搜索和订单详情都有两个经典设计失败案例,一直说要整理下,还没有整理出来,看到这段话时候很触动。两次设计失败都是为了追求通用性,复用性,所以将不同功能集于一身,成为一个万能接口,导致功能支持起来后期比较吃力。举个例子订单搜索支持通用搜索(非订单搜索),而早期入参是按订单搜索维度设计,所以导致通用个搜索入参特别不友好。早期订单详情支持实时+非实时一起,而发现实时的可返回字段远远少于非实时字段,实时非实时对应支持的扩展组件也不尽相同,不得不重构抽离。
有备无患
用“泳道”隔离故障
- 隔离的好处 不赘述
- 隔离的原则
- 泳道不共享
- 泳道之间不进行同步调用
- 异步调用设置超时和开关控制
- 限制泳道间异步调用
避免系统串联
这个比较常见的雪崩源头都是系统串联,其中一环发生问题,不断导致上游出问题,而上游显得很被动,串联组件受多重失败乘法效应的影响。减少以串联形式连接的组件数量。
启用和禁用功能
限流降级的重要性,最大程度的保证应用不被其他应用拖垮。核心依赖的跑不了。
超然物外
力求无状态
有状态会限制可扩展性,降低可用性并增加成本
异步通信
尽可能异步通信
尽可能优先考虑异步通信而不是同步通信,异步通信可以削峰解耦,极大程度的避免耦合。同时同步调用使得整个程序停下来等待响应,它捆绑的所有服务和层,进而导致连锁性延迟或故障。
思考数据价值
海量订单下的数据价值显得格外重要,一些无用的垃圾信息或者不重要信息占据昂贵的存储资源上,同时需要提供这种久远历史的查询,对整个系统的意义显得格外低下,之前超过6个月的物流详细信息进行了删除并引导到对应物流公司网址查,后来据说要保留下来以提供查询。还有就是6年前订单也要跟现有订单一起查,体会不到这个看似无比强大的能力背后带来的价值和整个系统架构为了实现该功能所付出的代价,也许站的角度不同,不被感知。
画蛇添足
避免写入后立即验证刚写入的数据
比较典型的场景是消息推送后反查,系统发送消息比如通知该订单已经支付,然后马上过来再查下确认下是否真的变成已支付。这种对实时性要求很高,只能引导去查实时数据库,造成很大浪费,理论上消息体中的信息就可以直接拿到,所见即所信,当然有的说消息推送的只是部分信息,需要来查全信息,那这种更倾向于推送的时候把信息退全,避免被动即时拉取。
意犹未尽
梯级存储策略
将存储成本与数据价值匹配,包括删除价值低于存储成本的数据,并非所有的数据对业务都有相似的价值,事实上,随着时间的推移,数据的价值经常下降。因此不应该用单一的存储解决方案以同样的成本存储所有的数据。
RFM价值 Recency 指问题中数据多久前被访问过 Frequency 数据多久被访问一次 Monetization 数据的业务价值
分类处理不同负载
通过分区和故障隔离,处理独特的工作负载,以最大限度的提高整体可用性、可扩展性和成本收益
完善监控
把必须监控应用作为一条架构原则,看看整体监控策略确保可以回答
- 有问题吗?
- 问题在哪里?
- 什么问题?
附上完整思维导图
《架构真经》读书笔记
大道至简
我学到了几点思路:
① 5个原则,其实说白了就是要方案简单。其中过渡设计有几个例子举得比较好,你可以用来喷人,比如你说PD,你这个方案太复杂,过渡设计,就像你设计支持100度的空调一样。
② DID原则,设计、实施、部署,层层递进,设计的时候考虑很多,可以随便脑暴,把未来规划好,但是实施(写代码)的时候,我们就要用最简单的方案,之后部署的时候,我们只要最低部署成本,随着业务量增加,则慢慢线性扩展。
这个原则很重要,我们在设计系统的时候,一定要面向未来去考虑。
③ 几个技术点,一是减少域名解析,二是减少页面(合并加载),同构网络(不要以为有了协议,就能为所欲为,有了协议,同时有不同实现的话,兼容性成为灾难),浏览器并发连接数
这章告诉我们设计架构,一定要尽可能的简单,下次再有人过渡设计,用这个原则去怼他们。
分而治之
这里我学到了一个,伸缩立方体。集成了一些水平扩展的技术思路。xyz轴,x轴即水平扩展,我理解其实就是scale out,一个负载均衡后面,可以直接扩展机器。或者读写分离,多加读机器;y轴,我理解是垂直拆分,就是soa或者微服务化,将一个业务拆分为多个子业务,直接通过RPC调用;z轴就比较高级了,可以理解为蚂蚁的用户水平拆分或者逻辑zone的概念,这种概念可以将用户(或者别的什么东西)按照某个原则分成多个。每个部分都是独立部署单元,每个部署单元,都是一个小型的蚂蚁金服。这样带来的好处是容灾能力大大增强
水平扩展
几个原则,一个是类似谷歌分布式系统的原则一样,用便宜廉价的大量小机器代替高性能昂贵的机器。其二是利用多数据中心,如果不能自建则用公有云或私有云(云主要用来所伸缩弹性计算)。这个一般公司涉及不到,类似阿里,蚂蚁等会有这些东西。美团应该也有。
多数据中心的问题是数据跨机房复制。
先利其器
本章给我的最大收获之一是,在进行数据架构设计的时候,一定要对你的数据结构,数据量,读写比,访问量,是否是强事务等做一个全面的评估,再去选择合适的存储系统,这样不但能够使你的系统后期扩展性更强,也能够减少成本。
所以对于架构师来讲,你要了解各种存储系统的成本差异、技术实现细节差异等等。诸如文件系统、rdb、nosql(表格存储、键值存储、文档存储)、newsql、图数据库、位图数据库、时序数据库,等等。你都需要了解。
画龙点睛
两个我觉得有用的原则,其一是永远不要写后读。这个一定要记得,尤其是在读写分离场景,很大概率你读到的是旧数据,还有一种场景是你可能很少遇到的,但也算是写后读。定时回调读+写。比如我注册一个定时回调器,这个回调代码中会查询数据x,然后做一些动作。但在回调代码执行之前执行了写x的操作,而后马上到达定时时间,此时就会发生写后读。
当然写后一定要读的场景,我们也可以通过其他方案解决,比如加入缓存,或者直接返回bean(insert之前的原始数据)等等,如果一定要校验(比如有监管问题),那么可以采用异步日志的方式,而不要阻塞用户线程。
第二个原则是放宽时间约束,其实我们最常见的场景就是要最终一致性方案,这个是舍弃实时,变为准实时,甚至是t+h或t+1等等。其实除了这个场景,我这里还有一个放宽约束的场景,比如到一个时间点给用户发某些东西(优惠、短信、push通知)等等,一种解决方案是采用状态+时间+读方案,但是这无法主动触发;另一个方案是定时器,但是定时器有一个问题,时间精度总不是那么精确,在加上系统稳定性等因素,总会有一定的延迟,此时怎么做呢?可以在产品规则上做一些调整,比如不要那么精确,例如:12:00你可以说12:00-12:30之前送达。这种也是放宽时间约束的一种办法。
缓存为王
不想多啰嗦,没什么新东西
前车之鉴
蚂蚁金服有三板斧,可回滚、可监控、可灰度。任何发布,任何变更必须可回滚、可监控、可灰度。这个是原则问题。
关于知识的深度和广度,作者有一个深度多问why,广度也要考虑人员、管理、流程。这两点比较有收获。深度可以通过多问why,刨根问底,自然深度就慢慢有了,都没有必要专门去读有深度的书(有可能你读不懂,浪费时间),针对具体事情,你把他深挖,就够你用的了。广度一般我们都以为各种新技术啥的,其实不全是,你还要看人员(比如产品运营等,你也要深入了解他们的思维和工作),管理(你要学习管理学,如何跟人打交道等),流程(如何优化现有流程,如何能更好的优化资源利用,如何能合理排期等)
关于复盘,有一个比较好的流程:时间轴,你先梳理问题发生的时间线索;明确问题,你多问why,找到哪里除了问题;确定方案,最后至少要给出一个方案,并且这个方案是符合SMART原则的。具体的、可度量、可实现的、现实的、有时间性的。
重中之重
主要讲db使用上的一些注意事项。比如,不要写存储过程,尽量不要连表,不要select * ,谨慎使用行锁,不要用两阶段提交等等。其实我觉得这章没啥意思,很多讲mysql的书都有这些经验,而且讲的更全面和细致。这里姑且算作者以自身经验作了一次汇总吧。
有备无患
本章主要讲可用性对可扩展性的影响。有几个观点,故障隔离泳道+开关+放单点故障(SPOF),其中SPOF这个英文缩写得记得,比较牛逼,拿出去显得很专业。另外泳道隔离其实无处不在。比如线程池中的每个线程是隔离的,每台服务器都是隔离的(彼此不影响),集群是隔离的,机房是隔离的,等等。其中故障隔离的几个原则挺不错的,可以用来说服别人:1,不要相互依赖,独立工作,2,减少异步调用。其实说白了就是尽量降低他们的相关系数,cov(x,y)=0,让其不相关,这样故障概率就可以做到p(x)*p(y),即最小化故障概率。
另外,书中用电路图类比架构,我觉得这个思想很有意思,很形象,以后你在跟别人讲架构的时候,可以用来借鉴。串联架构,并联架构等等。流量入口就像电池一样。
超然物外
主要讲无状态服务。举两个无状态服务的例子。一是map-reduce的处理,写map或者reduce方法,使用函数编程方式,要保证无状态,同时要无副作用等,相同参数一定有相同输出。其二是保存用户session的例子,如果session存在缓存服务器,则是有状态,你需要维护他们,如果放到cookie,服务端只去校验,session服务用于生成cookie,则就变成无状态服务。
如果一定要使用会话状态,则考虑使用分布式缓存,蚂蚁就是这么做的,通过分布式缓存tair,来存储session。
阿里P8架构师谈:分布式Session共享的4类技术方案,与优劣势比较什么是session服务器为每个用户创建一个会话,存储用户的相关信息,以便多次请求能够定位到同一个上下文。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。 当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。 Web开发中,web-server可以自动为同一个浏览器的访问用户自动创建session,提供数据存储功能。最常见的,会把用户的登录信息、用户信息存储在session中,以保持登录状态。 什么是session一致性问题?只要用户不重启浏览器,每次http短连接请求,理论上服务端都能定位到session,保持会话。 分布式session单服务器web应用中,session信息只需存在该服务器中,这是我们前几年最常接触的方式,但是近几年随着分布式系统的流行,单系统已经不能满足日益增长的百万级用户的需求,集群方式部署服务器已在很多公司运用起来,当高并发量的请求到达服务端的时候通过负载均衡的方式分发到集群中的某个服务器,这样就有可能导致同一个用户的多次请求被分发到集群的不同服务器上,就会出现取不到session数据的情况,于是session的共享就成了一个问题。 这个时候就需要解决Session一致性。 Session一致性解决方案1、session复制 session复制是早期的企业级的使用比较多的一种服务器集群session管理机制。 应用服务器开启web容器的session复制功能,在集群中的几台服务器之间同步session对象,使得每台服务器上都保存所有的session信息,这样任何一台宕机都不会导致session的数据丢失,服务器使用session时,直接从本地获取。 简介:将一台机器上的Session数据广播复制到集群中其余机器上 session复制使用场景:机器较少,网络流量较小 优点:实现简单、配置较少、当网络中有机器Down掉时不影响用户访问 缺点:广播式复制到其余机器有一定廷时,带来一定网络开销。 这种方式在应用集群达到数千台的时候,就会出现瓶颈,每台都需要备份session,出现内存不够用的情况。 2、session绑定 利用hash算法,比如nginx的ip_hash,使得同一个Ip的请求分发到同一台服务器上。 session绑定使用场景:机器数适中、对稳定性要求不是非常苛刻 优点:实现简单、配置方便、没有额外网络开销 缺点:网络中有机器Down掉时、用户Session会丢失、容易造成单点故障。 这种方式不符合对系统的高可用要求,因为一旦某台服务器宕机,那么该机器上的session也就不复存在了,用户请求切换到其他机器后么有session,无法完成业务处理。 3、利用cookie记录session session记录在客户端,每次请求服务器的时候,将session放在请求中发送给服务器,服务器处理完请求后再将修改后的session响应给客户端,这里的客户端就是cookie。 缺点
优点: cookie的简单易用,可用性高,支持应用服务器的线性伸缩,而大部分要记录的session信息比较小,因此事实上,许多网站或多或少的在使用cookie记录session。 4、session服务器 session服务器可以解决上面的所有的问题,利用独立部署的session服务器(集群)统一管理session,服务器每次读写session时,都访问session服务器。 这种解决方案事实上是应用服务器的状态分离,分为无状态的应用服务器和有状态的session服务器,然后针对这两种服务器的不同特性分别设计架构。 对于有状态的session服务器,一种比较简单的方法是利用分布式缓存(redis、memcached), 或者存储在数据库(如MySQL)等。在这些产品的基础上进行包装,使其符合session的存储和访问要求。 如果业务场景对session管理有比较高的要求,比如利用session服务基层单点登录(sso),用户服务器等功能,需要开发专门的session服务管理平台。 session服务器使用场景:集群中机器数多、网络环境复杂。 优点:可靠性好 缺点:实现复杂、稳定性依赖于缓存的稳定性、Session信息放入缓存时要有合理的策略写入。 |
|
阿里P8架构师谈:分布式缓存的应用场景、选型比较、问题和挑战高并发环境下,例如典型的淘宝双11秒杀,几分钟内上亿的用户涌入淘宝,这个时候如果访问不加拦截,让大量的读写请求涌向数据库,由于磁盘的处理速度与内存显然不在一个量级,服务器马上就要宕机。
为什么要使用分布式缓存高并发环境下,例如典型的淘宝双11秒杀,几分钟内上亿的用户涌入淘宝,这个时候如果访问不加拦截,让大量的读写请求涌向数据库,由于磁盘的处理速度与内存显然不在一个量级,服务器马上就要宕机。从减轻数据库的压力和提高系统响应速度两个角度来考虑,都会在数据库之前加一层缓存,访问压力越大的,在缓存之前就开始CDN拦截图片等访问请求。 并且由于最早的单台机器的内存资源以及承载能力有限,如果大量使用本地缓存,也会使相同的数据被不同的节点存储多份,对内存资源造成较大的浪费,因此,才催生出了分布式缓存。 分布式缓存应用场景
分布式缓存比较:Memcache VS Redis1、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset(orderd set 有序集合),hash等数据结构的存储。而memcache只支持简单数据类型,需要客户端自己处理复杂对象 2、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用(PS:持久化在rdb、aof)。 3、由于Memcache没有持久化机制,因此宕机所有缓存数据失效。Redis配置为持久化,宕机重启后,将自动加载宕机时刻的数据到缓存系统中。具有更好的灾备机制。 4、Memcache可以使用Magent在客户端进行一致性hash做分布式。Redis支持在服务器端做分布式(PS:Twemproxy/Codis/Redis-cluster多种分布式实现方式) 5、Memcached的简单限制就是键(key)和Value的限制。***键长为250个字符。可以接受的储存数据不能超过1MB(可修改配置文件变大),因为这是典型slab 的***值,不适合虚拟机使用。而Redis的Key长度支持到512k。 6、Redis使用的是单线程模型,保证了数据按顺序提交。Memcache需要使用cas保证数据一致性。CAS(Check and Set)是一个确保并发一致性的机制,属于“乐观锁”范畴;原理很简单:拿版本号,操作,对比版本号,如果一致就操作,不一致就放弃任何操作 cpu利用。由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比Memcached性能更 高。而在100k以上的数据中,Memcached性能要高于Redis 。 7、memcache内存管理:使用Slab Allocation。原理相当简单,预先分配一系列大小固定的组,然后根据数据大小选择最合适的块存储。避免了内存碎片。(缺点:不能变长,浪费了一定空间)memcached默认情况下下一个slab的***值为前一个的1.25倍。 8、redis内存管理: Redis通过定义一个数组来记录所有的内存分配情况, Redis采用的是包装的malloc/free,相较于Memcached的内存 管理方法来说,要简单很多。由于malloc 首先以链表的方式搜索已管理的内存中可用的空间分配,导致内存碎片比较多。 分布式缓存选型总结其实对于企业选型Memcache、Redis而言,更多还是应该看业务使用场景(因为Memcache、Redis两者都具有足够高的性能和稳定性)。假若业务场景需要用到持久化缓存功能、或者支持多种数据结构的缓存功能,那么Redis则是***选择。 (PS:Redis集群解决方式也优于Memcache,Memcache在客户端一致性hash的集群解决方案,Redis采用无中心的服务器端集群解决方案) 综上所述:为了让缓存系统能够支持更多的业务场景,选择Redis会更优。 分布式缓存的常见问题和挑战1.缓存雪崩 缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。 2.缓存穿透 缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存***率问题。 3.缓存预热 缓存预热这个应该是一个比较常见的概念,相信很多小伙伴都应该可以很容易的理解,缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据! 4.缓存更新 除了缓存服务器自带的缓存失效策略之外,我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种: (1)定时去清理过期的缓存; (2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。 两者各有优劣,***种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。 5.缓存降级 当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。 降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。 在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案: (1)一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级; (2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警; (3)错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的***阀值,此时可以根据情况自动降级或者人工降级; (4)严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。 |
|
分布式系统session一致性的问题session的概念什么是session?服务器为每个用户创建一个会话,存储用户的相关信息,以便多次请求能够定位到同一个上下文。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。 Web开发中,web-server可以自动为同一个浏览器的访问用户自动创建session,提供数据存储功能。最常见的,会把用户的登录信息、用户信息存储在session中,以保持登录状态。 什么是session一致性问题?只要用户不重启浏览器,每次http短连接请求,理论上服务端都能定位到session,保持会话。 分布式session单服务器web应用中,session信息只需存在该服务器中,这是我们前几年最常接触的方式,但是近几年随着分布式系统的流行,单系统已经不能满足日益增长的百万级用户的需求,集群方式部署服务器已在很多公司运用起来,当高并发量的请求到达服务端的时候通过负载均衡的方式分发到集群中的某个服务器,这样就有可能导致同一个用户的多次请求被分发到集群的不同服务器上,就会出现取不到session数据的情况,于是session的共享就成了一个问题。 如上图,假设用户包含登录信息的session都记录在第一台web-server上,反向代理如果将请求路由到另一台web-server上,可能就找不到相关信息,而导致用户需要重新登录。 Session一致性解决方案1.session复制(同步)
思路:多个web-server之间相互同步session,这样每个web-server之间都包含全部的session 优点:web-server支持的功能,应用程序不需要修改代码 不足:
2.客户端存储法思路:服务端存储所有用户的session,内存占用较大,可以将session存储到浏览器cookie中,每个端只要存储一个用户的数据了 优点:服务端不需要存储 缺点:
“端存储”的方案虽然不常用,但确实是一种思路。 3.反向代理hash一致性思路:web-server为了保证高可用,有多台冗余,反向代理层能不能做一些事情,让同一个用户的请求保证落在一台web-server上呢?
方案一:四层代理hash 反向代理层使用用户ip来做hash,以保证同一个ip的请求落在同一个web-server上 方案二:七层代理hash 反向代理使用http协议中的某些业务属性来做hash,例如sid,city_id,user_id等,能够更加灵活的实施hash策略,以保证同一个浏览器用户的请求落在同一个web-server上 优点:
不足:
session一般是有有效期的,所有不足中的两点,可以认为等同于部分session失效,一般问题不大。 对于四层hash还是七层hash,个人推荐前者:让专业的软件做专业的事情,反向代理就负责转发,尽量不要引入应用层业务属性,除非不得不这么做(例如,有时候多机房多活需要按照业务属性路由到不同机房的web-server)。 4.后端统一集中存储
思路:将session存储在web-server后端的存储层,数据库或者缓存 优点:
不足:增加了一次网络调用,并且需要修改应用代码 对于db存储还是cache,个人推荐后者:session读取的频率会很高,数据库压力会比较大。如果有session高可用需求,cache可以做高可用,但大部分情况下session可以丢失,一般也不需要考虑高可用。 总结保证session一致性的架构设计常见方法:
对于方案3和方案4,个人建议推荐后者:
来源:架构师之路 |
|
分布式Session的几种实现方式SpringBoot 分布式session
方案:
方案:负载均衡
方案:Redis存储用户生成的sessionId或者存储保存sessionId的cookie
Redis做缓存持久化存储sessionRedis存储cookie,里面保存用户生成的uuid(token作为sessionId)
使用Shiro集成的
|
异步通信
本章后半部分的内容过于理想化了,尤其最后的那个成本-价值四象限,其实维度单一,而且没有考虑人性(大部分人都是低估成本,高估价值)。所以没有参考性。
异步通信这块,是属于架构中最基本内容。同步转异步,增加性能,但是会带来复杂性。这个其实不用做多说明,你就看是否是强弱依赖。强依赖基本都要走同步,弱依赖基本都可以走异步。然后再考虑性能因素,削峰蓄洪,得用队列。
消息队列扩展这里,其实一般做法都是垂直扩展,分租户啊,分业务啊,2-8原则啊等等。按照topic划分。
意犹未尽
几点,一是防止被供应商遏制咽喉(这里一般是CTO决策层了)。二是分级存储+分级处理负载。这个比较有用,分级存储主要看你数据的价值+访问频次。有一个价值+成本曲线,这个值得学习,也是架构师成本收益思维的一个典型应用。其二是分级处理负载,这个比如阿里搞的离线在线混部等等。不同业务类型用不同的机器等。
完善监控,这个也是架构师必须知道的,甚至是常识。无论怎么样的系统,都一定要有监控,甚至监控系统也要有自己的监控。至于监控的维度,内容,思想,则都是经验积累了。
保持竞争力。。。这个就虚了点。因为竞争力是更成本有关系的。你要保持竞争力,必然要投入成本。所以不是想保持就保持的。也要有投入产出比。核心技术,一定要保持竞争力,成本投入到这里。非核心技术,作为公司影响力和技术形象的一种,类似公益活动。。
总结,不否定本书作者能力以及本书价值。对于国内大部分读者来讲,应该是读不懂的,因为毕竟这本书是站在一个更高的层次来写的,面向的都是CTO或者大公司总监,至少经历过公司基础设施从零到有的过程。有些思想很值得借鉴,比如AKF矩阵,关于深度广度的思考等等。有些则比较过时了,比如cookie大小问题(其实可以采用localStorage),比如现在早都已经进入移动互联网时代了,还在讨论web的相关技术等等,让我觉得,其实中国技术圈从实战到工程理论,已经不比硅谷那帮子工程师差多少了。想想每年的双十一,618等大促,我们的见识已经不在浅薄,工程技术能力,已经不在落后,甚至超过了美国的很多大公司。但中国的计算机基础研究,还是很薄弱,尤其是在对理论算法和行业标准制定上落后很多。工程能力强也只是一个方面的体现罢了。
架构真经:互联网技术架构的设计原则
主旨
这本书的英文名是scalability rules,但这里的scalability比狭义的可扩展性含义更广泛,不止是架构上,也涉及到工程、团队等方面的经验总结。
50条可扩展性规则
- 规则1 避免过度设计
产品的设计超出设计需求、完成的产品对于用户过度复杂、技术实现复杂到令他人难以理解都是过度设计的表现。复杂的系统实施成本高、维护困难,简单的系统容易扩展、可维护性强且成本低。
- 规则2 方案中包括扩展
在早期考虑到容量扩展的需求,但借助IaaS等服务可以在容量临近所需时再按需扩展。
- 规则3 三次简化方案
从方案的范围、设计和实施角度进行简化。
- 规则4 减少域名解析
对于性能敏感的网页,减少下载页面所需的域名解析次数。
- 规则5 减少页面目标
对于性能敏感的网页,减少网页上的对象数量和尽可能缩小对象大小。
- 规则6 采用同构网络
对于路由器和交换机,确保使用同一供应商的产品,可以减少可用性和可扩展性方面的问题。
- 规则7 X轴扩展
AKF立方体中的X轴扩展,即水平扩展。通过复制服务或者数据库以分散事务负载。研发成本低,适用于业务发展初期,业务复杂度低,需要增加系统容量的场景。
- 规则8 Y轴拆分
AKF立方体中的Y轴扩展,将服务和数据按照动词/名词定义的边界进行拆分。研发成本中,适用于业务逐渐复杂,数据量逐渐增大,代码耦合度变高,团队规模较大时的场景。
- 规则9 Z轴拆分
AKF立方体中的Z轴扩展,即分片。将数据或者服务按照用户属性进行拆分。研发成本高,适用于用户数量按指数级快速增长的场景。
- 规则10 向外扩展
向外扩展(Scale Out)而不是向上扩展(Scale Up),使技术资源投入的成本随着业务复杂度而线性增加。
- 规则11 用商品化系统
商品化系统是指成本低廉的小型机而不是规格巨大的大型机。单机规格越大,性价比就越低,投资成本越大。廉价的小型机意味着可随时被替换,并适合将来业务高速扩张时进行扩展。
- 规则12 托管方案扩展
使用多活数据中心来降低数据中心的总体成本、增强可用性和实现异地灾备。N(N≥3)个数据中心的解决方案与两数据中心相比总体成本更低。只要保证N-1个数据中心的可用性,就可保证数据和功能不受影响。
- 规则13 利用云
利用云的弹性和敏捷性应对临时需求,可实现在短时间内按需扩展。
- 规则14 适当使用数据库
考虑数据量、数据关系、一致性、灵活性、可扩展性等因素,在关系型数据库、NoSQL、文件系统之间进行取舍。
- 规则15 慎重使用防火墙
防火墙容易成为可扩展性的瓶颈,要慎重使用,对于不同类型的数据选用不同的安全等级和风险防范工具。
- 规则16 积极使用日志文件
充分利用日志文件帮助定位生产问题。通过Splunk或ELK等工具可以帮助对日志进行聚合和监控。
- 规则17 避免画蛇添足
避免对刚刚写入的数据进行读取验证,可以通过对写操作的返回码验证写入是否正确。
- 规则18 停止重定向
如有可能尽量避免重定向,因为其消耗计算资源、延迟用户交互过程、增加出错概率并对搜索引擎不利。如必须重定向,可使用服务器配置而非html页面或脚本代码。
- 规则19 放宽时间约束
因为关系型数据库的ACID属性,保持高可用和强一致是较为困难的,不妨放宽时间约束,使用最终一致性,允许短时间内的不一致。
- 规则20 利用CDN缓存
CDN是一组边缘节点或服务器,用于缓存用户的页面内容和数据,可以是静态文件或动态数据。CDN的实施也有成本,按流量收费,所以通常大中型客户才能利用CDN获得收益。
- 规则21 灵活管理缓存
使用http header中的cache-control、keep-alives等启用缓存和持续连接,提高性能。
- 规则22 利用Ajax缓存
通过http header控制Ajax接口也可以利用缓存。
- 规则23 利用页面缓存
服务器前部署页面缓存,并使用E-tag等http header。
ETag HTTP响应头是资源的特定版本的标识符。这可以让缓存更高效,并节省带宽,因为如果内容没有改变,Web服务器不需要发送完整的响应。 | |
REST笔记(五):你应该知道的HTTP头------ETag - tyb1222 - 博客园 |
- 规则24 利用应用缓存
分析如何拆分架构,并最大化应用缓存效果。
- 规则25 利用对象缓存
在数据库和应用层之间建立对象缓存。
- 规则26 独立对象缓存
在架构中使用单独的对象缓存层并挪到自己的服务器上。
- 规则27 失败乃成功之母
千万不要浪费失败的机会,抓紧每个机会学习,发现架构、人和过程中的问题。
- 规则28 不靠QA发现错误
系统质量无法测试,QA只能发现过去所犯的错误,应当通过内建质量来减少返工机会。
- 规则29 不能回滚注定失败
稳健的飞行员不会在飞机不能着陆时起飞,明智的工程师不会在代码不能紧急回滚时发布。
- 规则30 从事务处理中清除商务智能
例如把存储过程的逻辑从数据库移到应用中,让系统更容易测试和扩展。
- 规则31 注意昂贵的关系
设计数据模型时,考虑数据库分离和未来可能的数据扩展需求。
- 规则32 正确使用数据库锁
正确选择和使用数据库锁,以便最大化系统的并发性。
- 规则33 禁用分阶段提交
2PC之类的分阶段提交协议实际上是一个阻塞式协议,会阻塞其它事务完成,因此尽可能避免使用分阶段提交协议来延长单体数据库的寿命,不利于系统扩展。
- 规则34 慎用Select for Update
慎用行锁,可能减缓事务处理速度。
- 规则35 避免选择所有列
避免在select或insert语句中使用语意不清的通配符,因为会浪费传输资源、可能出问题或者影响回滚。
- 规则36 用“泳道”隔离故障
通过物理或者逻辑上分隔的“泳道”来隔离故障。
- 规则37 拒绝单点故障
在架构图上寻找单实例,尽可能将其配置成主动/主动模式。
- 规则38 避免系统串联
受多重失败乘法效应的影响,每增加一个串联组件就会使回路的可用性降低。应该尽量减少串联组件和增加并联组件。
- 规则39 启用与禁用功能
采用类似断路器这样的上线/下线框架,来关闭非关键的功能,以保证对用户重要关键的功能。
- 规则40 力求无状态
状态会限制系统的可扩展性,消耗资源和成本,降低系统单位时间内可服务用户的数量。在设计系统时反复拷问是否确实需要状态,力求无状态,如确有必要保持状态则参考规则41和规则42。
- 规则41 在浏览器中保存会话数据
建议将会话数据保存在用户cookie中,可以减少系统查询会话数据的负担并容易水平扩展。但要尽量控制cookie的大小,以避免降低页面加载速度。另外,可通过https传输防止cookie被劫持。
- 规则42 用分布式缓存处理状态
对于需要存储会话数据但又不能在浏览器上存储的场景,可以使用分布式缓存来存储会话数据,相对于数据库方案成本更低廉且容易扩展。
- 规则43 尽可能异步通信
同步通信的调用方需要等待,可能会导致系统连锁性故障,因此尽可能地使用异步通信。
- 规则44 扩展消息总线
消息总线本身也需要扩展,考虑按AKF立方体中的Y轴或Z轴扩展比较适合。
- 规则45 避免总线过度拥挤
消息总线上不需要发布一切消息,以价值和成本综合衡量发布何种消息。
- 规则46 警惕第三方方案
使用本书中所阐述的规则来扩展系统,不要依赖第三方方案,以便控制额外的复杂性和成本。
- 规则47 梯级存储策略
使用多样的存储方案,根据数据价值的不同,使用与之匹配的存储。
- 规则48 分类处理不同负载
将工作负载分为归纳、演绎、批处理、用户交互四种不同类型,各种类型均有不同的性能和可用性要求,需要放置在不同的故障隔离区。
- 规则49 完善监控
在系统中适当的埋点以记录事务的时间,把必须监控作为一条架构原则,以在故障发生时回答许多问题。
- 规则50 保持竞争力
对于系统中的每个组件都要足够了解并保持有竞争力的解决方案。
session的概念
什么是session?
服务器为每个用户创建一个会话,存储用户的相关信息,以便多次请求能够定位到同一个上下文。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。
Web开发中,web-server可以自动为同一个浏览器的访问用户自动创建session,提供数据存储功能。最常见的,会把用户的登录信息、用户信息存储在session中,以保持登录状态。
什么是session一致性问题?
只要用户不重启浏览器,每次http短连接请求,理论上服务端都能定位到session,保持会话。
分布式session
单服务器web应用中,session信息只需存在该服务器中,这是我们前几年最常接触的方式,但是近几年随着分布式系统的流行,单系统已经不能满足日益增长的百万级用户的需求,集群方式部署服务器已在很多公司运用起来,当高并发量的请求到达服务端的时候通过负载均衡的方式分发到集群中的某个服务器,这样就有可能导致同一个用户的多次请求被分发到集群的不同服务器上,就会出现取不到session数据的情况,于是session的共享就成了一个问题。
如上图,假设用户包含登录信息的session都记录在第一台web-server上,反向代理如果将请求路由到另一台web-server上,可能就找不到相关信息,而导致用户需要重新登录。
Session一致性解决方案
1.session复制(同步)
思路:多个web-server之间相互同步session,这样每个web-server之间都包含全部的session
优点:web-server支持的功能,应用程序不需要修改代码
不足:
-
session的同步需要数据传输,占内网带宽,有时延
-
所有web-server都包含所有session数据,数据量受内存限制,无法水平扩展
-
有更多web-server时要歇菜
2.客户端存储法
思路:服务端存储所有用户的session,内存占用较大,可以将session存储到浏览器cookie中,每个端只要存储一个用户的数据了
优点:服务端不需要存储
缺点:
-
每次http请求都携带session,占外网带宽
-
数据存储在端上,并在网络传输,存在泄漏、篡改、窃取等安全隐患
-
session存储的数据大小受cookie限制
“端存储”的方案虽然不常用,但确实是一种思路。
3.反向代理hash一致性
思路:web-server为了保证高可用,有多台冗余,反向代理层能不能做一些事情,让同一个用户的请求保证落在一台web-server上呢?
方案一:四层代理hash
反向代理层使用用户ip来做hash,以保证同一个ip的请求落在同一个web-server上
方案二:七层代理hash
反向代理使用http协议中的某些业务属性来做hash,例如sid,city_id,user_id等,能够更加灵活的实施hash策略,以保证同一个浏览器用户的请求落在同一个web-server上
优点:
-
只需要改nginx配置,不需要修改应用代码
-
负载均衡,只要hash属性是均匀的,多台web-server的负载是均衡的
-
可以支持web-server水平扩展(session同步法是不行的,受内存限制)
不足:
-
如果web-server重启,一部分session会丢失,产生业务影响,例如部分用户重新登录
-
如果web-server水平扩展,rehash后session重新分布,也会有一部分用户路由不到正确的session
session一般是有有效期的,所有不足中的两点,可以认为等同于部分session失效,一般问题不大。
对于四层hash还是七层hash,个人推荐前者:让专业的软件做专业的事情,反向代理就负责转发,尽量不要引入应用层业务属性,除非不得不这么做(例如,有时候多机房多活需要按照业务属性路由到不同机房的web-server)。
4.后端统一集中存储
思路:将session存储在web-server后端的存储层,数据库或者缓存
优点:
-
没有安全隐患
-
可以水平扩展,数据库/缓存水平切分即可
-
web-server重启或者扩容都不会有session丢失
不足:增加了一次网络调用,并且需要修改应用代码
对于db存储还是cache,个人推荐后者:session读取的频率会很高,数据库压力会比较大。如果有session高可用需求,cache可以做高可用,但大部分情况下session可以丢失,一般也不需要考虑高可用。
总结
保证session一致性的架构设计常见方法:
-
session同步法:多台web-server相互同步数据
-
客户端存储法:一个用户只存储自己的数据
-
反向代理hash一致性:四层hash和七层hash都可以做,保证一个用户的请求落在一台web-server上
-
后端统一存储:web-server重启和扩容,session也不会丢失
对于方案3和方案4,个人建议推荐后者:
-
web层、service层无状态是大规模分布式系统设计原则之一,session属于状态,不宜放在web层
-
让专业的软件做专业的事情,web-server存session?还是让cache去做这样的事情吧。
来源:架构师之路