架构师接龙:金山张宴VS.淘宝岑文初
主持人:冯大辉,现任丁香园 (http://www.dxy.cn)网站CTO。曾历任支付宝架构师、数据库团队负责人等职。
张宴:在项目的架构设计中,对于未来可能发生的需求变更,你是如何考虑的?如何应对?
岑文初:需求变更可以分为业务性和非业务性两类。
对于业务性需求变更,思维方式应当按如下顺序进行:
第一,是否已经有类似功能,需要做些改进就可以满足需求;
第二,没有类似功能,是否可以抽取部分已有功能,再做部分封装即可实现;
第三,完全没有可以复用的内容,考虑一下后续可能的业务需求。
也许上面的内容比较虚,但业务一定是根据场景来做出实际判断,而这三点其实就是一个理念——不断优化业务代码,复用的思考会促进不断地合理化结构(因为大部分情况下,复用性越小的代码其结构本身存在耦合性过强的问题)。
非业务性需求变更主要是指由于系统自身应用场景发生变化(包括处理的数据量、业务规则复杂度等),而使得需要对现有系统做结构性调整。下面我举个例子。
开放平台的日志分析系统,在数据量不断增大和计算实时性要求增强的情况下,由单机多线程计算演变为多机分布式协作计算,从全量文件分析转
变为增量基于数据流分析。但整体结构却没有发生
太大变化,如图1所示。
最初,系统日访问量为六千万,数据分析报表每日出一次,分析器仅仅用于业务行为分析统计、单机每日拖取全量日志文件、多线程切割分析后合并。系统设计中有统计模型抽象层和简单的任务管理层:统计模型抽象层就是将传统的统计分析抽象成为对无结构化的数据做MapReduce计算,模型规则引擎可以根据配置直接分析无结构化定义的日志数据;任务管理层在第一阶段只是负责任务多线程内分配和管理。
当日志量每日达到两亿,分析器每日分析数据涵盖了系统、ISV、服务提供方,业务多角度分析和数据挖掘、单机I/O及CPU就成为瓶颈。因此,首先修改任务管理层,将原来多线程的任务管理和分配,扩展为支持多机任务管理和分配(基于TCP层的数据交互协议)。数据通信层理所当然地成为承载多机协作的基础层,因为本身没有太多的复杂流程在里面,就直接实现NIO的通信层。这次需求变更的代价就是扩展了任务分配协议以支持多机协作工作,本地的数据合并转变为通信传输后的本地数据合并,但对上层业务分析引擎没有任何影响。
当日志量每日达到八亿,分析器已经应用于整个系统的监控和告警及业务趋势分析等各方面,全量每日分析满足不了业务需求,于是由每日全量分析改变为实时增量分析。这次需要修改任务管理层:首先,扩展了Slave的数据源获取方式,除了支持文件获取,还支持HTTP数据流的获取;其次,扩展了Master对于任务周期的管理,任务列表可以被周期性重置,同时可以周期性导出增量数据,因此实时分析可以通过使用较短周期(分钟级别)的任务列表管理、任务分配、结果合并和增量导出,实现实时的大数据量分析。这次需求变更仅仅是对任务管理的周期做了更灵活的扩展,同时在Slave上做了多数据源数据的获取,对于本身的任务管理、合并、计算都没有任何影响。总之,对于非业务性的需求改变,需要能够将业务性设计和系统设计隔离开来,同时明晰系统设计边界,支持外部接口的可扩展,这样就能够支撑由于系统应用场景的变化带来的系统架构的变化。
张宴:在开放平台中,如何防范对于API应用接口的安全入侵、拒绝服务攻击?如何应对突发的访问请求激增情况?
岑文初:API的安全问题主要涉及两个方面:用户隐私安全和服务自身安全。
用户隐私安全,主要通过对应用的身份认证及用户主动授权两方面来保证。当然现在很多开放平台对于授权的时效性放得还比较松,这样会导致用户一次授权而被应用无限制使用的风险(用户往往找不到平台解除授权的地方)。淘宝开放平台,对应用做了级别划分和类型划分:首先在服务访问范围上,不同级别和类型的应用可访问的服务范围就被区别开来,因此高风险性的数据类服务只对Hosting的应用合作伙伴开放,不同类型的应用访问的服务也有所差别;其次,用户授权也根据应用类型的不同及服务本身的安全级别会有不同的授权时效性及授权提示,使得不同应用的操作用户数据会限制在不同时长内;最后,还提供了用户主动查询应用访问数据的情况及解绑授权的入口,为用户提供主动解除风险的机会。
服务自身安全,主要是应用自身主动和被动攻击(例如网站没有做数据缓存和防爬虫的功能,在爬虫爬取网页的同时,导致大量的无用服务请求)。当前开放平台主要通过两种模式多种维度来防止服务激增的问题。一种模式是主动在业务流程处理中按照简单规则计算并做访问流量控制,例如通过利用集中式和本地Cache作为计数器根据简单规则来屏蔽过量访问。另一种模式是通过记录日志,交由外部分析系统增量分析,外部分析器根据各种复杂关联性业务规则将分析后的决策推送给业务系统去做过量访问控制。这就涉及多种维度:IP、应用身份、用户身份、User Agent、访问的服务。这些维度可以简单地单个控制,也可以是组合控制。例如IP维度可以用在业务数据可能都是非法的情况下去屏蔽,由于规则很简单,可以直接放在主动模式下,而应用身份+用户身份比较复杂的控制,放在被动模式下,同时规则可以根据业务基线(不同日同时段、不同周同日的统计结果)的变化调整。从技术角度上来说,通过将业务线程池和容器线程池分开(类似于Servlet3容器线程生命周期不再固化与单一的方法),可以自己定义业务线程权重和处理流速来保证后端业务系统能力不会由于前端请求量激增时产生恶性循环。
张宴:在开放平台中,面对不同背景、习惯的用户和开发者,如何能够让他们使用起来更便捷、易用?
岑文初:在这方面,开放平台已经做和将要做以下事情。
第一,多语言SDK自动生成。淘宝当前有300多个API,而且服务还在不断增加,每次SDK的升级和发布都采用模板化方式自动生成,同时多语言的支持可以为开发者减小开发成本。这里就要谈到SDK的业务部分和非业务部分的抽象和隔离,更加有利于服务的增加和减少。
第二,服务数据化到服务流程化的转变。对于不同领域的开发者来说,淘宝的服务其实并不是都需要或者无差别的,例如淘江湖软件和商家管理软件就有很大差异,因此对于淘宝
这样行业性认知要求比较高的服务,使用成本降低需要通过将服务由专家来做一定的流程化定制,避免使用的高门槛。
第三,提供Demo和Doc直接附加在SDK中,让开发者比较直接地看到使用方式,并且简单地运行一些具体的实例,增加感性认识。
第四,提供Wiki和社区,为服务升级和变更提供足够的信息传播渠道。
第五,提供线上运行环境和控制台,用直观体验降低门槛。
张宴:在优化程序和降低系统资源消耗上,有哪些经验可以分享?
岑文初:第一,优化程序很大程度上是从全局而非局部去看,局部的优化可能导致全局的性能下降。例如,A系统和B系统是强依赖的,A的处理能力被提升后,到B的单位时间请求量会增加,而此时B服务能力达到瓶颈,单位事务处理时间就会增加,而A的处理能力增强并不一定会提高在A阶段的单位处理时间,因此整体事务处理时间增加,同时导致系统稳定性降低。因此优化需要关注更多的依赖,特别需要事先审视自己的系统是依赖流入还是流出。
第二,优化流程首先要切分流程的步骤,然后找到瓶颈点的步骤,而优化瓶颈点有两种方式,一种是直接优化减少瓶颈点资源消耗和处理时间;另一种是利用并行化方式,缩短资源生命周期,增加资源利用率从而减少对于资源的消耗,为瓶颈点的资源使用增加能力。
第三,合理利用外部计算资源,在可维护性和性能上找到平衡点。例如开放平台的结果返回处理通常情况下需要将对象根据规则定义转变为通用的格式。这部分工作可以交由开放平台集群来统一处理,也可以将规则处理后移到业务系统,前者的优势在于映射规则变动升级容易,而后者可以基于映射规则的成熟和无业务性,将计算工作分摊到各个业务集群上,因此计算外移是提高本系统性能的一种方式。
第四,可以将不同资源消耗型的应用部署在一起,合理利用资源(CPU消耗型、内存消耗型)。
第五,在做海量数据处理时,不要认为Java原生的东西都是无消耗的。尽量以最直接和简单的方式实现逻辑,用原生数据类型作为系统中间过程来处理内容(例如系统中就用long型做日期类处理)。
上面提到的也许看起来很抽象,但过于具体的细节优化其实遍及每个角落。优化最重要的就是找到全局的问题所在,同时在优化后是否会产生新的问题导致了性能反而不如优化前,以及系统稳定性也是优化所需要考虑的问题。
张宴:在高并发应用中,Cache的作用不可忽视,在Cache的使用上,有哪些问题需要去注意?
岑文初:第一,集中式缓存不是无代价的。从存储的数据量到交互次数上都需要去考虑如何降低成本。在海量并发请求的系统中,存储标识还是内容、多次交互还是一次交互,都会对系统产生很大影响。可以适当地使用多级缓存(remote + local),然后根据数据敏感度设置有效时间,简单处理数据失效问题。
第二,Cache不可用如何降级。对业务系统来说,一方面需要考虑Cache如何降级,也就是业务流程是否可以继续下去;另一方面如果Cache失效会从其他数据源获取数据,那么就需要考虑Cache的瞬间失效产生的峰值是否会直接击垮后端数据源。
第三,Cache如果采用数据源作为不命中的主动获取途径,那么需要防止无效的数据请求攻击透过Cache直接进入后端数据源。一般可以考虑用布隆算法来做增量白名单。
第四,注意使用好Cache提供的原子操作来避免并发带来的问题,例如add、replace、inc、dec等。
第五,需要去了解Cache的命中率和使用容量情况,不要为了技术而技术,需要更多的分析业务场景,最大限度地利用Cache的优势,同时减小存储消耗的代价。
张宴:开源产品,很多时候可以用来借鉴设计思路、进行二次开发、引用部分类库,并应用到业务产品中。你是如何看待开源产品和业务产品之间的关系?
岑文初:业务产品对于开源技术的选型需要慎重,同时切忌跟风。不必在意今天Facebook用了什么,明天Twitter用了什么,最重要的还是你自己的业务场景。如果可以用简单的技术实现,则优先考虑简单的技术借鉴部分开源设计的思想去做,同时量体裁衣简化系统设计。往往开源项目发展到一定规模会朝通用化方式发展,使用成本、附加功能、系统的松耦合,都会给中小型系统高速系统带来一定的负担。
使用开源项目最重要的是了解它解决的问题是什么、应用于什么场景、不同场景下的优势和劣势在什么地方、自身场景中使用的部分成熟度如何,最后做好足够的测试和验证,听来的总是虚的,因为业务场景不同,就算技术框架相同,结果也不同。
(本文来自《程序员》杂志11年02期,更多精彩内容敬请关注02期杂志)