“用户速度体验的1-3-10原则”如图3-10所示。
图3-10 用户速度体验的1-3-10原则
分析表明,服务的响应时间如果超过3s,用户的心情会急剧变化,由此可见,高性能是服务平台一个非常重要的指标,是优质用户体验的基础。
在面对大量用户访问和高并发请求方面,使用高性能的服务器、高性能的数据库、高性能的Web容器无法从根本上解决问题,而且投入成本也很高。架构设计需同时考虑高性能、可扩展性和低成本,通过合理优化策略,实现在大容量、高并发条件下的最优用户体验。
云计算服务是通过互联网向所有客户提供服务,一般在构建系统时会根据单机容量决定各个环节集群的数量,对于Web服务,一般会分为Web接入层、服务应用层和数据层,对于不同服务,系统性能瓶颈可能不同,如对于数据逻辑复杂的应用,应用层可能是系统性能瓶颈,而对于数据集中型服务,数据层决定着整个系统性能,而在互动与接入访问频繁、显示复杂的服务,接入层可能是系统性能的瓶颈,因此对于云计算设计者,首先要评估系统容量对各个环节的压力,然后找出瓶颈,有针对性地进行优化,如果不能准确地通过评估找到性能瓶颈,就需要搭建系统,通过测试了解系统瓶颈,然后有针对性地进行系统性能及容量优化。
相对于传统的软件产品,互联网服务提供的是一种在线Web应用,界面元素较为丰富,并且用户可以从不同地区,通过不同的网络访问服务。相对于应用层和数据库层的调优,Web层的优化相对容易。
选取目前比较流行的几种Web Server进行比较:
Lighttpd:Lighttpd是一个开源的轻量级Web Server,CPU占用率和内存开销都比较低,效能较好,模块也很丰富。
Apache:Apache是目前使用最为广泛的一个Web服务,具有跨平台和高安全性等特性。
Nginx:Nginx是一个轻量级、高性能的HTTP和反向代理服务器。
几种Web服务器的性能比较如表3-3所示。
表3-3 几种Web服务器的性能比较
对静态页面的请求及响应性能比较如表3-4~表3-6所示。
表3-4 Lighttpd性能数据
表3-5 Nginx性能数据
表3-6 Apache性能数据
从这些数据可以看到,Nginx作单纯的Web服务器性能上比Apache要好,特别是在承受压力、带宽及资源消耗上都要优于Apache。如果以3s的响应时间作为标准,Nginx能应付不超过10 000的并发连接数,如果以10s响应时间作为标准,Nginx能应付15 000以下的并发连接。
应用服务器主要处理核心的业务逻辑,对于静态资源,应尽可能分离,让高效的Web Server来处理,从而降低应用服务器的压力。常见的静态资源有图片、JavaScript、css、HTML等。
静态化的HTML页面效率最高、消耗最小,因此在能够静态化的地方,应优先考虑该方案。
一些大型的门户网站和信息发布类网站,都会采用CMS进行信息管理,并通过一定的策略自动生成静态页面。对交互性较高的网站,尽可能的静态化也是提高性能的必要手段。
HTML静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很少的应用,可以使用静态化HTML片段来实现,这些内容在后台更新的同时进行静态化,避免了大量的数据库访问请求。
通过URI编码,将网页请求的状态尽量保存在URI中,通过相同URI获得相同页面,可以通过Cache机制,将动态网页静态化,最大限度提升系统访问能力。
前面提到的优化方法对性能提高能起到很大的作用,但从开发的角度来看,高质量的代码才是根本之道。在这方面,Yahoo团队从实践中总结出了34条基本的黄金守则,前10条如下。
· 最小化HTTP请求(Minimize HTTP Requests)。
· 使用内容分发网络(Use a Content Delivery Network)。
· 在HTTP请求头加入过期时间字段或缓存控制字段(Add an Expires or a Cache-Control Header)。
· 使用Gzip压缩(Gzip Components)。
· 将CSS文件放在网页头部(Put Stylesheets at the Top)。
· 将脚本文件放在页面底部(Put Scripts at the Bottom)。
· 避免使用CSS运算表达式(Avoid CSS Expressions)。
· 将JavaScript脚本文件和CSS文件外部化(Make JavaScript and CSS External)。
· 减少DNS查找(Reduce DNS Lookups)。
· 最小化JavaScript和CSS文件(Minify JavaScript and CSS)。
互联网应用是整合所有资源的综合服务,从网络到服务器,从操作系统到应用软件,任何一个环节出现瓶颈或者异常都将降低服务质量,严重的可能会直接导致系统崩溃。
在架构设计上,需要综合平衡各方面资源,例如“页面静态化”,就是充分发挥Web Server的能力,减轻对应用层和数据库层的压力,下面主要对应用层的一些优化方法进行介绍。
缓存是提高性能的重要措施,合理的应用可以起到平衡资源的作用,比较典型的场景包括:
· 缓存整个页面或页面片段,如HTML、JS、图片等。
· 缓存那些耗时运算结果数据。
· 缓存那些耗时的查询结果和业务数据。
· 缓存上下文相关的数据,如应用级配置信息和会话级用户的信息等。
这里说的缓存是贯穿Web层、应用层和数据库层的一种综合优化方式,主要面向的缓存对象是相对稳定但访问频繁的数据。
缓存命中率是衡量缓存设计的一个重要指标,主要影响因素包括:
· 时效性:业务决定的缓存过期时间。
· 容量:缓存的大小,受限于硬件结构,如内存。
· 粒度:缓存的健值算法设计。
· 架构设计:分析出业务中的真正热点。
缓存的应用在于减少CPU、I/O、Network的消耗,从而提高相关资源的效率,常见加载模式如图3-11所示。
图3-11 缓存加载模式
对于选择集中式还是分布式缓存,需要根据具体情况进行分析,这里不做进一步分析。
在实例介绍部分,将介绍Memcached的详细应用。
在计算机基础数据结构中,对算法复杂度与系统开销有精确描述,2 n 、n 2 、nlgn等不同算法复杂度,对系统性能影响是巨大的,因此算法优化能极大提升系统性能,这是在设计系统时需要认真分析并仔细优化的地方。
在设计时,首先要考虑运行环境,如现在服务器一般是多核CPU,如果使用单线程,CPU性能不能完全利用,系统性能一定不理想。一般来讲,使用线程数量约是CPU数量的两倍为佳,太多线程会因为调度开销,降低整个系统的性能。此外,线程安全与同步,往往会引入线程锁,频繁的线程锁会极大降低系统性能,因此应该在算法中避免频繁使用线程锁,将线程锁范围尽量缩小,才能提升系统性能。
对于搭建系统,可以通过观察各种资源的消耗情况,并结合profiler工具进行分析和定位,对程序进行一定的优化,可以充分利用各种资源,提升服务的整体能力。
下面介绍两个实际调优的例子:
例1:这个例子是关于CPU使用的,代码的主要功能是生成一批静态页面,优化之前任务执行时间是30min,优化之后只需要2min。优化之前观察到服务器是4核的,但该任务是单线程的,仅使用到一个核,单核消耗CPU在30%左右,同时由于执行时间长,对公共资源的消耗也很大,导致整体服务不平稳。将任务改为多线程,并让单线程的任务片段间适当sleep,最终性能大幅提升。
例2:这个例子是关于锁的问题,在代码中使用OgnlRuntime的一个方法来做页面渲染,在OgnlRuntime的实现中该方法整个加了同步锁,而代理的方法需要使用数据库资源,观察到有的线程已经占用了OgnlRuntime,但在等待数据库连接,有的线程占用了数据库连接,但在等待OgnlRuntime的线程,最终导致大量的线程受阻,系统无法响应。新版本的OgnlRuntime对方法锁进行了分拆,缩小范围,另外也对代码进行了调整,避免发生死锁。
通过前面的例子可以看到,在程序调优上应尽可能发挥各类资源的优势,对耗时任务进行分拆,合理使用多线程技术,非实时任务采用异步实现,减少锁的使用。
应用层的扩展相对容易,数据库层的扩展比较困难,数据库层的合理优化将会极大地提升平台性能,下面分析一些常用的策略。
垂直分区(见图3-12)是指将不同模块数据存入不同的数据库中,而水平分区(见图3-13)是指将相同模块数据存入不同的数据库中。无论是垂直分区还是分区,对应用都是相对透明。
图3-12 数据库垂直分区
图3-13 数据库水平分区
另外,通过读写分离也可以增强数据层的扩展能力。
(1)建立合适的索引:加速数据检索。
(2)分离大字段:将一些数据特别大的字段从数据库表中分离出来,使用单独的表存放或者使用文件系统存放,减少I/O。
(1)避免使用复杂SQL语句:减少join操作。
(2)精简字段:减少I/O,仅获取需要的字段数据。
(3)避免大数据表连接:减少join成本。
云服务中存在一些特别高并发的模块,这些模块,仅使用常规的数据库管理和存储系统,没有办法达到需要的读、写的性能要求,需要引入多种手段和方法来解决。在这里仅做简单介绍,具体内容将在第14章做具体讲述。
第一种方法在客户端层面,使用客户端主动负载均衡路由,这与传统的服务系统使用统一的负载均衡不同,云服务在超大规模及分布式情况下,没有办法使用一个单一的路由中心来做路由策略。常见的思路是客户端通常会定期向云服务商轮询一个路由地址列表,该列表会将使用服务的域名和地址做动态解析映射,从而连接到不同的读服务器,例如百度的搜索服务www.baidu.com会跳转到sp0.baidu.com、sp1.baidu.com,等等。微信则使用了http DNS服务,客户端绕过传统DNS服务,主动通过http协议向固定服务器索要最终地址,这种做法还能有效防止域名劫持。这种客户端负载均衡的配置策略需要云服务商在地址解析层面提供基础的能力支持。
第二种方法是在接入层面,全分布式接入及降级处理。由于云服务商提供的基础接入能力是面对所有用户的,所以无法判断用户的业务高峰,如果不把接入的压力分散化,那么会导致在浪涌出现时,所有用户的服务都不可用。所以在某一个接入域的内部,是全分布式的处理结构,并且附带降级策略。我们以阿里云的服务器负载均衡(Server Load Balance,SLB)为例,阿里云将云服务分为了若干区,比如华北、华南、华东区,每个区内部还分为了北京、青岛等子域,每个域内的用户都独享自己的接入负载均衡器及接入流量限制。用户自己的SLB服务故障不影响其他用户,超过流量会被区域直接限制,用户的客户就会发现服务响应慢或者干脆无服务可用。
第三种方法是在持久化层面,除了传统的关系型数据库之外,采用全分布式的消息队列,裸写磁盘(DirectIO,绕过OS的缓冲区)的分片存储及Server SAN等软件定义存储技术。比如用户所看到的MySQL服务,对其数据目录来说是一个完整的虚拟磁盘,但实际上可能是某个盘阵中的一部分而已。即便是云服务商自己的配置管理数据库,一般都是若干个库的实例(进程)进行的分库分表存储。其数据库连接层面做了定制化处理,SQL语句的执行被自动分片,查询的结果被自动聚合,如图3-14所示。
图3-14 高并发机制