架构师成长记大型公司分布式码农的世界

网站的性能优化概述

2017-08-05  本文已影响124人  iszhenyu

网站的性能问题很多是在用户高并发访问时产生的,所以网站性能优化的主要工作就是改善高并发用户访问情况下的网站响应速度。对于最终用户而言,网站性能更多的是一种直观感受,我们的目的也是改善用户的体验,让用户感觉网站很快。从技术层面来说,对网站性能的调优,可以从三个大的方面入手,分别是前端性能的优化、服务器性能优化和存储性能的优化。

Web前端性能优化

浏览器访问的优化

  1. 减少HTTP的请求

由于HTTP协议是无状态的应用层协议,也就是每次HTTP请求都要通过三次握手来建立通信链路并进行数据传输。在服务端,每个HTTP请求都需要启动独立的线程去处理,而创建新线程的开销又很大,所以在页面加载的时候减少HTTP请求的次数可以有效提高访问性能。

所以,问题就变成如何减少HTTP请求上。浏览器在加载网页的同时,也会对页面引用的CSS文件、js文件以及图片等资源进行加载,如果可以减少这些静态文件引用的数目也必然减少了HTTP请求的次数,于是我们能想到的一个方法就是对这些静态资源进行合并,比如可以将多个icon放到一个图片里,通过css偏移来控制具体显示哪个icon。

  1. 使用浏览器缓存

由于静态文件更新的频率比较低,而又是每次HTTP请求都需要的,将这些文件缓存在浏览器中可以极好的改善性能。在HTTP Header中,有Cache-Control和Expires两个属性,我们可以通过指定这两个属性的值来设定浏览器缓存。当静态资源发生改变需要及时应用到客户端浏览器时,可以通过改变文件名来实现。对于大型的网站,在更新的时候还应该避免多个文件一次性全部更新,而是一个文件一个文件逐步更新,这样可以避免因为文件失效带给服务端的过大的压力。

  1. 压缩

对文件进行压缩也是改善前端性能的一个重要手段,通过在服务器端对文件进行压缩,可以有效减少通信传输的数据量。但是压缩对服务器和浏览器都会有一定的压力,因此,当带宽良好而服务器资源不足的时候要权衡考虑。

  1. CSS在页面最上面,JS在页面最下面

严格来说这可能都不算技术层面的优化,我们知道浏览器在下载完全部CSS文件后才会对整个页面进行渲染,而对于JS,浏览器则是在加载后立即执行,将CSS放在最上面JS在最下面,其目的就是让浏览器尽快下载CSS,同时避免JS运行对页面渲染的阻塞,进而造成页面显示缓慢。我们也只是在感官上让用户感觉页面打开变快了,其实页面完全加载的总耗时并没减少。

  1. 减少Cookie传输

Cookie也是我们考虑优化的一个方面,在每次请求和响应中,Cookie都会作为附带数据进行传输,因此,如果Cookie过大也会严重影响数据传输。哪些数据需要写入Cookie需要慎重考虑,对于一些静态资源,发送Cookie没有意义,可以考虑使用独立域名来访问静态资源,从而避免Cookie的传输。

CDN加速

CDN,全拼是Content Distribute NetWork,翻译过来就是内容分发网络,CDN通常是部署在网络运营商的机房,同时这些网络运营商也为终端用户提供上网服务,当用户的请求经过运营商时会首先在CDN中检查是否有可用的资源,如果资源存在,则直接返回给浏览器,从而大大缩短了请求的路径,也就加快了访问的速度,也减轻了数据中心负载的压力。我们在CDN中一般会放一些静态文件,如图片、CSS文件、JS文件,静态网页等,从这一点来看,CDN本质上可以认为就是一个缓存。

反向代理

我之前一直不明白为啥叫反向代理,有人总喜欢搞一些名词把大家弄的云里雾里的。其实这个反向代理也不难理解,说白了就是服务端的负载均衡器。那为啥会叫反向代理呢,这里着重的是反向这个词,有反向就必然有正向,正向代理是很好理解的,比如我们在国内需要访问google,但是又没办法直接访问,于是我们会用一个vpn,让vpn帮我们去请求,然后把结果再返回来,vpn在这里所扮演的角色就是正向代理。google在接收到请求后,会通过负载均衡来转发给具体的某台服务器来处理,从请求者的角度来看,我们无需关心处理请求的是哪台机器,这个负载均衡器在这里起到了代理具体服务器的作用。我们只需要知道,反向代理是啥东东就可以了。

