主持人:冯大辉

冯大辉
杨海朝:在错综复杂的庞大系统中,如何避免多条线频繁地发布新代码对线上业务的影响? 孙立:在公司或者业务快速发展的过程中,应用系统的功能越来越丰富,系统模块也变得非常繁杂。由于前期没有进行良好的整体架构,后期又为了快速响应需求,所以系统模块、多个业务线之间的耦合性非常强,很可能代码也不太规范,导致出现发布了A系统的A模块的代码后,B系统的B模块出现了问题,而B系统的B模块开发人员花了一整天的时间才发现问题是由于A系统的某个发布版本导致的。所以可能出现在系统长期运行稳定、压力不大的情况下,突然宕机或者报错,经过大量时间的诊断,发现是由于另外一个系统发布了新版本,接口不兼容所导致。解决这些问题,不仅会浪费开发人员的大量时间,而且这些问题对线上业务的影响也是巨大的,如何减少甚至避免这些问题的发生呢?我认为在整个系统架构上应该有下面的考虑。
孙立
代码版本回滚。代码回滚功能可以快速地使出现问题的错误版本轻松恢复到上一个正确版本。最简单的方式就是通过svn来实现。如果系统在发布新版本后,出现了没有预估到的错误或者故障,那么就可以快速回滚到上一个发布版本。谁也不能保证新发布的版本万无一失,所以快速回滚功能是需要频繁发布的系统所必需的功能。 分层接口化和组件化。接口化和组件化可以促使系统模块之间更加抽象化,在更换接口的实现方式或者组件时,不更改调用者的代码,就可以使系统稳定地平滑过渡。2010年,手机凤凰网就通过这种方式,在不更改代码、不停机的情况下平滑地将底层存储从TTServer迁移到自行开发的NoSQL存储上。接口化和组件化还能更好地实现代码重用,开发人员可以把接口实现类和组件开发得更加稳定。因此,在系统内部,要尽可能地分层接口化和组件化。
杨海朝
服务化。服务化应该算是对接口化和组件化一个更高的抽象,是将系统需要的某些功能独立成单独的、稳定的可靠性服务,通过约定的协议进行交互。比如系统的用户登录可以独立成SSO服务,用户IP归属地功能、文件上传功能、图片处理也都可以分别独立成单独的服务。我们可以部署一套服务进行抽象化,供所有的系统使用。这样服务和系统的功能都简化和抽象了,也更加容易维护和开发新功能。可以想象,在系统发布新版本后,就不会再影响上传功能、图片处理功能、登录功能的正常运行了。 自动化测试。自动化测试有助于发现人工不好发现的Bug。开发人员在开发程序时,尽量多使用单元测试;在代码发布之前,进行自动化的单元测试检测,只有全部单元测试通过后才允许发布。 应用监控。通过上面的方法并不能100%地避免由于代码频繁发布带来的系统影响,但是我们需要尽可能快地知道带来的影响。所以我们需要对应用进行全面监控,这里的监控不同于系统运维人员使用的Cacti之类的对操作系统、数据库等底层的监 控,而是对系统的输入输出、系统服务接口的进行正确性、响应时间等的监控。 杨海朝:在设计一个大规模伸缩性服务时需要做好哪些准备?是否会考虑分区、故障、自动化、异步等方面的问题? 孙立:在设计一个大规模系统时,我们首先要考虑满足用户需求,如果不能满足,那么这个系统就是废品。其次是系统的可伸缩性,我认为可伸缩性大致可以从以下两个方面来看。 功能和需求的伸缩性。功能和需求的伸缩性是指随着业务的发展,可以在现有系统上方便地增加、删除一些功能,并能快速应付需求变更。系统要想实现功能和需求的伸缩性,架构师不但要深刻理解需求,还要深入到业务中去,要对业务有深刻的理解,然后结合系统设计,考虑各种业务需求可能的变化和发展。 性能的伸缩性。性能的伸缩性是指系统能应付不断的数据量增加和访问量增长。性能的伸缩性可以通过硬件的横向扩展(Scale-Out)或纵向扩展(Scale-Up)来完成。通过升级硬件带来的纵向扩展虽然易于实现,但能力有限,所以在设计大规模系统时,必须采用通过增加机器的横向扩展方案。 要实现横向扩展,最简单的方式就是让系统无状态。比如Web服务,如果不使用本地文件、Session等有状态的模式,即可快速地在负载均衡后端增加机器进行扩容。然而,最终的数据库往往是集中式的,所以还得根据应用场景来进行数据的分区、主从复制等来实现横向扩展。 要保证一个大规模伸缩性服务具有高可靠性、高性能、高可用性,还需要进行一些故障的自动处理(比如故障的自动切换),进行全面的有效监控。面对一些跨机房请求、网络连接处理、I/O密集型、CPU密集型等操作时,我们要考虑是否需要异步化、线程池化、使用事件驱动模型、多种语言的融合使用等。 杨海朝:对基于网络的分布式应用,网络传输是一个影响应用性能的重要因素。如何在架构中合适地使用缓存来节省网络传输带来的开销? 孙立:一个大规模可伸缩性应用,必然是一个基于网络的分布式应用,网络传输在其中扮演着重要的角色。按照网络传输的类型,大致可以分为同机房(局域网)和跨机房(广域网)两种网络传输。 同机房网络传输不但速度快,而且非常稳定。正因为如此,大部分开发人员不曾注意到同机房数据传输潜在的问题,即使是我们常用的千兆交换机、千兆网卡也是有千兆上限的,在高并发下,非常容易跑满上限,影响整体的性能。所以,在架构一个大规模的分布式应用时,需要适量考虑本地Cache、数据压缩传输等以节省内网网络传输量。比如Memcached服务器缓存了大量的大文本信息,那么最好使用压缩,这样不但可以节省网络传输,还能节省内存。 为了解决架构设计中不断出现的超过预估的数据量和访问量,需要做到以下方面: 第一,进行良好的可伸缩性设计; 第二,要对数据量和访问量进行监 控,并保持敏感性; 第三,控制恶意访问。 跨机房网络传输的速度和稳定性都得不到很好的保障,而且带宽还是需要付费的。在国内环境下,跨运营商的网络传输就更加不稳定。如果应用程序通过阻塞的方式进行跨机房请求,在网络不稳定时,对系统的冲击将会是灾难性的。解决跨机房网络传输的方案大致可分为客户端和服务器端两种模式。 客户端模式主要是通过浏览器的跨域处理能力把不同机房的数据组合在一个页面。一般通过iframe、JS回调等方式进行跨机房数据传输,常见的第三方统计系统就是使用的这种方案。客户端模式不但容易实现,而且避免了对服务器端产生额外压力,通过异步化处理,还能提高页面渲染的速度。不过还需要考虑到客户端模式安全性低、不能处理复杂业务逻辑、不支持WAP网站等问题。 服务器端模式处理跨机房网络传输要复杂很多,需要考虑数据一致性、避免阻塞式网络传输影响性能、带宽成本等。一个高并发应用,如果存在大量的阻塞式跨机房请求,不但占用大量的带宽成本,而且性能和访问速度也会急剧下降。根据用户在一定的时间段内上网地点固定不变的原理,那么用户始终都是访问的同一个机房。这样就可以针对用户来做Local Cache,缓解跨机房传输的成本。机房之间通过队列的方式进行异步和压缩传输,不但能极大地提高系统性能,而且对用户来说,几乎也感 觉不到延迟。 杨海朝:在架构设计中如何应对不断出现的超过预估的数据量和访问量? 孙立:系统在实际的运营过程中,虽然经过测试和调研分析了可能的数据量和访问量,但还是有可能因为突发事件、预估不准确而导致数据量和访问量大大超出预期,那么我们一般可以从以下几个方面来解决。 进行良好的可伸缩性设计。出现超过预期的数据量和访问量时,能够快速通过增加机器进行横向扩展,而无需改写和优化系统代码。 一个无状态的应用是最容易扩展的。Web服务器可以很容易地设计成无状态模型,从而可以方便地增加机器进行扩展。数据库和存储可以通过数据分区来进行扩展,数据分区其实也算是对状态的弱化。 要对数据量和访问量进行监控,并保持敏感性。监控能使我们更快地了解系统数据量和访问量增长的情况,甚至可以预测短期内可能的数据量和访问量增长,以便提前进行扩容准备。 控制恶意访问。对恶意攻击性访问,我们必须进行阻止,以防止影响正常的系统访问。比如一些不知名的爬虫恶意爬取、接口调用方由于Bug导致了大量的访问,所以在操作系统层和应用层都应该对恶意访问有所控制。 杨海朝:分布式的数据存储层如何结合应用特点选择合适的存储方式?比如MySQL、Redis、MongoDB等的应用场景。 孙立:选择存储方式首先要考虑满足应用的功能需求,其次要满足应用的伸缩性需求,最后需要考虑的是性能,结合具体的应用场景再进行权衡选择。 Web2.0时代具有大数据量、高并发等特点,所以我们往往会结合应用的特点,把多种数据存储结合起来使用,使它们发挥各自的优点。比如复杂点的关系查询考虑用MySQL,高并发的点击统计考虑用TTServer,列表类型的缓存考虑用Redis,字段不固定的文档存储考虑MongoDB。同一份数据还可以存入到多种存储,根据查询场景分别选择适合的存储查询。 杨海朝:分布式数据库中如何考虑一致性、分区策略和延时问题? 孙立:在系统架构中使用分布式数据库时,我们必须面对一致性、分区策略和延时问题的处理,也就是著名的CAP理论阐述的问题。对CAP的选择需要结合我们的应用场景来选择。 一致性问题。其实延时问题也是一致性问题。一致性可以分为强一致性、弱一致性和最终一致性,并不是所有的应用都需要强一致性。根据应用的特点,为了提高系统的整体性能,我们还可以考虑会话(Session)一致性。比如用户发表的微博、评论数据,用户自己可以马上看到,但是其他用户可能需要等一段时间。还有一些场景需要强一致性,比如支付问题。 MySQL数据库一般会使用Master-Salve复制模式进行扩展。在高压力访问情况下,MySQL数据库主从复制延迟可能变得非常高。我们可以从上面提过的会话一致性来解决复制延迟问题。另外一个方案就是提高复制速度。在MySQL主从复制过程中,从服务器解析出binlog日志后,向从数据库执行写入操作是单线程的。如果你可以修改源代码把MySQL复制变成多线程那就更好了,还可以通过增加从库实例,从库只复制部分表的方式来变相地进行并行复制。MySQL5.1以上版本提供的行复制模式 也可以加快复制速度。 分区策略问题。为了提高数据库的性能和可扩展性,我们往往会根据某些规则把数据进行拆分存储。分区策略应该遵循查询能在一个表完成的原则,尽量避免跨表查询。不管以Hash、Range或者其他方式都必须遵循这个原则。如果因为有多种关系查询,无法达到一次分区就满足上述原则,那么可以进行多次分区和冗余存储。 主持人介绍: 冯大辉,现任丁香园(http://www.dxy.cn)网站CTO。曾历任支付宝架构师、数据库团队负责人等职。 提问嘉宾介绍: 杨海朝,新浪首席DBA,负责整个公司的数据库管理工作。热衷于数据库设计、性能优化、分布式部署方案和高可用性方面的研究。在大规模高并发、海量访问特别是大规模数据库运维方面有丰富的管理和维护经验。 回答嘉宾介绍: 孙立,去哪儿网高级系统架构师,曾就职于凤凰网、酷6和搜狐。对分布式搜索引擎开发、大数据量网站系统架构优化、运维监控等有丰富的经验。开源项目phplock和phpbuffer的作者,近期开发了NoSQL数据库存储INetDB。 (本文选自《程序员》杂志11年04期,更多精彩内容敬请关注05期杂志) 《程序员》11年05期精彩内容:云计算应用之路! 《程序员》杂志订阅

Logo

20年前,《新程序员》创刊时,我们的心愿是全面关注程序员成长,中国将拥有新一代世界级的程序员。20年后的今天,我们有了新的使命:助力中国IT技术人成长,成就一亿技术人!

更多推荐