网站的高性能架构

4.1性能测试

网站性能是客观的指标,可以具体体现到响应时间、吞吐量等技术指标,同时也是主观的感受。从用户视角、开发人员视角、运维人员视角来看一个网站的性能是不同的,因为他们所关注的点不同。 不同视角下有不同的性能标准,不同的标准有不同的性能测试指标,从开发和测试人员的视角,网站性能测试的主要指标有响应时间,并发数,吞吐量等。性能测试的方法有:性能测试,负载测试,压力测试,稳定性测试。

根据网站分层架构,可分为Web前端性能优化、应用服务器性能优化、存储服务器性能优化3大类。

4.2web前端性能优化

web前端的主要优化手段有优化浏览器访问、使用反向代理、CDN等。

4.2.1浏览器访问优化

1.减少http请求

HTTP协议是无状态的应用层协议,意味着每次HTTP请求都需要建立通信链路、进行数据传输,而在服务器端,每个HTTP都需要启动独立的线程去处理。这些通信和服务的开销都很昂贵,减少HTTP请求的数目可有效提高访问性能。

减少HTTP的主要1手段是合并CSS、合并JS、合并图片。将浏览器一次访问需要的JS、CSS合并成一个文件,这样浏览器只需要一次请求。

2.使用浏览器缓存

对于一个网站而言,CSS、JS、Logo这些静态资源文件更新的频率都比较低,而这=这些文件几乎是每次HTTP请求都需要的,如果将这些文件缓存在浏览器中,可以很好的改善性能。通过设置HTTP头中cache-Control和Expires属性,可设定浏览器缓存,缓存时间可以是几天或者数月。

3.启用压缩

在服务端对文件进行压缩,在浏览器端对文件解压缩,可有效减少通信传输的数据量。文本文件的压缩效率可达80%以上,因此HTML、CSS、js文件启用压缩可达到较好的效果。但是压缩对服务器和浏览器产生一定的压力。在通信带宽良好,而服务器资源不足的情况下要权衡考虑。

4.CSS放在页面最上面,JS放在页面最下面

浏览器在下载完全部CSS之后才对整个页面进行渲染,因此最好的做法是将CSS放在页面最上面,让浏览器尽快下载CSS。JS相反,浏览器在加载JS后立即执行,有可能会堵塞整个页面,造成页面显示缓慢,因此JS最好放在页面最下面。但如果页面解析时就需要用到JS,这时放在底部就不合适了。

5.减少cookie传输

一方面,Cookie包含在每次请求和响应中,太大的cookie会严重影响数据传输,因此哪些数据需要写入cookie需要慎重考虑,尽量减少Cookie中传输的数据量。另一方面,对于某些静态资源的访问,如CSS,JS等,发送cookie没有意义,可以考虑静态资源使用独立域名访问,避免请求静态资源时发送Cookie

4.2.2CDN加速

CDN(内容分发网络)的本质仍然是一个缓存,而且将数据缓存在离用户最近的地方,使用户以最快速度获取数据。

网站的高性能架构

CDN能够缓存的一般都是静态资源,如图片、文件、CSS、js、静态网页等,但是这些文件访问频率很高,将其缓存在CDN可极大改善网页的打开速度。

4.2.3反向代理

反向代理服务器位于网站机房一侧,代理网站Web服务器接受HTTP请求。和传统代理服务器可以保护浏览器安全一样,反向代理服务器也具有保护网站安全的作用。来自互联网的访问请求必须经过代理服务器,相当于在建立了一个屏障。

除了安全功能外,代理服务器也可以通过配置缓存功能加速Web请求。当用户第一次访问静态内容时,静态内容就被缓存在反向代理服务器上,这样当其他用户访问该静态内容时,就可以直接从反向代理服务器返回了。加速web请求响应速度,减轻web服务器负载压力。实际上,有些网站会把动态内容也缓存在代理服务器上,把热门词条,帖子,博客缓存在反向代理服务器上加速用户访问速度,当这些动态内容有变化时,通过内部通知机制通知反向代理缓存失效,反向代理会重新加载最新的动态再次缓存。

4.3应用服务器性能优化

应用服务器就是处理网站业务的服务器,网站的业务代码都部署在这里,是网站开发最复杂,变化最多的地方,优化手段主要有缓存、集群、异步等。

4.3.1分布式缓存

在网站架构演化历程中,当网站遇到性能瓶颈时,最先想到的解决方案就是使用缓存。在整个网站应用中,缓存无处不在,即存于浏览器,也存于应用服务器和数据库服务器;既可以对数据缓存,也可以对文件缓存,还可以对页面片段缓存。

网站性能优化的第一定律:优先使用缓存优化性能。

缓存中主要用来存放很少变化的数据,如商品的类目信息,热门词的搜索列表信息,热门商品信息等。应用程序读取数据时,先到缓存中读取,如果读取不到或数据已失效,再访问数据库,并将数据写入缓存。

网站的高性能架构