那么反向代理到底是如何加速网络访问速度的呢?因为静态内容的更新频率是很低的,反向代理可以通过配置缓存功能,来加速Web请求响应速度。

通过上面的总结,不难看到,Web前端优化性能的目的很明确,就是尽早的把请求的内容返回给用户,手段也很直接,就是缓存、缓存还是缓存。

服务器性能优化

使用缓存

我们在上一节介绍了那么多的优化手段,无一例外是跟缓存相关的,所以我们就先来说说缓存到底是个什么东东。

缓存的本质是一个内存Hash表,数据以Key、Value的形式存储在这个表里,而Hash表读写的时间复杂度是O(1),因此可以快速访问Hash表中的数据。一方面缓存访问速度快,可以减少数据访问的时间,另一方面,如果缓存的数据是经过计算得到的,那么也就无需重复计算即可直接使用,因此,缓存还起到减少计算时间的作用。

但是,并非所有的数据都是适合放到缓存中,如果不合理使用缓存,非但不会提高系统的性能,还会成为系统的累赘甚至会造成风险。在使用缓存之前,我们需要明确几个问题:

  1. 数据是否频繁修改:
    当数据写入一次缓存,在其更新前至少读取两次才有意义,否则就会徒增系统的负担。

  2. 放入缓存的是否是热点数据:
    我们知道,内存的资源是非常有限的,在一定的空间内,当新的数据被写进缓存,就会将历史数据清理出去,因此,如果访问数据没有热点,那么大部分数据还没被再次访问就已经被挤出缓存了。

  3. 数据不一致与脏读:
    一般我们都会对缓存设置失效时间,一旦超过失效时间,就需要从数据库重新加载,因此,在一定时间内,数据可能会不一致,所以在写缓存之前我们要明白,是否可以容忍数据的不一致性。

  4. 缓存可用性:
    随着业务的发展,缓存会承担大部分数据访问的压力,这个时候数据库已经习惯了有缓存的日子,当缓存服务崩溃时,数据库会因为无法承受如此大的压力而宕机,进而导致整个网站不可用,这种情况被称作缓存雪崩。尤其是大型网站,我们在使用缓存的时候也不得不考虑这方面的问题。

  5. 缓存预热:
    在缓存系统刚刚启动的时候是没有任何数据的,缓存数据的建立是一个过程,在这个过程中,所有的压力就不得不落到数据库身上,过大的压力同样会拖垮整个网站。那么,最好的方法就是在缓存系统启动的时候就把热点数据加载好,这个预加载的手段就叫做缓存预热。

  6. 缓存穿透:
    缓存穿透是我们特别容易忽略但是又非常重要的一个考虑因素。因为不恰当的业务或者恶意攻击导致持续高并发的请求某个不存在的数据,由于缓存没有保存该数据,所有的请求都会落到数据库上,也就是缓存穿透。一个简单的对策就是将不存在的数据也缓存起来,比如空的列表或null值等。

分布式缓存

一台机器能够提供的缓存大小毕竟是有限的,所以随着业务的增长,我们通常需要将缓存部署在多个服务器组成的集群中,也就是分布式缓存。其架构有两种方式,一种是以JBoss Cache为代表的需要更新同步的分布式缓存,一种是以Memcached为代表的不互相通信的分布式缓存。

对于第一种,在集群中所有的服务器都保存了相同的缓存数据,当某台服务器有缓存数据更新的时候,会通知集群中其他机器也要进行更新,这种方式带来的问题也很明显,一是缓存数据的数量受限于单台服务器的内存空间,二是当集群规模较大时,缓存更新信息同步到所有机器的代价是巨大的。鉴于这些缺点,很少有大型网站使用这种方式。

Memcached则采用的是一种完全不同的方式,被称作互不通信的分布式架构方式,其最大的特点是缓存服务器之间不通信,缓存的集群规模可以很容易的扩容,应用程度通过一致性Hash等路由算法选择缓存服务器并远程访问缓存数据。这个一致性Hash算法我们在后面的文章中会介绍。

