计算机网络——HTTP的优化方式
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计算机网络——HTTP的优化方式相关的知识,希望对你有一定的参考价值。
摘要
对于HTTP的优化,对于很多小伙伴都想到的是使⽤ KeepAlive将 HTTP/1.1从短连接改成长连接。当然这的确是一个优化的方式。但其实还可以从其他⽅向来优化 HTTP/1.1 协议,本博文提供如有如下3 种优化思路:
- 尽避免发送 HTTP 请求。
- 在需要发送 HTTP 请求时,考虑如何减少请求次数。
- 减少服务器的 HTTP 响应的数据⼤小。
一、如何减少HTTP的请求
对于⼀些具有重复性的 HTTP 请求,⽐如每次请求得到的数据都⼀样的,我们可以把这对请求-响应的数据都缓存在本地,那么下次就直接读取本地的数据,不必在通过⽹络获取服务器的响应了,这样的话 HTTP/1.1 的性能肯定肉眼可⻅的提升。
所以,避免发送HTTP请求的⽅法就是通过缓存技术,HTTP 设计者早在之前就考虑到了这点,因此 HTTP 协议的 头部有不少是针对缓存的字段。
那缓存是如何做到的呢?
客户端会把第⼀次请求以及响应的数据保存在本地磁盘上,其中将请求的 URL 作为 key,⽽响应作为 value,两者 形成映射关系。这样当后续发起相同的请求时,就可以先在本地磁盘上通过 key 查到对应的 value,也就是响应,如果找到了,就 直接从本地读取该响应。毋庸置疑,读取本次磁盘的速度肯定⽐⽹络请求快得多,如下图:
万⼀缓存的响应不是最新的,⽽客户端并不知情,那么该怎么办呢?
这个问题 HTTP 设计者早已考虑到。 所以,服务器在发送 HTTP 响应时,会估算⼀个过期的时间,并把这个信息放到响应头部中,这样客户端在查看响 应头部的信息时,⼀旦发现缓存的响应是过期的,则就会᯿新发送⽹络请求。HTTP 关于缓说明会的头部字段很 多,这部分内容留在下次⽂章,这次暂时不具体说明。
如果客户端从第⼀次请求得到的响应头部中发现该响应过期了,客户端重新发送请求,假设服务器上的资源并没有 变更,还是⽼样⼦,那么你觉得还要在服务器的响应带上这个资源吗?
很显然不带的话,可以提⾼ HTTP 协议的性能,那具体如何做到呢?
只需要客户端在重新发送请求时,在请求的 Etag 头部带上第⼀次请求的响应头部中的摘要,这个摘要是唯⼀标 识响应的资源,当服务器收到请求后,会将本地资源的摘要与请求中的摘要做个⽐较。
如果不同,那么说明客户端的缓存已经没有价值,服务器在响应中带上最新的资源。
如果相同,说明客户端的缓存还是可以继续使⽤的,那么服务器仅返回不含有包体的 304Not Modified 响应, 告诉客户端仍然有效,这样就可以减少响应资源在⽹络中传输的延时,
缓存真的是性能优化的⼀把万能钥匙,⼩到 CPU Cache、Page Cache、Redis Cache,⼤到 HTTP 协议的缓存。
二、如何减少HTTP的请求的次数
减少 HTTP 请求次数⾃然也就提升了 HTTP 性能,可以从这 3 个方面入手: 减少重定向请求次数; 合并请求; 延迟发送请求;
2.1 减少重定向请求次数
服务器上的⼀个资源可能由于迁移、维护等原因从 url1 移⾄ url2 后,⽽客户端不知情,它还是继续请求 url1,这 时服务器不能粗暴地返回错误,⽽是通过 302 响应码和 Location 头部,告诉客户端该资源已经迁移⾄ url2 了,于是客户端需要再发送 url2 请求以获得服务器的资源。那么,如果᯿定向请求越多,那么客户端就要多次发起 HTTP 请求,每⼀次的 HTTP 请求都得经过⽹络,这⽆疑会 越降低⽹络性能。另外,服务端这⼀⽅往往不只有⼀台服务器,⽐如源服务器上⼀级是代理服务器,然后代理服务器才与客户端通 信,这时客户端᯿定向就会导致客户端与代理服务器之间需要 2 次消息传递,如下图:
如果重定向的⼯作交由代理服务器完成,就能减少 HTTP 请求次数了,⽽且当代理服务器知晓了᯿定向规则后,可以进⼀步减少消息传递次数,如下图:
2.2 合并请求
如果把多个访问⼩⽂件的请求合并成⼀个⼤的请求,虽然传输的总资源还是⼀样,但是减少请求,也就意味着减少 了重复发送的 HTTP 头部。
另外由于 HTTP/1.1 是请求响应模型,如果第⼀个发送的请求,未收到对应的响应,那么后续的请求就不会发送, 于是为了防⽌单个请求的阻塞,所以⼀般浏览器会同时发起 5-6 个请求,每⼀个请求都是不同的 TCP 连接,那么 如果合并了请求,也就会减少 TCP 连接的数量,因⽽省去了 TCP 握⼿和慢启动过程耗费的时间。
有的⽹⻚会含有很多⼩图⽚、⼩图标,有多少个⼩图⽚,客户端就要发起多少次请求。那么对于这些⼩图⽚,我们 可以考虑使⽤ CSS Image Sprites 技术把它们合成⼀个⼤图⽚,这样浏览器就可以⽤⼀次请求获得⼀个⼤图⽚, 然后再根据 CSS 数据把⼤图⽚切割成多张⼩图⽚。这种⽅式就是通过将多个⼩图⽚合并成⼀个⼤图⽚来减少 HTTP 请求的次数,以减少 HTTP 请求的次数,从⽽减 少⽹络的开销。
除了将⼩图⽚合并成⼤图⽚的⽅式,还有服务端使⽤ webpack 等打包⼯具将 js、css 等资源合并打包成⼤⽂ 件,也是能达到类似的效果。
另外,还可以将图⽚的⼆进制数据⽤ base64 编码后,以 URL 的形式潜⼊到 html ⽂件,跟随 HTML ⽂件⼀并 发送.
可以看到,合并请求的⽅式就是合并资源,以⼀个⼤资源的请求替换多个⼩资源的请求。 但是这样的合并请求会带来新的问题,当⼤资源中的某⼀个⼩资源发⽣变化后,客户端必须重新下载整个完整的⼤ 资源⽂件,这显然带来了额外的⽹络消耗。
2.3 延迟发送请求
不要⼀⼝⽓吃成⼤胖⼦,⼀般 HTML ⾥会含有很多 HTTP 的 URL,当前不需要的资源,我们没必要也获取过来, 于是可以通过「按需获取」的⽅式,来减少第⼀时间的 HTTP 请求次数。 请求⽹⻚的时候,没必要把全部资源都获取到,⽽是只获取当前⽤户所看到的⻚⾯资源,当⽤户向下滑动⻚⾯的时 候,再向服务器获取接下来的资源,这样就达到了延迟发送请求的效果。
三、如何减少HTTP响应的数据的大小
对于 HTTP 的请求和响应,通常 HTTP 的响应的数据⼤⼩会⽐较⼤,也就是服务器返回的资源会⽐较⼤。 于是,我们可以考虑对响应的资源进⾏压缩,这样就可以减少响应的数据⼤⼩,从⽽提⾼⽹络传输的效率。
压缩的⽅式⼀般分为 2 种,分别是: ⽆损压缩; 有损压缩;
3.1 无损压缩
⽆损压缩是指资源经过压缩后,信息不被破坏,还能完全恢复到压缩前的原样,适合⽤在⽂本⽂件、程序可执⾏⽂ 件、程序源代码。
⾸先,我们针对代码的语法规则进⾏压缩,因为通常代码⽂件都有很多换⾏符或者空格,这些是为了帮助程序员更 好的阅读,但是机器执⾏时并不要这些符,把这些多余的符号给去除掉。
接下来,就是⽆损压缩了,需要对原始资源建⽴统计模型,利⽤这个统计模型,将常出现的数据⽤较短的⼆进制⽐ 特序列表示,将不常出现的数据⽤较⻓的⼆进制⽐特序列表示,⽣成⼆进制⽐特序列⼀般是霍夫曼编码算法。
gzip 就是⽐较常⻅的⽆损压缩。客户端⽀持的压缩算法,会在 HTTP 请求中通过头部中的 Accept-Encoding 字 段告诉服务器:
Accept-Encoding: gzip, deflate, br
服务器收到后,会从中选择⼀个服务器⽀持的或者合适的压缩算法,然后使⽤此压缩算法对响应资源进⾏压缩,最 后通过响应头部中的 content-encoding 字段告诉客户端该资源使⽤的压缩算法。
content-encoding: gzip
gzip 的压缩效率相⽐Google 推出的 Brotli 算法还是差点意思,也就是上⽂中的 br,所以如果可以,服务器应该选 择压缩效率更⾼的 br 压缩算法。
3.2 有损压缩
与⽆损压缩相对的就是有损压缩,经过此⽅法压缩,解压的数据会与原始数据不同但是⾮常接近。
有损压缩主要将次要的数据舍弃,牺牲⼀些质ᰁ来减少数据ᰁ、提⾼压缩⽐,这种⽅法经常⽤于压缩多媒体数据, ⽐如⾳频、视频、图⽚。
可以通过 HTTP 请求头部中的 Accept 字段⾥的「 q 质量因⼦」,告诉服务器期望的资源质量。
关于⾳视频的压缩,⾳视频主要是动态的,每个帧都有时序的关系,通常时间连续的帧之间的变化是很⼩的。
⽐如,⼀个在看书的视频,画⾯通常只有⼈物的⼿和书桌上的书是会有变化的,⽽其他地⽅通常都是静态的,于是 只需要在⼀个静态的关键帧,使⽤增量数据来表达后续的帧,这样便减少了很多数据,提⾼了⽹络传输的性能。对 于视频常⻅的编码格式有 H264、H265 等,⾳频常⻅的编码格式有 AAC、AC3。
博文参考
《小林图解网络》
性能优化
前端性能优化
1、页面的HTTP请求数量
在建立HTTP连接时需要重新经历TCP协议握手,并在每次请求时需要包含相同的header和cookie,这就需要耗费网络带宽,因此可以采用合并样式和脚本文件的方式来减少请求数。
2、使用压缩
对前端样式文件与脚本文件可以采用压缩的方式去掉空格或者注释等不必要的字符,这样可以减少在网络中传输的字节数。但是压缩会消耗一定的CPU资源。
Java程序优化
1、单例模式
对于I/O处理、数据库连接、配置文件解析加载等一些非常耗费资源的操作,我们必须对这些实例的创建进行限制,或者始终使用一个公公用的实例依次来节约系统开销,在这种情况下可以使用单例模式。
2、Future模式
在执行一个任务需要花费大量的时间时,为了省去不必要的等待时间,可以使用Futrue,然后继续处理其他的任务,直到Futrue任务执行完毕后将结果返回,对结果进行处理。
3、线程池
在有某一个任务执行时需要耗费大量的时间,如果该任务可以分解执行,可以通过多线程的方式并行执行,并将执行后的任务结果合并。但是并发数不能太多,内存占用太多,因为这有可能会导致内存溢出。
4、降低锁竞争
锁的竞争会使得更多的线程竞争,本来并行的操作变为串行,并且会导致上下文频繁的切换,因此减少竞整是提高效率的一种方式,降低锁竞争的一种有效方式是尽可能的缩短锁的持有时间,例如可以将与锁无关的代码段移到锁之外。
另一种减少锁的持有时间的方式是将原先保护的多个变量变为多个相互独立的锁分别保护这几个变量。
class LikeCount{ private Integer likeApple=0; private Integer likePear=0; public synchronized void likePear(){ likePear++; } public synchronized void likeApple(){ likeApple++; } public synchronized void unlikePear(){ likePear--; } public synchronized void unlikeApple(){ likeApple--; }
}
将上述代码改为如下形式:
class LikeCount{ private Integer likeApple=0; private Integer likePear=0; public void likePear(){ synchronized (likePear){ likePear++; } } public void likeApple(){ synchronized(likeApple){ likeApple++; } public void unlikePear(){ synchronized (likePear){ likePear--; } } public void unlikeApple(){ synchronized (likeApple){ likeApple--; } } }
结果缓存
对于请求比较频繁并且每次都要对数据库进行查询,为了每次请求能够及时的获取数据,减少数据库的查询压力,可以将查询结果保存到缓存中,可以是redis或者memcached等。
数据库查询性能优化
1、合理使用索引
2、合理设计范式
3、使用查询缓存
4、使用搜索引擎
5、GC优化
以上是关于计算机网络——HTTP的优化方式的主要内容,如果未能解决你的问题,请参考以下文章