张宴经典

张宴VS.岑文初
张宴:在项目的架构设计中,对于将来可能发生的需求变动,你是如何考虑的?如何应对?
岑文初:需求变动能够分为业务性和非业务性两类。
对于业务性需求变动,思惟方式应当按以下顺序进行:
第一,是否已经有相似功能,须要作些改进就能够知足需求;
第二,没有相似功能,是否能够抽取部分已有功能,再作部分封装便可实现;
第三,彻底没有能够复用的内容,考虑一下后续可能的业务需求。
也许上面的内容比较虚,但业务必定是根据场景来作出实际判断,而这三点其实就是一个理念——不断优化业务代码,复用的思考会促进不断地合理化结构(由于大部分状况下,复用性越小的代码其结构自己存在耦合性过强的问题)。
非业务性需求变动主要是指因为系统自身应用场景发生变化(包括处理的数据量、业务规则复杂度等),而使得须要对现有系统作结构性调整。下面我举个例子。
开放平台的日志分析系统,在数据量不断增大和计算实时性要求加强的状况下,由单机多线程计算演变为多机分布式协做计算,从全量文件分析转变为增量基于数据流分析。但总体结构却没有发生太大变化。
最初,系统日访问量为六千万,数据分析报表每日出一次,分析器仅仅用于业务行为分析统计、单机每日拖取全量日志文件、多线程切割分析后合并。系统设计中有统计模型抽象层和简单的任务管理层:统计模型抽象层就是将传统的统计分析抽象成为对无结构化的数据作MapReduce计算,模型规则引擎能够根据配置直接分析无结构化定义的日志数据;任务管理层在第一阶段只是负责任务多线程内分配和管理。
当日志量每日达到两亿,分析器每日分析数据涵盖了系统、ISV、服务提供方,业务多角度分析和数据挖掘、单机I/O及CPU就成为瓶颈。所以,首先修改任务管理层,将原来多线程的任务管理和分配,扩展为支持多机任务管理和分配(基于TCP层的数据交互协议)。数据通讯层理所固然地成为承载多机协做的基础层,由于自己没有太多的复杂流程在里面,就直接实现NIO的通讯层。此次需求变动的代价就是扩展了任务分配协议以支持多机协做工做,本地的数据合并转变为通讯传输后的本地数据合并,但对上层业务分析引擎没有任何影响。
当日志量每日达到八亿,分析器已经应用于整个系统的监控和告警及业务趋势分析等各方面,全量每日分析知足不了业务需求,因而由每日全量分析改变为实时增量分析。此次须要修改任务管理层:首先,扩展了Slave的数据源获取方式,除了支持文件获取,还支持HTTP数据流的获取;其次,扩展了Master对于任务周期的管理,任务列表能够被周期性重置,同时能够周期性导出增量数据,所以实时分析能够经过使用较短周期(分钟级别)的任务列表管理、任务分配、结果合并和增量导出,实现实时的大数据量分析。此次需求变动仅仅是对任务管理的周期作了更灵活的扩展,同时在Slave上作了多数据源数据的获取,对于自己的任务管理、合并、计算都没有任何影响。总之,对于非业务性的需求改变,须要可以将业务性设计和系统设计隔离开来,同时明晰系统设计边界,支持外部接口的可扩展,这样就可以支撑因为系统应用场景的变化带来的系统架构的变化。
许式伟:做为系统架构师,您通常会从哪些方面来保证网站的高可用性(下降故障时间)?
张宴:不少因素都会致使网站发生故障,从而影响网站的高可用性,好比服务器硬件故障、软件系统故障、IDC机房故障、程序上线前测试未发现的Bug、遭受分布式攻击、突发访问人数剧增等。
一套良好的网站系统架构,应该尽量地避免只有一台服务器、一个数据库、一套软件节点等单点故障的存在。单点故障一旦发生,将直接致使网站服务不可用,恢复正常服务所需的时间也比较长,甚至还可能没法恢复。负载均衡集群、双节点热备、分布式处理等均可以用来解决单点故障,好比提供相同业务的Web服务器、MySQL数据库从库,均可以构建负载均衡集群。一旦集群中的一台服务器、一个服务出现故障,自动实时摘除,对用户来讲是不可感知的,不会影响到整个网站的访问,能够为运维工程师留下足够的时间去排查和解决故障。
对于重要的MySQL数据库主库,咱们习惯于从硬件层和软件层来实现热备,避免单点。越是复杂的设备,发生故障的几率越大。在磁盘没有损坏的状况下,应用程序致使服务器宕机的几率,远高于简单的磁盘阵列宕机的几率。因此,从硬件层解决的话,能够在两台服务器上安装相同的数据库版本、进行相同的配置,用SAS或SCSI线链接一台磁盘阵列,将数据库数据文件存放到盘阵上。正常状况下用服务器A挂载盘阵分区,启动MySQL,绑定虚拟IP;若是服务器A宕机,则用服务器B挂载盘阵分区,启动MySQL,接管虚拟IP。从软件层解决的话,则能够借助DRBD等软件作镜像。
IDC机房发生故障的几率较小,但若是发生的话,影响面也是最大的。若是全部服务器都托管在一个IDC机房,一旦该机房遭遇长时间流量攻击、断电、断网、地方政策性封网等,一般只能联系IDC去处理,除此以外一筹莫展,解决时间也比较长。若是成本容许,将网站服务器分布在两个以上的IDC机房,当某个IDC发生故障时,能够临时切换DNS域名解析来优先恢复服务。
虽然程序代码上线前,通过了测试人员的严格测试,但测试环境和生产环境毕竟有差别,因此一些会急剧影响性能、正常服务的Bug每每在程序上线以后,才会被发现,这就要求咱们在发现Bug后,可以迅速回滚到上一正常版本。咱们在SVN的基础上,开发了Web代码发布系统,会将每一个发布版本之间的文件变动记录下来,一键实现程序代码在多台Web服务器上的发布和回滚。
遭遇DDOS分布式拒绝服务攻击,使用防火墙来对付半链接、假IP,还算比较容易。而那种专挑复杂动态应用程序URL进行的分布式CC攻击,来源为真实IP、真实HTTP请求,具备模拟正规浏览器User-Agent、单个IP的每秒请求数不高、有成千上万个攻击源等特征,很难与正常访问区分开,比较难对付。可是,正常经过浏览器访问一个URL,会加载该URL中引入的JavaScript脚本、CSS样式、图片等文件。遇到CC攻击,须要及时分析日志,找出访问量异常上涨的URL,而后用事先写好的shell脚本找出哪些IP的请求只访问了该URL,而不加载该URL引入的文件,对这些IP进行自动封锁。
对于网游站点来讲,访问量受广告集中时间段投放、线上活动的影响较大,带宽峰值时间不固定,对于静态内容,可使用商业CDN,按实际使用量计费。对于动态内容,若是遇到突发访问人数剧增,超过现有服务器处理能力,最简单的临时处理办法就是增长服务器。上架新服务器须要时间,可是,同一个IDC机房内,能够借助其余业务的服务器,在不一样端口开启一组新进程,加入到原有负载均衡池中。另外,能够临时关闭一些Web中的次要功能,来减小服务器消耗。
许式伟:您在任务切分上,有什么经验分享?您经过哪些手段保证任务的独立性?
张宴:相信不少人都遇到过这种状况:在一个老项目上修改、增长一些新功能所花费的时间,不比从新来作一个包含全部功能的新项目时间用得少。一个须要长期维护的项目,不可避免地会面临老员工的离职、新员工的接手,不少时候,项目代码的可维护性将决定一个项目的生存周期。让一个新员工在规定开发时间的压力下,去面对一个文档不够详细、陌生的、功能复杂的庞大项目,短期弄明白全部功能逻辑不是一件容易的事。因此,任务须要切分,将一个大的任务切分红一个个小模块以后,各模块之间能够作到代码独立,互不影响,可维护性也大大加强。
关于任务切分,在第一个项目:金山游戏官网的《用户行为分析系统》中,因为数据挖掘计算须要消耗较高的内存、CPU资源,一台服务器的处理能力不够,而商业的分布式数据仓库价格又太贵,因此,只有从程序应用中下手,进行任务切分。咱们先按须要挖掘的数据指标,将整个数据挖掘任务切分红多个数据挖掘插件,每一个插件能够在不一样的服务器上运行,多个插件能够同时在多台服务器上。多个数据挖掘插件之间,若是用到相同的某项数据,那么,就将该项数据以冗余方式,复制几份提供给须要的插件,从而实现插件之间无交互、无关联,保证了超大数据量下插件的运算速度。
在第二个项目:金山游戏新版运营管理系统中,则将整个任务切分红了PHP Web管理界面、PHP Web API功能接口、C/C++中间件引擎三部分。这是一种分层结构切分,最上层的“PHP Web管理界面”调用“PHP Web API功能接口”,“PHP Web API功能接口”调用运行在游戏服务器端的“C/C++中间件引擎”,“C/C++中间件引擎”与“游戏服务器端进程”经过TCP、UDP二进制协议、信号、命令行等多种方式通讯。四者之间相对独立,代码无关联,经过一层层API接口实现交互。“PHP Web管理界面”负责通用界面实现。“PHP Web API功能接口”内部,又按接入的游戏模块、子功能模块进行了更细的切分,各功能模块之间经过内部API交互。“C/C++中间件引擎”大而全,不处理具体指令,但兼容TCP、UDP、HTTP、HTTPS/SSL、信号、命令行等大多数通讯方式,负责和各类类型的游戏服务端交互。这是一套彻底由API接口驱动的系统架构,一款新游戏接入运营管理系统时,只需在“PHP Web API功能接口”中增长一个模块;一个游戏新管理功能的增长,只须要在“PHP Web API功能接口”中增长一个子模块。经过任务切分,将复杂功能简单化,也将原来接入一款新游戏所须要的几个月时间,缩短为1~2周。shell