网站数据遵循二八定律,即80%的访问落在20%的数据上,因此利用Hash表和内存的高速访问特性,将这20%的数据缓存起来,可很好地改善系统性能,提高数据读取速度,降低存储访问压力。

合理使用缓存:

当然,也要合理使用缓存。对于频繁修改的数据没有热点的访问的数据来说不必使用缓存。另外还会产生数据不一致与脏读问题,一般会对缓存的数据设置失效时间,一旦超过失效时间,就要从数据库中重新加载。因此应用要容忍一定时间的数据不一致,如卖家已经编辑过商品属性,但是仍需要过一段时间才能被买家看到。在互联网应用中,这种延迟通常是可以接受的,但是具体应用仍需慎重对待。还有一种策略是数据更新时立刻更新缓存,不过这也会带来更多系统开销和事务一致性问题。

另外还需考虑缓存可用性。缓存是为提高数据读取性能的,缓存数据丢失或者缓存不可用不会影响到应用程序的处理,它仍可以直接从数据库读取数据。但是随着业务的发展,缓存会承担大部分数据访问的压力,数据库习惯了有缓存的日子,所以当缓存服务崩溃时,数据库会因为完全不能承受如此大的压力而宕机,这种情况称为缓存雪崩。发生这种故障,甚至不能简单地重启缓存服务器和数据库服务器来恢复网站访问。实践中,有点网站通过缓存热备等手段提高缓存可用性。当某台服务器宕机时,将缓存访问切换到热备服务器上。但是这种设计显然有违缓存的初衷,缓存根本就不应该作为一个可靠的数据源。还可以通过分布式缓存服务器集群,当一台缓存服务器集群宕机时,只有部分数据丢失,重新从数据库加载这部分数据不会对数据库产生很大影响。

缓存中存放的是热点数据,新启动的缓存系统如果没有任何数据,在重建缓存数据的过程中,系统的性能和数据库负载都不太好,那么最好在缓存系统启动时就把热点数据加载好,这个缓存预加载的手段称为缓存预热。对于一些元数据如城市地名列表、类目信息等,可以在启动时加载数据库中全部数据到缓存中进行预热。

3.分布式缓存架构

分布式缓存指缓存部署在多个服务器组成的集群中,以集群方式提供缓存服务,其架构方式有两种,一种是以JBoss Cache为代表的需要更新同步的分布式缓存,一种是以Memcached为代表的不互相通信的分布式缓存

JBoss Cache的分布式缓存在集群中所有服务器中保存相同的缓存数据,当某台服务器有缓存数据更新的时候,会通知集群中其他机器更新缓存数据或清除缓存数据,JBoss Cache通常将应用程序和缓存部署在同一台服务器上,应用程序可从本地快速获取缓存数据,但是这种方式带来的问题是缓存数据的数量受限于单一服务器的内存空间,而且当集群规模较大的时候,缓存更新信息需要同步到集群所有机器,其代价惊人。因而这种方案更多见于企业应用系统中,而很少在大型网站中使用。

网站的高性能架构

大型网站需要缓存的数据量一般都很庞大,可能会需要数TB的内存做缓存,这时候就需要另一种分布式缓存,Memcached采用一种集中式缓存集群管理,也被称为互不通信的分布式架构方式。缓存与应用分离部署,缓存系统部署在一组专门的服务器上,应用程序通过一致性hash等算法选择缓存服务器远程访问数据,缓存服务器之间互不通信,缓存集群的规模可以很容易地实现扩容,具有良好的可伸缩性。

4.Memcached

Memcached曾一度是网站分布式缓存的代名词,被大量网站使用。其简单的设计、优异的性能、互不通信的服务器集群、海量数据可伸缩的架构令网站架构师们趋之若鹜。

网站的高性能架构

简单的通信协议

Memcached使用TCP协议(UDP也支持)通信,其序列化协议则是一套基于文本的自定义协议,非常简单,以一个命令关键字开头,后面是一组命令操作数。例如读取一个数据的命令协议是get<key>,Memcached以后,许多NoSQL产品都借鉴了或直接支持这套协议。

丰富的客户端

几乎所有主流的网站编程语言,Java,c++,python等都支持Memcached。

互不通信的服务器集群架构

如上所述,正是因为这个特性使得Memcached从JBoss Cache等众多分布式缓存产品中脱颖而出,满足网站对海量缓存数据的需求。而其客户端路由算法 一致性hash更成为数据存储伸缩性架构设计的经典范式。正是集群内部服务器互不通信使得集群可以做到几乎无限制的线性伸缩,这也是目前流行的许多大数据技术的基础架构特点。

虽然现在,NoSQL产品层出不穷,在数据持久化,支持复杂数据结构,甚至性能方面有许多产品优于Memcached,但Memcached由于其简单,稳定的特点,仍然在分布式缓存领域占据着重要地位。

4.3.2异步操作

使用消息队列将调用异步化,可改善网站的扩展性。事实上,使用消息队列还能够改善网站系统的性能。

网站的高性能架构

