用户输入你的站点网址,等了半天。。还没打开,裤衩一下就给关了。好了,流失了一个用户。为何会有这样的问题呢。怎么解决本身站点“慢”,体验差的问题呢。前端
在这段等待的时间里,到底发生了什么?事实上这并不简单,大概经历了如下几部分时间:
数据在网络上传输的时间
站点服务器处理请求并生成回应数据的时间
浏览器本地计算和渲染的时间web
数据在网络上传输的时间总的来讲包括两部分,即浏览器端主机发出的请求数据通过网络到达服务器的时间,以及服务器的回应数据通过网络回到浏览器端主机的时间。这两部分时间均可以视为某一大小的数据从某主机开始发送一直到另外一端主机所有接收所消耗的总时间,咱们称它为响应时间,它的决定因素主要包括发送的数据量和网络带宽。数据量容易计算,可是究竟什么是带宽呢?咱们将在后续章节中详细介绍带宽的本质。数据库
站点服务器处理请求并生成回应数据的时间主要消耗在服务器端,包括很是多的环节,咱们通常用另外一个指标来衡量这部分时间,即每秒处理请求数,也称吞吐率,注意这里的吞吐率不是指单位时间处理的数据量,而是请求数。影响服务器吞吐率的因素很是多,好比服务器的并发策略、I/O模型、I/O性能、CPU核数等,固然也包括应用程序自己的逻辑复杂度等。编程
浏览器本地计算和渲染的时间天然消耗在浏览器端,它依赖的因素包括浏览器采用的并发策略、样式渲染方式、脚本解释器的性能、页面大小、页面组件的数量、页面组件缓存情况、页面组件域名分布以及域名DNS解析等,而且其中一些因素随着各厂商浏览器版本的不一样而略有变化。浏览器
可见,一个页面包含了若干个请求,每一个请求都或多或少地涉及以上这些过程,假若有一处关键环节稍加拖延,总体的速度即可想而知。缓存
“带宽”也许是计算机科学中最幽默的一个词,当我向一些开发者询问到底什么是带宽的时候,他们的回答老是让我联想到相似高速公路的宽度,而当我继续询问“咱们的带宽有多宽”时,他们就会不知所措。
另外一部分开发者显然知道带宽的单位是“bit/s”,也就是单位时间的比特数,因此他们将带宽解释为数据在线路中的移动速度,也就是将带宽的高低视为线路能力的强弱,那么很显然他们认为光纤对数据的传播能力大于铜线,但很惋惜,事实上这是错误的。顺便说一下,咱们通常常说的好比100M带宽,全称应该是100Mbit/s,或者100Mbps,后边的“bit/s”常常省略。安全
说到数据的发送,也就是数据从主机进入线路的这段旅程,通常须要通过如下几个环节:
1.应用程序首先得将要发送的数据写入该进程的内存地址空间中,熟悉网络编程的开发者对这个环节必定很是熟悉,一般在程序开发中这只须要通常的运行时变量赋值便可。
2.应用程序经过系统函数库接口(好比send函数)向内核发出系统调用,由系统内核来进行随后的操做,它将这些数据从用户态内存区复制到由内核维护的一段称为内核缓冲区的内存地址空间。这块地址空间的大小一般是有限的,全部要发性能优化
送的数据将以队列的形式进入这里,这些数据可能来自于多个进程,每块数据都有必定的额外记号来标记它们的去向。若是要发送的数据比较多,那么该系统调用须要屡次进行,每次复制必定的数据大小,这个大小取决于网络数据包的大小以及内核缓冲区的承载能力。重复的系统调用体如今应用编程层面重复调用send函数。
3.当数据写入内核缓冲区后,内核会通知网卡控制器前来取数据,同时CPU转而处理其余进程。网卡控制器接到通知后,便根据网卡驱动信息得知对应内核缓冲区的地址,将要发送的数据复制到网卡的缓冲区中。注意在以上一系列的数据复制中,数据始终按照链接两端设备的内部总线宽度来复制,也就是字节的整数倍,好比在32位总线的主机系统中,采用PCI-X总线接口的网卡通常使用32位总线宽度,那么从内核缓冲区到网卡缓冲区的数据复制过程当中,任什么时候刻只能复制32位的比特信息。
4.网卡缓冲区中的数据须要发送到线路中,同时释放缓冲区来获取更多要发送的数据。可是咱们知道,只有二进制的数字信号才能够在线路中传输,因此这时候须要对数据进行字节到位的转换,这种转换不难想象,就是将数据的每一个位按照顺序依次发出。服务器
5.发送时,网卡会使用内部特定的物理装置来生成能够传播的各类信号,好比在使用铜线线路时,网卡会根据“0”和“1”的变化产生不一样的电信号;而使用光纤线路时,网卡会产生不一样的光信号。网络
在大多数状况下,咱们都将Web站点服务器托管在IDC,经过将其链接到某个交换机,从而接入互联网。这时候,咱们的服务器拥有本身的IP地址,当站点用户经过互联网向这台服务器请求数据后,数据从服务器流经交换机到达指定的路由器,这个过程须要交换机的存储转发机制,也就是交换机从链接服务器的端口接收数据,存储到交换机内部的高速缓冲区队列中,而后将其从链接路由器的端口发送出去,再通过路由器的转发,进入另外一个网络,接下来依次重复这些过程,直到进入站点用户的PC。
若是全世界只有你的服务器和你的用户在传输数据,那么这部分数据流经的每一个交换节点都会全心全意地作好转发工做,此时你的数据在各节点转发的发送速度均可以达到理论上设备所能达到的最大值。但实际上每处交换节点都有可能同时转
发来自其余主机的数据,包括你的数据在内的全部数据都聚集进入路由器的转发队列,路由器按照转发队列中的顺序来交错地发送这些来自不一样主机的数据,因此单历来自不一样主机的数据而言,其转发时的发送速度一定小于全部从路由器转发出去的数据的发送速度(即该交换节点的出口带宽)。
由于带宽是有限的,它毫无疑问是个抢手资源,并且互联网运营商也不会白白搭建网络,因此运营商在全部的基础交换节点上设置关卡,也就是限制数据从你的主机流入路由器转发队列的速度,而只要流入路由器转发队列的数据,都会按照路由器的出口带宽,流入其余网络。
这种关卡设置实际上等于限制了你的主机发送数据的速度,也就是限制了主机的出口带宽,而至于这种限制的实现机制,我想你已经很清楚了,那就是经过限制交换机对于你主机的数据接收速度,来将你的发送速度紧紧控制在手,由于数据链路层的流量控制是经过控制接收方来实现的。对于交换机的限速设置,IDC的网管很是擅长。
一切都清楚了,下面咱们来看看两个被交换机限制了带宽的主机,它们都安装了MRTG,能够生成网卡流量报告单,不过咱们在这里关心的不是流量,而是报告单顶部的一段信息,请注意这里的Max Speed属性值,它即是从交换机接收端口得到的最大接收速度,同时也是该主机的最大数据发送速度,但并不必定是此刻的实时发送速度,由于每时每刻的发送速度都是传输协议根据接收方的接收能力不断调整的,好比经过数据链路层或者传输层的滑动窗口技术等流量控制机制进行速度的调整。
如表2-1所示,这台主机使用了独享10M带宽,也就是10Mbit/s的数据发送速度,换算成字节的话,正好就是上面的1250.0kBytes/s。在这种状况下,若是路由器的出口带宽为100M,交换机的设置应该保证来自广播域内其余主机的数据发送速度总和不超过90Mbit/s,以保证该主机任什么时候刻均可以以10Mbit/s的速度发送数据,这才叫独享带宽,它独享的是路由器的一部分出口带宽,而不是交换机的带宽,由于交换机原本就是各个端口独享带宽而互不影响。若是这台交换机还为其余主机提供独享10M带宽的接入,而且路由器的出口带宽为100M,那么理论上总共只能接入10台主机,这样才能够保证每台主机的实际带宽老是10M。假设带宽运营商为了多赚钱,给该交换机上接入了20台主机,而后对每台交换机仍然都限制了10M带宽,这时候从MRTG的Max Speed属性上仍然看到的是1250.0 kBytes/s,可是你能够观察MRTG流量图或者使用Nmon实时流量监控来分析本身的10M带宽是否真的有所保证。
不管是大文件下载,仍是网页、图片、样式表的下载,其下载速度都是站点用户最关心的指标,也是用户惟一能体验到的站点性能,因此若是能估算出各地用户的下载速度,并根据它来决策服务器的位置和带宽,是很是有意义的。在一般状况下,咱们很清楚也很容易计算传输的数据量大小,因此只有搞清楚响应时间的计算方法,才能够计算出下载速度。
经过前面的介绍,咱们了解了互联网上两台主机之间数据发送和传输的整个过程,事实上,数据的响应时间不可贵出:响应时间 = 发送时间 + 传播时间 + 处理时间
发送时间很容易计算,即“数据量/带宽”。好比要发送100Mbit的数据,并且发送速度为100Mbit/s,也就是带宽为100M,那么发送时间便为1s。值得注意的是,在两台主机之间每每存在多个交换节点,每次的数据转发,都须要花费发送时间,那么总的发送时间也包括这些转发时所花费的发送时间。
传播时间主要依赖于传播距离,由于传播速度咱们能够近似认为约等于2.0×108m/s,那么传播时间便等于传播距离除以传播速度。好比两个交换节点之间线路
好比两个交换节点之间线路的长度为1 000km,至关于北京到上海的直线距离,那么一个比特信号从线路的一端到另外一端的传播时间为0.005s。
处理时间是什么意思呢?其实在以前的介绍中,虽然没有提出这个概念,可是已经包含了对其本质的介绍。简单地说,处理时间就是指数据在交换节点中为存储转发而进行一些必要的处理所花费的时间,其中的重要组成部分就是数据在缓冲区队列中排队所花费的时间,注意,准确地说应该是“你的数据”在队列中排队所花费的时间,由于在队伍中还有其余与你绝不相干的数据。此时,若是你想起在介绍带宽的时候咱们提到的共享带宽,那就对了。
若是全世界只有你的服务器和你的用户在传输数据,那么用于排队的处理时间能够忽略。可见,处理时间的多少,取决于数据流经各交换节点所在网络的数据通讯量,它每每是不可预测的,因此它的计算比较复杂,每每没有一个简单的数学计算公式,而是依赖于多变的外部因素,必须结合实际状况具体分析。
那么,咱们能够将响应时间的计算公式整理为:
响应时间 = (数据量比特数 / 带宽) + (传播距离 / 传播速度) + 处理时间另外,下载速度的计算公式以下:
下载速度 = 数据量字节数 / 响应时间
有了以上的计算方法,下面咱们仍是在具体场景中试着来计算响应时间,注意,这里为了计算,咱们暂时先忽略处理时间。
咱们的站点用户毫不可能处于同一个互联网运营商的网络中,而事实上即便国内的互联网能够高速互联,若是咱们的站点用户覆盖全球,而且要保证高速服务,那么跨国运营商互联和国际出口带宽依然是残酷存在的问题,幸运的是这些问题均可以抽象为同类问题来考虑。
归根结底,但愿经过本节的介绍,可让你们清晰地认识到响应时间和下载速度的本质和计算方法,而至于究竟将服务器部署在哪里的问题彻底要经过你们本身的考察得出结论,好比选择IDC的时候要考察出口带宽以及与骨干网络是否直连,若是要同时为多个互联网运营商网络的用户提供服务,则须要考察出口节点与运营商互联节点的带宽,而若是要面向全球用户提供服务,则须要考察国际网络结构和各个国家的国际出口带宽等。另外一方面,带宽做为稀缺资源,其价格严格服从市场供求规律,好比一样的独享带宽在北京就比沈阳贵不少,因此咱们在选择的时候价格因素也至关重要。
将多个图片合并为一个文件,利用CSS背景图片的偏移技术呈如今网页中,避免了多个图片的下载。
合并JavaScript脚本或者CSS样式表。
充分利用HTTP中的浏览器端Cache策略,减小重复下载。
咱们知道,用脚本语言编写的程序文件须要经过相应的脚本解释器进行解释后生成中间代码,而后依托在解释器的运行环境中运行。因此生成中间代码的这部分时间又成为你们为获取性能提高而瞄准的一个目标,对于一些拥有较强商业支持的脚本语言,好比ASP.NET和JSP,均有内置的优化方案,好比解释器对某个脚本程序第一次解释的时候,将中间代码缓存起来,以供下次直接使用。
对于开源类的脚本语言,也有不少第三方组件来提供此类功能,好比PHP的APC组件等。使用这些组件进行脚本优化真的那么有用吗?不一样的应用效果是否有所不一样呢?
动态内容技术就像Web开发领域的一场工业革命,它带来了产业升级和Web开发者的地位提高,在过去至关长一段时间里,你们广泛认为一个站点的技术含量主要体如今后台的动态程序上,因此不少工程师都会带着虚荣心警告你:“请叫我后台开发工程师。”事实上这种概念和偏见已经开始逐渐被历史抛弃,但这不是咱们此刻讨论的重点。
自动态内容技术产生后,聪明的工程师们为了减小动态内容的重复计算,想到了截取动态内容的胜利果实,将动态内容的HTML输出结果缓存起来,在随后的一段时间内当有用户访问时便跳太重复的动态内容计算而直接输出。
在实际应用中,动态内容缓存多是你们使用得最多的技术,可是并不见得全部的动态内容都适合使用网页缓存,缓存带来的性能提高偏偏与有些动态数据实时交互的需求造成矛盾,这是很是尴尬的,而解决该问题的惟一途径不是技术自己,而是你如何权衡。
另外一方面,缓存的实现还涉及了一系列很是现实的问题,即成千上万的缓存文件如何存储?缓存的命中率如何?缓存的过时策略如何设计?在拥有多台Web服务器的分布式站点上应用动态内容缓存须要考虑什么呢?
动态内容缓存是将数据和表现总体打包,一步到位,但就像快餐店里的组合套餐同样,有时候未必彻底合乎咱们的口味。当咱们意识到在本身的站点中,某些动态内容的计算时间其实主要消耗在一些烦人的特殊数据上,这些数据或者更新过于频繁,或者消耗大量的I/O等待时间,好比对关系数据库中某字段的频繁更新和读取,这时咱们为了提升缓存的灵活性和命中率,以及性能的要求,便开始考虑数据缓存。
更加细粒度的数据缓存避免了过时时大量相关网页的总体更新,好比不少动态内容都包含了一段公用的数据,若是咱们将整个页面所有缓存,那么假如这段数据频繁更新致使频繁过时,无疑会使得全部网页都要频繁地重建缓存,这对网页的其余部份内容彷佛很不公平。此时如何协调网页缓存和数据缓存呢?是否可以将它们一块儿使用并各显其能呢?
另外,将数据缓存存储在哪里呢?这须要考虑多方面的因素。速度是一方面,若是没法提供高速的读写访问,那么这部分数据缓存可能不久便成为新的系统瓶颈。另外,数据缓存的共享也相当重要,如同一主机上不一样进程间的共享、网络上不一样主机间的共享等,一旦设计不当,将对站点将来的规模扩展带来致命的威胁。
在动态内容缓存技术的实现机制中,虽然避免了可观的重复计算,可是每次还都须要调用动态脚本解释器来判断缓存是否过时以及读取缓存,这彷佛有些多余,并且关键是消耗了很多时间。直接让浏览器访问这些动态内容的缓存不是更好吗?在这种状况下缓存成为直接暴露给前端的HTML网页,而整个缓存控制机制也发生了根本的变化,咱们广泛称它为静态化,静态网页独立了,当家作主了,不再用被脚本解释器呼来唤去。
从20世纪末开始影响全球经济的开源软件,不能否认给咱们的生活带来了更多丰富的体验和选择,可是更多的选择也表明着更多的结局,不论结局是好是坏,咱们都须要为此承担责任。
在Web服务器软件的选择问题上,不少架构师依然困惑,大量的压力测试对比数据蛊惑着激进的开发者和运维工程师,人们只关注所谓的并发量冠军,却忽视了更加本质性的东西,甚至不了解眼前测试数据的潜在前提。社会老是这样的,象牙塔式的精英教育和残酷的淘汰机制断送了无数人才的将来。而这一次,错误的选择将要付出沉痛的代价。有人拿着所谓的测试数听说Apache已通过时,你相信吗?
也许下此结论为时尚早,尽管放弃它的人比比皆是,可是它的成功不是空穴来风,毕竟它已经活了好久了。
另外一方面,你正在使用的Web服务器软件也许让你无比自豪,但是你知道那些复杂的参数配置背后的本质吗?你知道为何它仅仅在处理你的站点请求时如此出色吗?若是让你本身编写Web服务器软件,你可让它更快一些吗?
咱们必须中止盲目的选择,中止对表面现象的崇拜,咱们须要学习一些稍显底层的知识来武装本身。
从某种角度看,中学校园里的快慢分班彷佛合乎逻辑,虽然不必定合乎情理。快班的学生学习能力强,理解知识快,那么课程安排的节奏能够加快一些;慢班的学生则能够放缓课程安排的节奏,这样既互不影响,学校的升学率又能够获得保证,固然假设的前提是学生之间互相帮助效果不大。
在Web站点中,网页和各类各样的组件是否也须要“分班”呢?显然它们的下载量和对服务器的能力要求不尽相同,若是由同一台物理服务器或者同一种并发策略的Web服务器软件来统一提供服务,那势必形成计算资源的浪费以及并发策略的低效。因此,分离带来的好处是显而易见的,那就是能够根据不一样组件的需求,好比下载量、文件大小、对服务器各类资源的需求等,有针对性地采用不一样的并发策略,而且提供最佳的物理资源。固然,若是你的站点基本无人问津,并且服务器的各类资源大量闲置,那么天然不存在什么性能问题,也不须要什么组件分离。可是若是你的站点负载已经让你意识到组件分离是大势所趋,那仍是趁早动手。
真让人发疯,互联网为何不是只有一个,你也许会说难道不是只有一个Internet吗?是的,可是Internet所特指的“互联网”是某种文化意义上的名词,同一个世界,同一个梦想,同一个互联网。而我这里说的互联网,则是指由某互联网运营商负责搭建的一系列网络节点,它覆盖的地域有大有小,接入这些网络节点的局域网也能够相互通讯,同时这些互联网之间也可以经过骨干线路互联互通。
世界上不少国家都有不止一个互联网运营商,中国的互联网运营商想必你们都很是熟悉,当你在家中安装宽带或者须要托管服务器的时候,都面临着运营商选择的问题,包括电信、网通、铁通、移动在内的几大国内运营商让你很头疼。特别是在部署Web站点各种服务器的时候,是否可以找到合理的位置部署服务器相当重要。
咱们都知道,在基于IP寻址的互联网中,IP地址相近的主机之间通讯,数据通过少数的路由器便可到达,好比同一局域网内通讯或者接入同一个城市交换节点的局域网之间通讯,在这种状况下数据到达时间相对较短。
而若是通讯的两端主机位于不一样运营商的互联网中,那么数据必须流经两个互联网运营商的顶级交换节点和骨干线路,在这个过程当中可想而知数据要通过更屡次的存储转发,并且各互联网顶级交换节点之间又存在出口带宽的限制,若是互联网之间数据通讯量比较大的话,那么这个顶级交换节点,也就是“出口”,将会是瓶颈所在,就像链接两座城市之间的高速公路,当大量汽车须要频繁地往返于两座城市时,高速公路出现车流缓慢,那么汽车从一个城市到另外一个城市的整体时间加长了。
显而易见,咱们固然但愿Web站点的用户和服务器位于同一个互联网运营商的网络内,但如何实现呢?
到此为止,咱们已经最大程度地发挥了单台Web服务器的处理能力,可是,当它所承受的压力达到极限时,就须要有更多的服务器来分担工做,咱们须要想办法将流量合理转移到更多的服务器上。
为此,咱们须要经过各类不一样的方法来实现Web负载均衡,多是简单的HTTP重定向,或者是基于DNS的轮询解析,或者经过反向代理服务器来实现负载均衡调度,还能够经过LVS来组建服务器集群,它们有什么区别呢?不管如何,透过这些具体的实现方法,咱们更加关心的是可否真正地均衡调度请求,以及是否具有高可用性,还有影响规模扩展的制约因素.
对于使用数据库的Web站点来讲,你是否在性能优化时或多或少地忽视了数据库的存在?每每一些性能问题可能都发生在表现不佳的数据访问层面,这来源于不合理的应用程序数据访问组件设计、不合理的数据库表结构设计以及对于数据库内部构造缺少深刻的了解。绝不夸张地说,也许你以前的优化全都白干了。
Web服务器与数据库服务器的数据通讯通常基于标准的TCP,即使它们位于同一台物理主机也是如此。其通讯链接的创建和释放涉及表明一段内核高速缓冲区的文件描述符的建立和销毁,这须要很多的时间开销,包括系统调用致使的内核态切换以及某些异步阻塞I/O模型采用的文件描述符队列扫描机制。因此,频繁的数据库链接和释放无疑将致使数据访问等待时间的加长,这段时间浪费得毫无心义。
使用数据库持久链接有效地解决了这一难题,它包括不一样程度上的持久化,本质的区别在于持久链接的应用范围和生命周期,好比某个进程内部的全局数据库链接,供进程内全部计算任务共享,在这个进程终止后便被释放;或者在某个动态内容的执行周期内,代码层面的持久链接对象,在动态内容计算结束后便不复存在;还有跨进程的数据库链接池,保存多个持久链接供应用程序重复使用。在这些采用数据库持久链接的应用设计中,同时还要注意保证数据访问的线程安全性。
与此同时,在设计关系数据库的表结构时,你是否合理使用了各类类型的索引呢?要作到这一点,你必须了解索引的有关知识,然而更重要的是如何根据Web站点变幻莫测的数据访问特色来有针对性地设计每一个表的索引,这每每也是最有难度的,索引的合理使用对于依赖数据库访问的Web应用相当重要。
另外,你了解数据库存储引擎的特性吗?其实这并不困难,由于全部的主流数据库文档中都有详细介绍,可是究竟你的Web站点应该选择什么存储引擎呢?固然,没有绝对完美的方案,咱们在这个世界上要作的惟一的事情就是不断进行取舍,像考虑索引同样去弄清楚存储引擎的本质,是绝对不会让你失望的。
随着时间的推移,你的Web站点可能逐渐被数据库绑架,单台数据库服务器再也没法应付整个站点的须要,这包括存储空间以及查询时间,人们开始抱怨数据库模型的不良设计制约了横向扩展以及负载均衡,这不是咱们但愿看到的结果。为此,咱们将数据散列在多台主机,包括必要的冗余数据,以此来合理地分散数据库的密集访问,数据库扩展便成为咱们考虑的方案。
对于Web站点的可扩展性讨论已经家常便饭了,不管是代码层面的扩展,仍是架构层面的扩展,涉及的内容很是多,究竟咱们应该从何谈起呢?这是一个值得深思的问题。缺少良好的可扩展性设计就像慢性自杀或者等待死亡,这甚至比Web站点所能遇到的其余一切困难更让人头疼,由于扩展对于咱们来讲,就像在山穷水尽的时候被指引了一条星光大道,一旦扩展都没法进行,那真是死路一条。
的确,可扩展性并非性能和速度的概念,它是指当系统负载增大时,经过增长资源来提升性能的能力。高性能每每须要经过这种能力来实现快速扩展,几乎没有多少团队能够在一个星期内经过增长服务器立刻让服务能力扩容100倍。另外一方面,可扩展性的目的在于适应负载的变化,从扩展的技术实现上来看,又包含了不少对局部性能的思考,以及了解什么时候须要扩展,这离不开对站点性能的把握。
然而,就像“人的病都是吃出来的”同样,Web站点在成长的道路上不断吸取新的技术,然而每一次技术的应用不当,均可能引入必定程度上的不可扩展。但现实每每是矛盾的,咱们不得不使用一些技术来构建Web站点,同时又使用一些技术来提高站点性能,这些技术构成了咱们理想架构的一部分,关键在于在这些技术和架构的应用中,咱们是否意识到可扩展性,而且可否正确评估可扩展性的需求。
实在不行就给用户一些提示吧!最后我只能这么说了,事实上,这不是什么大不了的事情,即便认识到架构的瓶颈并投入大量人力来改善,也不是一天两天就能够完成的,要意识到用户也许只是但愿你不要不理他而已。
这部分显然已经超出了本书的讨论范围,它涉及人机交互的相关知识,而且充满着人文情怀,要真正作好它,恐怕要比本书中全部的问题都更有难度和挑战性,这绝不夸张,咱们要认可这个现实,由于世界上最难的学问就是研究人,你以为呢?
引用自《构建高性能web站点》