目录

动静分离

所谓的动静分离,就是将 Web 应用程序中静态和动态的内容分别放在不同的 Web 服务器上,有针对性的处理动态和静态内容,从而达到性能的提升。我们知道如果一个 HTML 有多个域名请求数据文件会提高
Tomcat 服务器在处理静态和并发问题上比较弱,所以事先动静分离的方式一般会用 Apache+Tomcat、Nginx+Tomcat 等。

以 Apache+Tomcat 为例,其运行机理是:页面请求首先给 Apache,然后 Apache 分析请求信息是静态还是动态,静态则本机处理,动态则交给 Tomcat 做处理这其实是负载均衡的雏形,这样的实现不用让开发人员做任何特殊开发,一个<img src="demo.jpg">交给服务器即可,至于这个文件是从 Apache 还是从 Tomcat 取得,开发人员完全无需关注。

HTTP 持久连接

持久连接(Keep-Alive)也叫做长连接,它是一种 TCP 的连接方式,连接会被浏览器和服务器所缓存,在下次连接同一服务器时,缓存的连接被重新使用。HTTP 无状态性表示了它不属于长连接,但 HTTP/1.1 提供了对长连接的支持(不过这必须依赖浏览器和服务器双方均支持长连接功能才行),最常见的 HTTP 长连接例子是“断点下载”。
浏览器在请求的头部添加 Connection:Keep-Alive,以此告诉服务器“我支持长连接,你支持的话就和我建立长连接吧”,而倘若服务器的确支持长连接,那么就在响应头部添加“Connection:Keep-Alive”,从而告诉浏览器“我的确也支持,那我们建立长连接吧”。服务器还可以通过 Keep-Alive:timeout=..., max=...的头部告诉浏览器长连接失效时间。
配置长连接通常是要服务器支持设置,有测试数据显示,使用长连接和不使用长连接的性能对比,对于 Tomcat 配置的 maxKeepAliveRequests 为 50 来说,效率竟然提升了将近 5 倍。

GZIP 压缩技术

HTTP协议支持GZIP的压缩格式,当服务器返回的HTML信息报头中包含Content-Encoding:gzip,它就告诉浏览器,这个响应的返回数据已经压缩成GZIP格式,浏览器获得数据后要进行解压缩操作,一定程度上减轻了服务器传输数据的压力。
很多服务器已经支持通过配置来自动将HTML信息压缩成GZIP,比如tomcat、又比如很火的Nginx。如果无法配置服务器级别的GZIP压缩机制,可以改为程序压缩。

HTTP 协议的合理使用

浏览器缓存带来的性能提升已经众人皆知了,而很多人却并不知道浏览器的缓存过期时间、缓存删除、什么页面可以缓存等,都可以由我们程序员来控制,只要您熟悉 HTTP 协议,就可以轻松的控制浏览器。

扩展阅读:深入理解 HTTP 协议

CDN 机制

    所谓的CDN,就是一种内容分发网络,它采用智能路由和流量管理技术,及时发现能够给访问者提供最快响应的加速节点,并将访问者的请求导向到该加速节点,由该加速节点提供内容服务。
    通俗点说,你在成都(浏览器)购买了北京卖家(服务器)的产品,北京卖家通过快递(CDN服务)寄送包裹,从北京到成都可以走路、坐汽车、火车或飞机,而采用CND的快递会选择飞机直达,因为这种寄送方式最快。

当然使用 CDN 有两个注意事项:
1、CDN 加速服务很贵,如果你觉得你的网站值得加速,可以选择购买;
2、CDN 不适合局域性网站,比如你的网站只有某一个片区访问或者局域网访问,因为区域性网络本来就很近,无需 CDN 加速。

懒加载与预加载

    预加载和懒加载,是一种改善用户体验的策略,它实际上并不能提高程序性能,但是却可以明显改善用户体验或减轻服务器压力。
    预加载表示当前用户在请求到需要的数据之后,页面自动预加载下一次用户可能要看的数据,这样用户下一次操作的时候就立刻呈现,依次类推。
    懒加载表示用户请求什么再显示什么,如果一个请求要响应的时间非常长,就不推荐懒加载。

渐进式增强设计

    渐进式增强设计的通俗解释就是:首先写一段满足所有浏览器的基本样式,再在后面针对不同高级浏览器编写更漂亮的样式
    如下代码,所有浏览器都支持background-color: #2067f5;满足了浏览器基本现实需求,而后面的background-image: -webkit-gradient等则为不同高级浏览器使用,只要浏览器识别就能执行这段代码(不识别,CSS也不会报错只会直接忽略)。

