系统架构优化-从高性能考虑
1.1 网站性能测试
(1) 用户角度看提升性能:用户不管后面代码写的多么花哨,他直接体验的是网站的响应速度,需要的是一种流畅的使用体验。
对前端进行的性能优化是用户体验提升最大的,例如:利用浏览器的异步、并发调整页面缓存策略,合并一些资源;优化
html
、js
脚本;使用CDN
、反向代理等服务。总之使浏览器尽快展现用户感兴趣的内容、尽可能近的获取内容,即使不动架构用户体验提升也很明显。
(2) 开发人员角度提升性能:主要就是优化算法,降低复杂度;使用缓存加速数据读取;使用集群扛住更高并发;使用异步加快增加吞吐量、以及实现流量削峰。
(3) 运维人员角度提升性能:主要就是提升硬件性能;优化网络;虚拟技术提高资源利用率。
(4) 衡量性能的指标
- 并发量:同一时间,向机器发起的请求数量。
- 响应时间: 最重要的指标。指发出一个请求到收到回应这之间的时间,一般测试多次再除以测试次数得到单个请求响应时间。随着并发量提高响应时间先慢慢变长,到达临界点(机器资源以经最大化利用),然后急剧变长,最后系统崩溃变为无限长,完全无响应。
- 吞吐量:单位时间机器处理的数据量/请求量,随着并发量提高吞吐量先逐渐提高,到达临界点,慢慢下降,最后系统崩溃变为0;
(5) 网站的一般测试方法
- 性能测试:在未达到临界点前,根据并发数查看各项性能指标,看是否符合预期。
- 负载测试:不断增加的并发数观察各项性能,多次测量获得大致的最佳负载点。在这个负载点之后再增加压力,吞吐量会下降,响应时间急剧上升。
- 压力测试:不断增加并发,直到系统崩溃,多次测量得到大致的系统崩溃点。
(6) 性能优化方法
请求会经历很多个环节,出现性能问题时一般都需要通过日志、监控系统等排查、定位问题所在。有可能是代码问题,也有可能是架构问题,也有可能是系统资源确实不足、网络问题等外部原因。
优化一般有前端服务优化、应用服务优化、存储优化。
1.2 web前端的优化
(1) 浏览器访问优化
- 减少
http
请求,或者使用长连接,避免频繁的建立tcp
连接。 - 使用浏览器缓存,因为
css、js、logo
、图片是静态资源,很少发生变化,缓存在浏览器可以大幅减少资源。一般通过cache-control
的expires
属性设置浏览器缓存时间。当静态资源发生变化且需要及时应用到html
,则可以直接生成一个新文件让html
引用这个新文件,而不是去修改这个静态文件的内容。就算要更新静态文件时,也要避免批量更新而是一个一个更新,防止服务器负载骤增。 - 启用压缩,文本压缩率通常有80%以上,
css、js、html
使用Gzip
压缩效果很好。但是通信带宽充足,而服务器资源不足,就要谨慎使用压缩了,因为这会增加服务器的负载。 -
css
文件放在js
文件上面,浏览器在加载完全部css
后才会进行渲染,js
则是加载一个立即执行,可能会阻塞。因此为了尽快加载用户感兴趣的东西,css
要放在js
上面。 - 减少
cookie
传输从而降低对传输其他数据的影响,一个是减少cookie
数据量,让数据存到session
,而cookie
只放seesionid
;一个是减少发送cookie
的频率,比如请求静态资源,发送cookie
完全没有意义,可以考虑静态资源服务器单独拆出来,访问这个服务器的请求都无需cookie
。
(2) CDN加速
内容分发网络CDN
本质上还是一个缓存,专门缓存静态资源,区别是它不是缓存在浏览器上的,而是缓存在离用户最近的某个服务器上。CDN
服务一般都由网络运营商提供,这些运营商是用户的网络提供商,也就是用户请求到达的网络第一跳(一般就是离用户最近的服务器),如果购买了CDN
服务,请求第一跳就到达了CDN
服务器,直接把静态资源返回,响应极快。
当然,CDN
只适合存静态资源,且是那些访问频率高改动少的。动态资源必须请求源服务器,且缓存某一时间内可能与真实资源不一致。
(3) 反向代理
正向代理就是在浏览器侧放一个服务器转发请求,web
服务器只知道是这个代理发来的请求不知道背后是那个浏览器发来的,保护浏览器。反向代理则相反,在网站侧放一个代理服务器,浏览器只知道访问了这个代理不知道自己的请求真正是由谁处理的,反向代理服务器可以负载均衡、可以缓存静态资源、可以保护背后的web
服务器集群,好处多多。
1.3 应用服务器性能优化
相比web
前端主要处理静态资源,应用服务器处理网站业务,是最复杂的地方。一般的可选性能优化方法有缓存、集群、异步。
(1) 网站性能优化第一定律:优先考虑缓存
-
由于前端是静态资源,一般都是根据:
请求->静态资源
的方式进行缓存。后端主要是动态数据,通常每次请求的结果都不一样,不能根据请求->数据
设置缓存键值对,而是数据名->数据值
,这样自己设置缓存数据的名字,如curUser->{...}
这个键值对缓存当前登录用户信息。 -
缓存一般采用访问速度快的存储介质存储,采用
Hash
表的结构存储,通过对键进行哈希运算马上就可得到键值对存放的地址,取值是O(1)
的复杂度。网站访问一般遵循二八定律,八成的访问落在那两成的数据上,我们一般对这两成数据设置缓存即可,缓存的数据还要满足读多写少的特点。
不是什么地方都适合加缓存的,一般的,改动频繁的数据不适合缓存;冷数据,即访问频率低的数据不适合缓存;一致性要求高的数据不适合缓存;
- 缓存雪崩:缓存结点突然不可用(宕机、断网)、或者某一批缓存数据在很短的时间内集体失效,导致数据库的压力陡增,有可能压垮数据库。一般的解决办法就是不要将缓存的过期时间全部设为一样,而是要根据其访问热度合理设置过期时间。
- 缓存穿透:大并发环境或恶意攻击不停的请求缓存中某个不存在的
key
,这会把所有请求都转嫁到数据库上,有可能压垮数据库。简单有效的做法就是,在缓存中添上这个不存在的key
,其value
设为null
即可,但是其过期时间要设置短一些,几十秒即可。 - 缓存击穿:某一个
key
成为当红炸子鸡,极高的并发量访问,当这个key
突然失效时,极高的并发请求全突然落到数据库,有可能压垮数据库。一般这种情况较少,简单有效的办法就是先将这个key
设为永不过期,当后面情况有变这个key
不热门了再考虑删除。
(2) 异步操作
异步操作最直接的作用就是:解耦,削峰。一般通过fork
线程,消息队列完成异步操作。
- 解耦:使得两个模块之间的调用更灵活,可以加快请求的响应速度,例如用户注册本来先要将注册信息落库再调用邮件服务发送邮件,然后返回响应注册成功,解耦后注册信息落库后就可以返回注册成功的响应,邮件服务可以异步调用,晚一点发邮件完全无问题;还可以加快计算速度,一个任务通过异步的方式调用多个线程计算,速度更快。
- 流量削峰:归根结底实际也是解耦的好处,模块之间不是直接调用而是通过消息队列做中间人、缓冲区。短时间的极高并发请求首先写入消息队列,而不是直接冲击后台服务器,服务器承受的压力要小很多。
(3) 集群
使用一台负载均衡的服务器,反向代理,分发请求到具体的应用服务器,分担压力。可以显著提高网站各项性能指标。
(4) 代码优化
这个比较虚,优化点主要有多线程(tomcat、nginx
天然就是多线程处理请求,我们要注意线程安全问题,共享变量最好都是无状态的);算法复杂度;正确合适的数据结构。
1.4存储性能的优化
氪金上最好的固态,选择合适的存储结构。
1.5 建议
不要追求技术上的极致性能,那样是舍本逐末,没有任何意义,网站的终极目标是用户体验、业务需求。
性能提升往往意味着其他损失。如服务器的数量、维护带来的成本,数据一致性下降。扛住高并发或提升高并发下的响应时间,往往低并发时的响应时间比以前变长了。