您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 公司方案 > 典型系统架构设计讨论
岑文初:模块化来降低耦合性时如何把握模块划分的粒度?如何权衡复用性与粒度过细导致依赖复杂的矛盾?杨海朝:耦合性是影响软件系统复杂程度和设计质量的重要因素,模块化设计的目标是建立模块间耦合度尽可能松散的系统,通过尽量使用数据耦合,少用控制耦合,限制公共耦合的范围和一定要避免使用内容耦合来降低接口的复杂性。在系统架构中模块化设计对于降低耦合性有非常重要的作用,相关的功能合在一起,不相关的功能分离开来。模块划分的粒度决定着最终划分结果的合理性和有效性。模块划分的粒度越细,越易于定义和开发,但随着模块数的增加,模块之间的接口也随之增加,使得各个模块在组装成系统消耗的时间增加,同时加大了整个系统测试的复杂度。模块划分的粒度越粗,越有利于整个系统的组装,整个系统的测试复杂度也降低,但整个系统的灵活性下降,难以满足用户对系统的多样性需求。模块划分的粒度是一个不太容易把握的问题,不同的产品和项目有不同的划分粒度方法。模块化的粒度需要根据模块的三大特征(相对独立性、互换性、通用性)来把握。每个模块的聚合度越高,耦合性就越低,反之亦然。在做模块划分时,应使模块之间尽可能独立,块内联系尽可能大,块间联系尽可能小,功能模块逐层分解和细化,直至形成若干容易编写的模块。同时还需要结合模块的功能、模块的规模、模块的出入口等诸多因素综合考虑。模块划分的粒度越小,通用范围广,可在较多应用进行复用,但其较小的粒度组装过程烦琐,会导致复用效率降低。反之,模块划分的粒度越大,组装的步骤越少,可复用效率越高,但其复用范围受到限制。岑文初:如何处理高并发系统中缓存失效的场景?杨海朝:从硬件到操作系统,从系统软件到应用软件,我们随处都可以看到缓存的身影。缓存在Web2.0网站中发挥着越来越重要的作用,特别是大流量的高并发系统。缓存在带来性能提升和支持高并发的同时,也带来另一个问题,如果缓存宕机导致所有的缓存失效,所有的流量都压到后端的服务上,例如数据库层。这时,后端服务因为压力过大无法提供服务或快速响应,缓存因为等待后端请求的响应而“热”不起来,最终导致“雪崩”问题。在高并发系统中,解决缓存失效有以下三个思路。使用ConsistentHashing算法。把数据缓存分片,减少缓存宕机对服务的影响范围。ConsistentHashing能最大限度地抑制Hash键的重新分布,同时要取得比较好的负载均衡的效果,需要在服务器数量比较少时,增加虚拟节点来保证服务器能均匀地分布在圆环上,最大限度地减小服务器增减节点时产生的缓存重新分布问题。缓存多份。通过应用写多份缓存,或应用写一份缓存由中间件把此份缓存复制多份,在缓存宕机时能切换到另一份缓存上,减少缓存宕机对后端数据存储的影响。这种方式最好采用缓存之间的复制,减少应用开发的复杂度。解决高并发系统中的缓存失效问题,需要结合业务逻辑进行综合考虑。实现缓存持久化或半持久化。所谓的持久化就是定期把缓存里面的数据刷到磁盘,保存起来,在缓存失效时能保证大部分数据仍然有效,例如使用Redis或MemcacheDB把一些数据存到磁盘。可能有人会说:“是否在刷磁盘那一刻会影响缓存的高效性?”这可以考虑通过高端硬件(例如FushionIO或SSD作为存储设备),来减少刷Cache过程中减少对服务的影响。这些仅仅是实现的三个思路,各有利弊。解决高并发系统中的缓存失效问题,需要结合业务逻辑进行综合考虑。岑文初:目前是如何做好应用的依赖监控的?例如依赖缓存,是否知道缓存的命中率情况、使用率等;依赖外部服务,是否知道外部服务的消耗时间、成功失败情况等。杨海朝:应用监控日益成为保障系统安全运行不可或缺的工具,在提高系统可用率和故障的预警能力、最大限度地缩短故障修复时间方面,日益显示出其重要程度。此外,对应用依赖的服务的监控也非常重要,目前可以建立一整套监控体系,这套监控体系不仅监控应用自身,同时也对外部服务进行监控。应用在调用依赖服务时,打印应用调用的详细日志,记录调用的返回时间,以及成功与否,同时要求外部或依赖服务提供一个统一的接口,用来返回每次调用消耗的时间以及失败率。通过对这些日志的实时分析来发送邮件和短信报警。岑文初:系统是否有对内或者对外提供服务,如何管理服务的使用权限,如何做服务升级?杨海朝:在系统中服务的使用权限至关重要,特别是既提供对内服务又提供对外服务的系统。目前管理服务的使用权限,设置白名单是最简单的方式。通过设置白名单,只有在白名单里面的用户和来源IP才能访问服务,其他的都无权访问。外部调用服务也通过使用appkey的方式,简单地说是API接口的钥匙,只有通过这个钥匙才能打开API的大门,从而获取数据,同时对appkey的申请设置一系列流程来保证其安全性。根据这些appkey来设置不同的权限等级,控制对目标数据的访问以及调用频率。通过使用Gearman系统进行“热”升级。在负载均衡设备的后端增加一个代理层,升级时通过对代理层的心跳探测页面修改,把落到这台机器上的流量切走,然后对这台机器进行升级,升级完之后进行测试,测试完成之后,再对某一区间机器进行升级,在某一个时间内测试没有问题,再对其他所有区间进行升级。提前准备好回退机制,保证每次升级对用户的影响最小,力求达到无缝升级。岑文初:对于不同系统之间的耦合如何处理?比如前端系统要求快速响应大量的用户请求,但是依赖于后端的服务体系,而服务系统的瓶颈可能在数据库读写上,如何协同这样需求差异化系统工作?杨海朝:不同系统之间的耦合性是指各个系统之间的互相依赖程度。需要熟悉各种耦合性的特性,为不同的系统之间选择合适的耦合性。如果系统之间采用紧耦合,那么涉及对象与对象的直接通信,这些对象通常比在松耦合系统中交互更为频繁。假如两个对象位于不同系统中并且由不同的网络分开,则会导致性能和延迟问题。如果系统之间采用松耦合以及基于消息的架构,客户端和服务端不需要知道对方如何实现。两端的消息协议符合协商,则客户端或服务器端的实现就可以根据需求进行变更,而不会互相影响。松耦合提供了紧耦合所不能提供的许多优点,并且它有利于降低客户端和服务端之间的依赖性。例如采用异步策略。对于需要快速响应的系统,尽可能把过程变成异步的,减少请求者所经历的响应延时。如果系统A同步调用系统B,那么A和B是紧密耦合,而紧耦合的系统是需要共同进退,两个系统需要承担相同的量,并且如果B不可用,则A也不可用。如果系统A和系统B之间是异步的方式,不管是通过消息队列、多播消息、批处理还是其他实现方式,那么系统A和系统B是相互独立的,如果B宕机,A仍然能够继续提供服务,也即所谓的优雅降级策略。这种异步策略能把高并发的写入高峰变成一个平缓的写入速率,减少了硬件的部署成本,同时可以控制消息队列的写入速度以及优化队列来避免后端服务出现压力过高问题。岑文初:对于系统优化,平时如何查找瓶颈,最后又如何确定优化后是有效的?可以的话请举例说明。杨海朝:系统上线,首先对系统进行必要的压力测试,量化能承担的最大量。通过对客户端性能指标和非客户端性能指标进行收集,对系统的响应时间、并发用户数、吞吐量、硬件资源的表现等这些关键点进行分析,最终找到系统的性能瓶颈。同时通过这些压力测试,确定系统在访问量不端增加时,可能出现瓶颈的组件。压力测试过程中输出详细的日志信息,然后对日志信息进行整理,分析各种组件的性能表现情况。对于已经在线的系统,要部署好各种状态监控系统,最好以图形化的方式呈现出来。在出现性能瓶颈时,能通过这些状态监控信息,找到性能瓶颈点或将性能瓶颈缩短在一定的范围之内,然后对线上系统按逻辑功能进行划分,例如静态池、动态池、缓存层、数据库服务。修改每个功能模块以便输出详细的日志信息,对这些日志信息进行整理和分析,找到瓶颈所在,同时记录各个功能块的性能表现,在优化后,收集相同信息进行对比来确定优化前后的性能差别。对于单个模块可以通过一些性能分析工具来对程序的性能进行分析,例如RationalQuantify。下面以数据库的瓶颈分析为例。收集一天内所有的SQL语句,以及统计每种类型的语句占的比例。收集当前系统中CPU、内存、硬盘I/O的表现情况,分析硬件资源瓶颈。收集满日志并对其进行分析整理。确定每条SQL语句在数据库中的平均执行时间。对执行效率低、消耗硬件资源、响应慢并且频繁调用的SQL语句进行优化。将收集优化后的硬件资源表现情况和之前进行对比,最终量化优化提高的比例。总之对于不同的系统,寻找性能瓶颈的步骤可能会有所不同,但都离不开对关键数据的收集和分析。主持人介绍:冯大辉,现任丁香园()网站CTO。曾历任支付宝架构师、数据库团队负责人等职。提问嘉宾介绍:岑文初,2006年加入阿里巴巴,2007年初开始负责阿里软件平台架构设计,2007年底开始进入开放平台领域,2009年8月加入淘宝开放平台,现在是淘宝开放平台主架构师。自认没有特长,只是学习能力比较强。回答嘉宾介绍:杨海朝,新浪首席DBA,负责整个公司的数据库管理工作。热衷于数据库设计、性能优化、分布式部署方案和高可用性方面的研究。在大规模高并发、海量访问特别是大规模数据库运维方面有丰富的管理和维护经验。五18架构师接龙杨海朝VS.孙立主持人:冯大辉杨海朝:在错综复杂的庞大系统中,如何避免多条线频繁地发布新代码对线上业务的影响?孙立:在公司或者业务快速发展的过程中,应用系统的功能越来越丰富,系统模块也变得非常繁杂。由于前期没有进行良好的整体架构,后期又为了快速响应需求,所以系统模块、多个业务线之间的耦合性非常强,很可能代码也不太规范,导致出现发布了A系统的A模块的代码后,B系统的B模块出现了问题,而B系统的B模块开发人员花了一整天的时间才发现问题是由于A系统的某个发布版本导致的。所以可能出现在系统长期运行稳定、压力不大的情况下,突然宕机或者报错,经过大量时间的诊断,发现是由于另外一个系统发布了新版本,接口不兼容所导致。解决这些问题,不仅会浪费开发人员的大量时间,而且这些问题对线上业务的影响也是巨大的,如何减少甚至避免这些问题的发生呢?我认为在整个系统架构上应该有下面的考虑。代码版本回滚。代码回滚功能可以快速地使出现问题的错误版本轻松恢复到上一个正确版本。最简单的方式就是通过svn来实现。如果系统在发布新版本后,出现了没有预估到的错误或者故障,那么就可以快速回滚到上一个发布版本。谁也不能保证新发布的版本万无一失,所以快速回滚功能是需要频繁发布的系统所必需的功能。分层接口化和组件化。接口化和组件化可以促使系统模块之间更加抽象化,在更换接口的实现方式或者组件时,不更改调用者的代码,就可以使系统稳定地平滑过渡。2010年,手机凤凰网就通过这种方式,在不更改代码、不停机的情况下平滑地将底层存储从TTServer迁移到自行开发的NoSQL存储上。接口化和组件化还能更好地实现代码重用,开发人员可以把接口实现类和组件开发得更加稳定。因此,在系统内部,要尽可能地分层接口化和组件化。服务化。服务化应该算是对接口化和组件化一个更高的抽象,是将系统需要的某些功能独立成单独的、稳定的可靠性服务,通过约定的协议进行交互。比如系统的用户登录可以独立成SSO服务,用户IP归属地功能、文件上传功能、图片处理也都可以分别独立成单独的服务。我们可以部署一套服务进行抽象化,供所有的系统使用。这样服务和系统的功能都简化和抽象了,也更加容易维护和开发新功能。可以想象,在系统发布新版本后,就不会再影响上传功能、图片处理功能、登录功能的正常运行了。自动化测试。自动化测试有助于发现人工不好发现的Bug。开发人员在开发程序时,尽量多使用单元测试;在代码发布之前,进行自动化的单元测试检测,只有全部单元测试通过后才允许发布。应用监控。通过上面的方法并不能100%地避免由于代码频繁发布带来的系统影响,但是我们需要尽可能快地知道带来的影响。所以我们需要对应用进行全面监控,这里的监控不同于系统运维人员使用的Cacti之类的对操作系统、数据库等底层的监控,而是对系统的输入输出、系统服务接口的进行正确性、响应时间等的监控。杨海朝
本文标题:典型系统架构设计讨论
链接地址:https://www.777doc.com/doc-3272491 .html