避免空链接属性

    如`<img src=""><a href="">`这样的设置方式是非常不可取的,即使链接为空,在旧的浏览器也会以固定步骤发送请求信息。
    另外`<a href="#"></a>`也不可取,最好的方式是在链接中加一个空的js代码`<a href="javascript:void();"></a>`

    # web访问流程

    用户输入网站域名

    通过DNS解析,找到目标服务器IP

    向ip地址发起请求,数据经互联网达到目标服务器

    目标服务器收到请求数据,进行处理(执行程序、访问数据库、文件服务器等)

    处理完成,将响应数据又经互联网返回给用户浏览器

    浏览器得到结果进行计算渲染显示给用户

    # DNS 解析优化

    从这个过程我们可以看到,优化的地方主要是减少DNS解析次数,而如果用户浏览器设置了缓存,则再第二次访问相同域名的时候就不会去请求DNS服务器,直接用缓存中的IP地址发出请求。

    因此这个过程主要取决于浏览器的设置。现在主流的浏览器默认设置了DNS的预取功能(DNS Prefetch),当然你也可以主动告知浏览器我的网站需要做DNS预取:

    `<meta http-equiv="x-dns-prefetch-control" content="on" />`

    # 数据传输的优化

    CDN 部署

    增加网站服务器上传宽带

    # 处理上的优化

    ##缓存

    根据需要使用本地缓存或分布式缓存

    ##使用异步操作

    这种方式不仅可以提高性能,也提高了系统的扩展性

    使用同步请求的方式,在高并发的情况下,会对数据库造成很大的压力,也会让用户感觉响应时间过长。

    异步请求方式,则可以快速的对用户做出响应,而具体的数据库操作请求,则通过消息队列服务器发送给数据库服务器,做具体的插入操作。插入操作的结果则已其他方式通知客户端。

    例如一般在订票系统当中,出票行为就是异步完成,最终的出票结果会以邮件或其他方式告知用户

    ##代码优化

    这里就不在详细描述,另一篇随笔《怎样编写高质量的java代码》对代码质量和风格做过大致的介绍,有兴趣可以看一下。

    ##存储优化

    大型网站中海量的数据读写对磁盘造成很大压力,系统最大的瓶颈还是在磁盘的读写。可以考虑使用磁盘阵列、分布式储存来改善存储的性能。

    性能的指标和测试

    ## 响应时间

    就是用户发出请求到收到响应数据的时间

    ## 并发量

    同时访问系统的请求的最高值

    ## 吞吐量

    单位时间内访问系统的总数
    # 渲染优化

    浏览器解析响应数据;浏览器创建DOM树;浏览器下载CSS样式,并应用到DOM树,进行渲染;浏览器下载JS文件,开始解析执行;显示给用户。

    首先我们可以尽量控制页面大小,使得浏览器解析的时间更短;

    并且将多个CSS文件、JS文件文件合并压缩减少文件下载的次数和大小;

    另外注意将CSS放在页面前面,JS访问页面后面,这样便于页面首先能渲染出来,再执行js脚本,对于用户来说有更好的体验。

    最后我还可以设置浏览器缓存,下次访问时从缓存读取内容,减少http请求。

    `<meta http-equiv="Cache-Control" content="max-age=5" />`

    该代码说明了浏览器启用了缓存并在5秒内不会再次访问服务器。注意缓存的设置需要结合你的业务特性来适当配
    # 前端性能其实是网速相关的
    说说前端的性能问题吧。有不少同学对前端性能很在意,事实上,我猜想可能和服务端关注性能有关系。服务端因为一台服务器要服务多个客户端,存在并发的问题,IO很可能成为一个瓶颈,比如数据库、读写文件,另外,量起来了,语言的快慢也会开始出现瓶颈。

    在我看来,前端重视性能,很可能在一定意义上受了服务端工程师的影响。但其实前端第一不存在并发问题,不存在横向扩展和纵向扩展问题,第二不存在大数据问题,前端对性能的需求其实并不是那么重要。把精力花在这上面,事实上并没有明显的价值。

    以我的工作经验来看,pc端性能基本可以完全无视,而移动端性能确实要接受很大的挑战,但这个挑战其实是webview层面的,不是前端工程师们可以解决的,当然一些小技巧还是有的,比如transform启动硬件加速,但最最核心最最重要的部分却完全无能为力,只能等移动os发发力,而我一等就是等了5年,从10年喊出web app以来,我一直等到今天,仍然对webview的性能表示无语。也就是说,要么性能你完全遇不到问题,要么你基本无能为力。

    一个领域除外——游戏。无论选用dom、canvas2d还是webgl,无论是pc还是移动,游戏开发都会对性能格外有要求,我只有在开发游戏时,在pc上遇到过性能问题。而移动端的性能更是把我虐怕了,游戏选型都不敢放开。只有在开发游戏时,我对性能才不敢掉以轻心,也真的需要一些性能方面的技巧,以立竿见影,比如减少对象的创建与销毁改用资源池,比如使用事件代理,比如减少canvas遍历所有sprite,将一部分图片缓存到一个看不到的canvas节点上。。。但现实情况是,游戏开发在前端的应用领域里是极小众的,绝大多数人接触不到这些。

    那么我们常说的前端性能,真实特别有意义的是什么?其实是网速相关的,比如减少http请求数,css sprite、web font图标、图片压缩、js和css压缩、合并、gzip、cdn、离线缓存什么的,这个方向的性能优化是有必要有价值的。而类服务端那样,在语言、框架层面的性能优化,甚至包括dom操作层面的性能优化,绝大多数应用场景下,都是没有什么意义的,如果因为做这种基本上看不出来效果的性能优化而在代码的可维护性上做了妥协,就更傻了。