在不使用消息队列的情况下,用户的请求数据直接写入数据库,在高并发的情况下,会对数据库造成巨大的压力。在使用消息队列后,用户请求的数据发送给消息队列服务器后立即返回,再由消息队列的消费者进程从消息队列中获取数据,异步写入数据库。 消息队列有很好的削峰作用,即通过异步处理,将短时间高并发产生的事务消息存储在消息队列中,从而削平高峰期的并发事务。

网站的高性能架构

需要注意的是,由于数据写入消息队列后立即返回给用户,数据在后续的业务校验,写数据库可能存在失败,因此在使用消息队列进行业务异步处理后,需要适当修改业务流程进行配合。 如订单提交后,不能立即返回用户订单提交成功,需要在消息队列的订单消费者进程真正处理完该订单,甚至商品出库后后,在通知用户订单成功。

4.3.3使用集群

在网站高并发访问的场景下,使用负载均衡技术为一个应用构建一个由多台服务器组成的服务器集群,将并发访问请求分发到多台服务器上处理,避免单一服务器因负载压力过大而响应缓慢,使用户请求具有更好的响应延迟性。

网站的高性能架构

4.3.4代码优化

1.开启多线程

目前主要的Web应用服务器都采用多线程的方式响应并发用户请求,因此网站开发天然就是多线程编程。

从资源利用的角度看,使用多线程的原因主要有两个:IO堵塞与多CPU。当前线程进行IO处理的时候,当前线程会释放CPU以等待IO操作完成(类似sleep方法),由于IO操作通常需要较长时间,这是CPU可以调度其他线程进行处理。 理想的系统Load是既没有进程(线程)等待也没有CPU空闲,,利用多线程IO阻塞与执行交替进行,可最大限度利用CPU资源。使用多线程的另外一个原因是服务器有多个CPU,一般的数据中心服务器至少16核CPU,要想最大限度利用这些CPU,必须启动多线程。

一台服务器上启动多少线程合适呢?

启动线程数 = [任务执行时间/(任务执行时间-IO等待时间)] * CPU数

最佳启动线程数和CPU内核数量成正比,和IO阻塞时间成反比。如果任务都是CPU计算型任务,那么线程数最多不超过CPU内核数;相反如果任务需要等待磁盘操作,网络响应,那么多启动线程有助于提高任务并发度,提高系统吞吐能力,改善性能。

多线程还需要注意线程安全问题,用户的请求是并发的,也就是说,所有的资源---对象、内存、文件、数据库等都可能被多线程并发访问。

解决线程安全的主要手段有:

将对象设计成无状态对象。 所谓无状态对象指对象本身不存储状态信息(对象无成员变量,或者成员变量也是无状态对象),这样多线程并发访问时就不会出现状态不一致,Java Web开发中常用的Servlet对象就设计为无状态对象,可以被应用服务器多线程并发调用处理用户请求。而Web开发中常用的贫血模式对象都是些无状态对象。

使用局部变量。即在方法内部创建对象,这些对象会被每个进入该方法的线程创建,除非程序有意思的将这些对象传递给其他线程,否则不会出现对象被多线程并发访问的情形。

并发访问资源时使用锁:通过锁的方式使多线程并发操作转为顺序操作,从而避免资源被并发修改。随着操作系统和编程语言的进步,出现各种轻量级锁,使得运行期现场获取锁和释放锁的代价都变得很小,但是锁导致线程同步顺序执行,可能会对系统性能产生严重影响。

2.资源复用

尽量减少开销很大的系统资源的创建和销毁,如数据库连接,网络通信连接,线程等。从编程角度讲,资源复用主要有两种模式:单例模式和对象池。

单例模式:由于现在Web开发中主要使用贫血模式,从Service到Dao都是无状态对象,无需重复创建,使用单例模式也就自然而然了。事实上,Java开发常用的对象容器Spring默认构造的对象都是单例(Spring的单例是Spring容器管理的单例,而不是单例模式构造的单例)。

对象池:对象池模式通过复用对象实例,减少对象创建和资源消耗。对于数据库连接对象,每次创建连接,数据库服务端都需要创建专门的资源以应对,因此频繁创建关闭数据库连接,对数据库服务器而言是灾难性的,同时频繁创建关闭连接也需要花费较长时间。因此在实践中,应用程序的数据库连接基本使用连接池的方式。

对于每个Web请求,Web应用服务器都需要创建一个独立的线程去处理,这方面,应用服务器也采用线程池的方式。这些所谓的连接池,线程池,本质上都是对象池,即连接,线程都是对象,池管理方式也基本相同。

3.垃圾回收

这里就不多说了,想详细了解的话可以看下  深入理解Java虚拟机  这本书

如果对下面几个概念不太清晰的话,可以看下下面几篇博客,都写的不错解释的很清晰。

session与cookie?

https://blog.csdn.net/liyifan687/article/details/80077928

有状态与无状态对象?

https://www.cnblogs.com/xubiao/p/6567349.html

贫血模式与充血模式?

https://blog.csdn.net/wanghantong/article/details/38424075

IO与线程状态?

https://www.cnblogs.com/onlywujun/archive/2012/11/25/2787167.html