第2篇 架构是什么
系统 泛指由一群有关联的个体组成,根据某种规则运作,能完成个别元件不能单独完成的工作的群体(能力)。
从逻辑的角度拆分系统后,得到的单元就是“模块”,从物理的角度拆分系统后,得到的单元就是“组件”。划分模块的主要目的是职责分离,划分组件的主要目的是单元复用。
框架是组件规范,提供基础功能的产品。软件架构指软件系统的“基础结构”,创造这些基础结构的准则,以及这些结构的描述。框架关注的是“规范”,架构关注的是“结构”。
软件架构指软件系统的顶层结构:
* 架构需要明确系统包含哪些“个体”,个体可以是 子系统、模块、组件等。
* 架构需要明确个体的运作和协作的规则。
* 顶层结构可以更好地区分系统和子系统。
自话:软件架构确定了系统中应该包含哪些个体,以及个体之间应该如何协作,以提供某种能力,从而实现系统的价值。
第2篇 架构设计的历史背景
“模块”“对象”“组件”本质上都是对达到一定规模的软件进行拆分,区别只是在于随着软件的复杂度不断增加,拆分的粒度越来越粗,拆分的层次越来越高。
第3篇 架构设计的目的
架构设计的主要目的是为了解决软件系统复杂度带来的问题。
架构设计首先要分析系统的复杂度所在,然后针对这些复杂度进行设计、制定方案。
第4篇 复杂度来源:高性能
软件系统中高性能带来的复杂度主要体现在两方面,一方面是单台计算机内部为了提高性能带来的复杂度;另一方面是多态计算机集群为了高性能带来的复杂度。
第5篇 复杂度来源:高可用
系统的高可用本质都是通过“冗余”来实现的,方法是增加机器。
高性能增加机器的目的在于“提升”处理性能,高可用增加机器的目的在于“冗余”处理单元。
存储高可用的难点不在于如何备份数据,而在于如何减少或者规避数据不一致对业务造成的影响。
高可用状态决策:无论是计算高可用还是存储高可用,其基础都是“状态决策”,即系统能够判断当前状态是正常还是异常,如果出现异常就要采取行动来保证高可用。
决策方式:
* 独裁式:所有冗余的个体向一个独立的决策主体上报状态信息,决策者进行决策。问题在于决策者本身是个单点。
* 协商式:两个独立的个体通过交流信息,然后根据规则进行决策,最常用的协商式决策就是主备决策。难点在于两者的信息交换出现问题时如何决策。
* 民主式:指多个独立的个体通过投票的方式来进行决策。缺陷是脑裂:原来统一的集群因为连接中断,造成两个独立分隔的子集群,每个子集群单独进行选举,选出两个决策者。解决方法是要求“投票节点数必须超过系统总结点数一半”。
第6篇 复杂度来源:可扩展性
可扩展性是指为了应对将来需求变化而提供的一种扩展能力。
设计具备良好可扩展性的系统,有两个基本条件:正确预测变化、完美封装变化。
预测变化的复杂性在于:不能每个设计点都考虑可扩展性、不能完全不考虑可扩展性、所有的预测都存在出错的可能性。
设计具备良好可扩展性的系统,有两个思考角度:从业务维度,对业务深入理解,对可预计的业务变化进行预测;从技术维度,利用扩展性好的技术,实现对变化的封装。
应对变化的常见方案一是将“变化”封装在一个“变化层”,将不变的部分封装在一个独立的“稳定层”。
方案二是提炼出一个“抽象层”和一个“实现层”,抽象是文档的,实现可根据具体业务需要定制开发,加入新的功能时,只需要增加新的实现,无需修改抽象层。
举例:设计一个支付网关对接不同的支付机构,为业务系统提供支付能力。每家支付机构的通信方式、请求/响应格式都是不一样的,但基本参数都是四要素信息(银行卡号、预留手机号、姓名、身份证号)、扣款金额等,扣款结果一般就是成功/失败/等通知,因此可以抽象出统一的接口,对接不同支付机构的具体实现类实现这个接口、完成具体的调用逻辑,当要对接新的支付机构时只需要添加一个实现类;业务系统只需访问这个统一的接口。
第7篇 复杂度来源:低成本、安全、规模
低成本是架构设计的一个约束条件,不是首要目标,本质上与高性能/高可用冲突。
从技术的角度,安全可以分为两类:功能上的安全,架构上的安全。
功能安全一般与具体的编码相关,是实现的问题。功能安全是一个逐步完善的过程,往往都是在问题出现之后才能针对性的提出解决方案,也无法预测下一个漏洞在哪里。
架构安全是防止暴力破坏,典型的就是防止DDOS攻击。
规模带来复杂度的主要原因是“量变引起质变”,当数量超过一定的阈值后,复杂度会发生质的变化。
常见的规模带来的复杂度有:功能越来越多,导致系统复杂度指数级上升;数据越来越多,系统复杂度发生质变;
第8篇 架构设计三原则
合适原则:合适优于业界领先。真正优秀的架构都是在企业当前人力、条件、业务等各种约束下设计出来的,能够合理地将资源整合在一起并发挥出最大功效,并且能够快速落地。
简单优于复杂。(复杂分结构复杂和逻辑复杂)
对于建筑,永恒是主题;对于软件,变化才是主题。软件架构需要根据业务发展不断变化。
第10篇 架构设计流程1:识别复杂度
深入研究业务需求、通过“排查法”分析复杂度所在。
第11篇 架构设计流程2:设计备选方案
成熟的架构师需要对已经存在的技术非常熟悉,对已经经过验证的架构模式烂熟于心,然后根据对业务的理解,挑选合适的架构模式进行组合,再对组合的方案进行修改和调整。
备选方案:数量3-5个最佳;差异要比较明显;技术不要只局限于已经熟悉的技术;不宜过于详细而忽略整体设计;
第12篇 架构设计流程3:评估和选择备选方案
列出需要关注的质量属性点,分别从这些质量熟悉的维度去评估每个方案,再综合挑选适合当时情况的最优方案。
常见的方案质量熟性有:性能、高可用、成本、复杂度、安全性、可扩展性、可伸缩性等。
第13篇 架构设计流程4:详细方案设计
详细方案设计就是将方案涉及的关键技术细节确定下来。
架构师不但要进行备选方案设计和选型,还需要对备选方案的关键细节有深入的理解。通过分步骤、分阶段、分系统等方式,尽量降低方案复杂度,方案本身的复杂度越高,某个细节推翻整个方案的可能性就越高,适当降低复杂性,可以减少这种风险。如果方案本身很复杂,采取设计团队的方式来进行设计,防止只有1-2个架构师可能出现思维盲点或经验盲区。;
第14/15篇 高性能数据库集群
读写分离
将读压力分散到集群中的多个节点,但没有分散存储压力。主从复制延迟和分配机制会带来复杂度。
解决主从延迟的常见方法:写操作后的读操作发送给数据库主服务器(和业务强绑定,侵入业务代码);读从机失败后再读一次主机(大量二次读会增加主机读压力);关键业务读写操作全部指向主机,非关键业务采用读写分离。
读写分离的实现一般有两种方式:程序代码封装和中间件封装。
程序代码封装指在代码中抽象一个数据访问层,实现读写操作分离和数据库服务器连接的管理。
中间件封装指的是独立一套系统出来,实现读写操作分离和数据库服务器连接的管理。
读写分离首先需要考虑业务的读写比例。
分库分表
既分散了读压力,又分散了存储压力。
分库带来的问题:join 操作问题,事务问题,成本问题。
水平分表相比垂直分表,引入的复杂性主要体现在:
* 路由:用于确定某条数据所在切分后的子表。常见的路由算法有 范围路由、hash路由、配置路由。
* join 操作
* count 操作
* order by 操作
第17篇 高性能缓存架构
缓存穿透
指缓存没有发挥作用,业务系统虽然去缓存查询数据,但缓存中没有数据,业务系统需要再次去存储系统查询数据。
缓存雪崩
指当缓存失效(过期)后引起系统性能急剧下降的情况。
常见的解决方法有两种:
* 更新锁机制:对缓存更新操作进行加锁保护,保证只有一个线程能够进行缓存更新,未能获取更新锁的线程要么等待锁释放后重新读取缓存,要么直接返回空值或者默认值。
* 后台更新机制:缓存本身的有效期设置为永久,后台线程定时更新缓存。
缓存热点:对于特别热点的数据,可以复制多份缓存副本,将请求分散到多个缓存服务器上,减轻缓存热点导致的单台服务器压力。
欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。