异步操作

异步操作的思想是任何可以晚点做的事情都应该晚点再做

异步操作的具体实施并不是我们通常理解的新建个线程去执行某些代码,而是利用消息队列来帮助我们将调用异步化。在使用消息队列后,用户的请求并不会立即得到执行,而是首先发送给消息队列然后立即返回。消息队列的消费者进程在另一端不断的从队列中获取数据,然后进行入库等操作。这种方式可以有效的改善用户的响应延迟。

但是,在使用消息队列的时候也要注意,数据在后续的业务校验、写数据库等操作中也可能失败,因此,我们的业务流程也要做适当的修改,不能立即返回用户提交成功,而是在真正处理完用户请求后再通过另外的方式通知用户。

使用集群

有的同学可能会混淆集群和分布式的概念,这里先简单说下区别,分布式是将不同的业务分布在不同的地方,而集群是将几台服务器集中在一起,实现同一业务,分布式中的每一个节点都可以做集群,而集群并不一定是分布式的。分布式的组织比较松散,不像集群有一个组织性,一台服务器宕了,其他的服务器可以顶上来,而分布式的一个节点垮了,那这个业务就不可访问了。

使用集群为啥可以优化用户请求的响应延迟呢?使用单一服务器时,当负载压力过大时会变得响应缓慢,通过负载均衡技术为一个应用构建一个由多台服务器组成的集群,从而将并发访问请求分发到多台服务器处理。由于单台服务器的并发请求数目控制在最佳运行区间,因此可以获得最佳的访问请求延迟。

代码优化

在计算资源越来越廉价的今天,很多人觉得代码的优化对改善网站性能没有什么实质性的帮助,我个人是完全不同意这个观点的,即使在做web开发时大多时候只是在框架之下处理一些业务逻辑。代码优化说起来简单,其实是最复杂的,不同的编程语言在优化手段上虽不尽相同,但也有一些共性的东西,比如多线程的使用、资源的复用以及垃圾的回收等,由于篇幅的原因,这里就不再一一展开说了。最后我个人觉得比较重要的一个方面就是数据结构,在早期对程序的一个定义是,程序就是数据结构加算法,足见数据结构对于编程的重要性。

存储性能优化

在很多时候,虽然我们通过前面的一些手段已经解决了大部分的性能问题,但不得不承认,磁盘仍然是系统最严重的瓶颈。对于一些大型网站,对磁盘的优化也是非常必要的。

从磁盘类型上看,目前市面上可以分为机械硬盘和固态硬盘。机械硬盘通过将磁头移动到指定的磁盘位置来访问数据,而磁头又通过马达带动磁头臂来进行移动。由于每次访问数据都需要移动磁头臂,因此,机械硬盘在随机访问时的性能非常低。固态硬盘又叫做SSD,它没有机械装置,并且可以像内存一样快速随机访问,由于去除了马达和机械臂这类的机械组件,SSD在功耗、震动和噪声等方面都较机械硬盘要小得多。当然了,SSD最大的缺点就是价格昂贵。

虽然机械硬盘的随机访问性能一般,但是前人通过牛逼的算法在一定程度上改善了这个问题,比如B+树、LSM树等。大家所熟知的Mysql就用到了B+树来做索引,而现在很火的NoSQL则大量采用了LSM算法。感兴趣的同学可以自行深入了解下这两个算法,这里就不再展开了。

至此,在网站性能优化这个层面上,基本上所有可用的手段就介绍完了,无论多牛逼的网站无非是将这些手段做到了极致。最后,要强调的是,网站的架构演变一定是随着业务的发展不断变化的,很多人刚开始做架构时都喜欢上来就规划的很大,好像只有这样才能显示出自己的牛逼,为了架构而架构时非常不可取的。笔者曾经就遇到过,一个几千用户的小网站光服务器就搭了上百台,最后整个团队精力都花在了维护这些服务器上,业务反倒没怎么上去。所以,知道在什么时候做什么事情很重要,不应当盲目的追求技术。


这样学机器学习
上一篇下一篇

猜你喜欢

热点阅读