02-架构复杂度来源
本文是通过学习李运华老师的《从0开始学架构》课程的随笔
现在自己对架构云里雾里的感觉,结合工作中的实践,学习与总结,慢慢的,会有质的提升的。
复杂度来源1:高性能
(复杂度来源1引自文中“公号-代码荣耀”的评论)
对高性能的理解
性能是软件的一个重要的质量属性。衡量软件性能包括了响应时间、TPS、服务器资源利用率等客观指标,也可以是用户主管感受(从程序员、业务用户、终端用户、客户等不同的视角,可能会有不同的结论)。
性能与伸缩性
性能更多的是衡量软件系统处理一个请求或执行一个任务需要耗费的时间长短;
伸缩性更加关注软件系统在不影响用户体验的前提下,能够随着请求数量或执行任务数量的增加(或减少)而相应地拥有相应地处理能力。
高性能
这应该是一个动态概念,与当前的技术发展状况与业务所处的阶段紧密相关。
高性能应该和业务所处的阶段来衡量,高到什么程度才能与当前或可预见的未来业务增长相匹配,一味的去追求绝对意义上的高性能,没有太大的实际意义,因为伴随着性能越来越高,相应地方法和系统复杂度也是越来越高,而这可能会与当前团队的人力、技术、资源等不相匹配。但是什么才是合适的高性能?可能需要从国内外同行业规模相当,比自己强的竞争者、终端用户使用反馈中获取答案并不断迭代发展。
体现
软件系统中的高性能带来的复杂度主要体现在两个方面。一方面是单台计算机内部为了高性能带来的复杂度;一个是多台计算机集群为了高性能带来的复杂度。
为什么需要高性能
追求良好的用户体验
满足业务增长的需要
如何做好高性能
1.垂直维度;垂直维度主要针对单台计算机,通过审计软硬件能力实现性能提升
增大内存减少I/O操作
更换为固态硬盘,提升I/O访问速度
使用RAID增加I/O吞吐能力
置换服务器获取更多的处理器或分配更多的虚拟核
升级网络接口或增加网络接口
2.水平维度;水平维度主要针对集群系统,利用合理的任务分配与任务分解实现性能的提升。
功能分解:基于功能将系统分解为更小的子系统。
多实例副本:同一组件重复部署到多台不同的服务器。
数据分割:在每台机器上都只部署一部分数据。
复杂度来源2:高可用
定义(维基百科):系统中无中断地执行其功能的能能力。
系统中的高可用方案五花八门,但万变不离其宗,本质上都是冗余实现高可用。
高性能增加机器的目的在于扩展处理性能;高可用增加机器的目的在于‘冗余’处理单元
计算高可用:无论在哪一台机器上进行计算,同样的算法和输入数据,产出的结果都是一样的。(复杂度体现如:一台机器变为多台机器,需要分配任务分配器,需要对连接进行控制,需要增加分配算法等。)
存储高可用:将数据从一台机器搬到另外一台机器,都需要经过线路进行传输,无论时间是多长,那么都会有那么一段时间,数据是不一致的。数据+逻辑=业务,所以总有那么一段时间,业务会不一致。存储高可用的难点不在于如何备份数据,而在于如何减少或者规避数据不一致对业务造成的影响。
高可用状态决策,计算高可用和存储高可用,基础都是状态决策。
1.独裁式:不会出现决策混乱;决策者故障,整个系统无法实现准确的状态决策;
2.协商式:两个独立的个体,通过交流信息,根据规则进行决策(主备决策是最常用的协商式决策)。
3.民主式:独立的个体通过投票的方式进行状态决策(多数获胜股则,投票者必须至少占总数一半)。
如何做到高可用(引自文中“公号-代码荣耀”的评论)
核心思想:网站高可用的主要技术手段是服务与数据的冗余备份与失效转移。同一服务组件部署在多台服务器上;数据存储在多台服务器上互相备份。通过上述技术手段,当任何一台服务器宕机或出现各种不可预期的问题时,就将相应的服务切换到其他可用的服务器上,不影响系统的整体可用性,也不会导致数据丢失。
从架构角度看可用性:当前网站系统多采用经典的分层模型,从上到下为:应用层、服务层与数据层。应用层主要实现业务逻辑处理;服务层提供可复用的服务;数据层负责数据读写;在部署架构上常采用应用和数据分离部署,应用会部署到不同服务器上,这些服务器被称为应用层的服务器;这些可复用的服务也会各自部署在不同服务器上,称为服务层的服务器;而各类数据库系统、文件柜等数据则部署在数据层的服务器。
硬件故障方面引起不可用的技术解决措施:(1)应用服务器。可通过负载均衡设备将多个应用服务器构建为集群对外提供服务(前提是这些服务需要设计为无状态,即应用服务器不保存业务的上下文信息,而仅根据每次请求提交的数据进行业务逻辑的操作响应),当均衡设备通过心跳检测手段检测到应用服务器不可用时,则将其从集群中移除,并将请求切换到其他可用的应用服务上。(2)服务层服务器。这些服务器被应用层通过分布式服务框架(如Dubbo)访问,分布式服务框架可在应用层客户端程序中实现软件负载均衡,并通过服务注册中心提供服务的服务器进行心跳检测,当发现有服务器不可用时,立即通知客户端程序修改服务列表,同时移除响应的服务器。(3)数据服务器。需要在数据写入时进行数据同步复制,将数据写入多台服务器上,实现数据冗余备份;当数据服务器宕机时,应用程序将访问切换到有备份数据的服务器上。
软件方面引起不可用的技术解决措施:通过软件开发过程进行质量保证。通过预发布验证、严格测试、灰度发布等手段,尽量减少上线服务的故障。
复杂度来源3:可扩展性
可扩展性:为了应对将来需求变化而提供的一种扩展能力,当有新的需求出现时,系统不需要或者仅需要少了修改就可以支持,无须整个系统重构或者重建。
面向对象思想、设计模式都是为了解决可扩展性而出现的方法与技术
设计具备良好可扩展性的系统,有两个基本条件:正确预测变化、完美封装变化
复杂性在于:不能每个设计点都考虑可扩展性。不能完全不考虑可扩展性,所有的预测都存在出错的可能性。
把握预测结果的准确性,是一个很复杂的事情,没有通用的标准,更多靠的是经验。
如何设计可扩展性好的架构?(引自文中“公号-代码荣耀”的评论)
面向对象思想、设计模式都是为了解决可扩展性的而出现的方法与技术。
设计具备良好可扩展性的系统,有两个思考角度:(1)从业务维度。对业务深入理解,对可预计的业务变化进行预测。(2)从技术维度。利用扩展性好的技术,实现对变化的封装。
(1)在业务维度。对业务深入理解,对业务的发展方向进行预判,也就是不能完全不考虑可扩展性;但是,变化无处不在,在业务看得远一点的同时,需要注意:警惕过度设计;不能每个设计点都考虑可扩展性;所有的预测都存在不正确的可能性。
(2)在技术维度。预测变化是一回事,采取什么方案来应对变化,又是另外一个复杂的事情。即使预测很准确,如果方案不合适,则系统扩展一样很麻烦。第一种应对变化的常见方案是将“变化”封装在一个“变化层”,将不变的部分封装在一个独立的“稳定层”。第二种常见的应对变化的方案是提炼出一个“抽象层”和一个“实现层”。
4.在实际工作场景中的解决方案
在实际软件系统架构设计中,常通过以下技术手段实现良好的可扩展性:(1)使用分布式服务(框架)构建可复用的业务平台。(2)使用分布式消息队列降低业务模块间的耦合性。
(1)分布式服务框架
利用分布式服务框架(如Dubbo)可以将业务逻辑实现和可复用组件服务分离开,通过接口降低子系统或模块间的耦合性。新增功能时,可以通过调用可复用的组件实现自身的业务逻辑,而对现有系统没有任何影响。可复用组件升级变更的时候,可以提供多版本服务对应用实现透明升级,对现有应用不会造成影响。
(2) 分布式消息队列
基于生产者-消费者编程模式,利用分布式消息队列(如RabbitMQ)将用户请求、业务请求作为消息发布者将事件构造成消息发布到消息队列,消息的订阅者作为消费者从消息队列中获取消息进行处理。通过这种方式将消息生产和消息处理分离开来,可以透明地增加新的消息生产者任务或者新的消息消费者任务。