最近一个高并发的项目上线了,申请了阿里的https的免费证书,然后在上线的第一天发现页面加载非常缓慢,页面完全加载要50多秒;这是不能接受的。
于是开始了问题的排查;
网络架构如下
通过网络访问请求路径分析,请求从防火墙进入 ,过WAF防护系统(7层),过SLB(4层),到达WEB服务器(nginx),对应数据请求转发后端jar包服务器。
现在的现象是页面加载缓慢;值得怀疑的:waf,slb,web上的nginx配置,和页面文件资源。
作为运维肯定要研究下nginx配置是否针对优化好了,还有哪些可以做:
HTTP/2
和http/1.1相比http/2所具有的优势
每个服务器只用一个连接,节省多次建立连接的时间,在TLS上效果尤为明显;
加速 TLS 交付,HTTP/2 只耗时一次 TLS 握手,通过一个连接上的多路利用实现最佳性能;
更安全,通过减少 TLS 的性能损失,让更多应用使用 TLS,从而让用户信息更安全;
在 Akamai 的 HTTP/2 DEMO中,加载300张图片,HTTP/2 的优越性极大的显现了出来,在 HTTP/1.X 需要 14.8s 的操作中,HTTP/2 仅需不到1s。
使用HTTP/2 的前提条件
1、openssl的版本必须在1.0.2e及以上;
2、开启https加密,目前http2.0只支持开启了https的网站
3、nginx的版本必须在1.9.5以上,原因是nginx从1.9.5开始,已经用 http_v2_module 模块替换了 ngx_http_spdy_module
检查自己的nginx版本和OpenSSL版本(升级openssl版本不必安装新的openssl,稍后详解步骤)
/usr/local/nginx/sbin/nginx -V
nginx/1.17.9 版本大于 1.9.5达标了;~
openssl version -a
OpenSSL 版本大于 1.0.2 ;我的版本:OpenSSL 1.0.2k-fips不达标。
到nginx-1.17.9.tar.gz安装包的解压目录下
cd /usr/local/src/nginx-1.17.9/
查看上次编译参数中是否有with-http_v2_module和http_ssl_module
/usr/local/nginx/sbin/nginx -V
--prefix=/usr/local/nginx --with-http_dav_module --with-http_stub_status_module --with-http_addition_module --with-http_sub_module --with-http_flv_module --with-http_mp4_module --user=nginx --group=nginx
没有http_ssl_module模块和 with-http_v2_module模块,现在要在编译参数中添加他们重新编译,并且增加对openssl的引用
上传openssl-1.1.1a.tar.gz到/usr/local/src/nginx-1.17.9下并解压
tar zxf openssl-1.1.1a.tar.gz
./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-http_dav_module --with-http_stub_status_module --with-http_addition_module --with-http_sub_module --with-http_flv_module --with-http_v2_module --with-openssl=openssl-1.1.1a --with-http_mp4_module --user=nginx --group=nginx
make
千万不要makeinstall,那会覆盖掉你现在已经安装的nginx
备份/usr/local/nginx/sbin/nginx
cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx_back
ll /usr/local/nginx/sbin/nginx*
更换nginx文件,并验证
cp objs/nginx /usr/local/nginx/sbin/nginx
/usr/local/nginx/sbin/nginx -V
TLS 1.3
和 HTTP/1.x 一样,目前受到主流支持的 TLS 协议版本是 1.1 和 1.2,分别发布于 2006年和2008年,也都已经落后于时代的需求了。在2018年8月份,IETF终于宣布TLS 1.3规范正式发布了,标准规范(Standards Track)定义在 rfc8446。
TLS 1.3 相较之前版本的优化内容有:
握手时间:同等情况下,TLSv1.3 比 TLSv1.2 少一个 RTT
应用数据:在会话复用场景下,支持 0-RTT 发送应用数据
握手消息:从 ServerHello 之后都是密文。
会话复用机制:弃用了 Session ID 方式的会话复用,采用 PSK 机制的会话复用。
密钥算法:TLSv1.3 只支持 PFS (即完全前向安全)的密钥交换算法,禁用 RSA 这种密钥交换算法。对称密钥算法只采用 AEAD 类型的加密算法,禁用CBC 模式的 AES、RC4 算法。
密钥导出算法:TLSv1.3 使用新设计的叫做 HKDF 的算法,而 TLSv1.2 是使用PRF算法,稍后我们再来看看这两种算法的差别。
总结一下就是在更安全的基础上还做到了更快,目前 TLS 1.3 的重要实现是 OpenSSL 1.1.1 开始支持了,并且 1.1.1 还是一个 LTS 版本,未来的 RHEL8、Debian10 都将其作为主要支持版本。在 Nginx 上的实现需要 Nginx 1.13+。
Brotli
Brotli 是由 Google 于 2015 年 9 月推出的无损压缩算法,它通过用变种的 LZ77 算法,Huffman 编码和二阶文本建模进行数据压缩,是一种压缩比很高的压缩方法。
根据Google 发布的研究报告,Brotli 具有如下特点:
针对常见的 Web 资源内容,Brotli 的性能要比 Gzip 好 17-25%;
Brotli 压缩级别为 1 时,压缩速度是最快的,而且此时压缩率比 gzip 压缩等级为 9(最高)时还要高;
在处理不同 HTML 文档时,brotli 依然提供了非常高的压缩率;
在兼容 GZIP 的同时,相较 GZIP:
JavaScript 上缩小 14%
HTML上缩小 21%
CSS上缩小 17%
Brotli 的支持必须依赖 HTTPS,不过换句话说就是只有在 HTTPS 下才能实现 Brotli。
yum -y install git
cd /usr/local
git clone https://github.com/eustas/ngx_brotli.git
cd /usr/local/ngx_brotli
git submodule update --init
完整的nginx编译语句
./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-http_dav_module --with-http_stub_status_module --with-http_addition_module --with-http_sub_module --with-http_flv_module --with-http_v2_module --with-openssl=openssl-1.1.1a --with-openssl-opt='enable-tls1_3 enable-weak-ssl-ciphers' --with-http_gzip_static_module --with-http_mp4_module --add-module=/usr/local/ngx_brotli --user=nginx --group=nginx
--with-http_ssl_module
开启ssl模块
--with-http_v2_module
http/2.0模块
--with-openssl=openssl-1.1.1a
指定nginx ssl模块依赖的ssl库;只使用-with-http_ssl_modulel 表示启动nginx的 ssl模块 (依赖openssl库 如果没有使用with-openssl 指定源码位置(已经安装过存在configure文件) 则使用系统自带的openssl)
--with-openssl-opt='enable-tls1_3 enable-weak-ssl-ciphers'
在编译时为openssl设置附加参数,“enable-tls1_3” 表示支持 TLS1.3,“enable-weak-ssl-ciphers”表示支持弱安全等级的 Cipher Suite,只有需要继续支持 IE8 才考虑加入此项,如不需要可以删除。
--with-http_gzip_static_module
在搭建squid网页加速的时候,对于大的css 或者js要进行压缩,然后再进行缓存,这样能够提高减小下载量提高页面响应速度。如果你用的是squid 3.0以前的版本并且用的是 ngnix server的话可能会碰到如下问题: 不用squid直接打开页面则客户端返回的是压缩的状态,如果启用squid加速会发现下载下来的页面不是压缩状态。这里面主要是没有启动ngnix 的静态缓存模块(ngx_http_gzip_static_module)导致。
打开静态缓存问题就解决了
--add-module=/usr/local/ngx_brotli
参数语法:–add-dynamic-module=[模块源码所在目录的绝对路径]
http {
gzip on;
gzip_min_length 2k;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 7;
gzip_types text/plain application/javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png image/ico;
gzip_vary off;
gzip_disable "MSIE [1-6]\.";
brotli on;
brotli_comp_level 6;
brotli_buffers 16 8k;
brotli_min_length 20;
brotli_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml
proxy_headers_hash_max_size 51200;
proxy_headers_hash_bucket_size 6400;
proxy_buffer_size 128k;
proxy_buffers 32 128k;
proxy_busy_buffers_size 128k;
proxy_connect_timeout 600;
proxy_read_timeout 600;
proxy_send_timeout 600;
}
server {
listen 443 ssl http2;
server_name 666.com;
root /data/666/dist;
proxy_set_header Host $host;
proxy_set_header X-real-ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
ssl_certificate cert/666.com.pem;
ssl_certificate_key cert/666.com.key;
ssl_session_cache shared:SSL:100m; #nginx工作进程共享ssl会话缓存,1M可以存放约4000个sessions。缓存大小100m。
ssl_session_timeout 4h; #客户端可以重用会话缓存中ssl参数的过期时间,内网默认5分钟,可设30m即30分钟甚至4h。
#设置ssl/tls会话缓存的类型和大小。如果设置了这个参数一般是shared,buildin可能会参数内存碎片,默认是none,和off差不多,停用缓存
ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
#设置协商加密算法时,优先使用我们服务端的加密套件,而不是客户端浏览器的加密套件。
}
location ~ .*\.(js|json|css)$ {
gzip on;
gzip_static on;
gzip_min_length 1k;
gzip_http_version 1.1;
gzip_comp_level 9;
gzip_types text/css application/javascript application/json;
}
启用 TLS1.3 和 HTTP/2
listen 443 ssl http2 fastopen=3 reuseport;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;
service nginx restart
配合gzip_static配置需要对前端vue页面进行如下改造:
生产环境是不需要sourceMap的,如下配置可以去除
module.exports = {
//去除生产环境的productionSourceMap
productionSourceMap: false,
}
1、对资源文件进行压缩
需要下载 compression-webpack-plugin
cnpm i compression-webpack-plugin -D
vue.config.js 中按照如下方式进行配置:
const CompressionWebpackPlugin = require('compression-webpack-plugin')
module.exports = {
// 根据你的实际情况更改这里
publicPath,
assetsDir: 'assets',
lintOnSave: true,
configureWebpack: {
plugins:[
new CompressionWebpackPlugin({
filename: '[path].gz[query]',
algorithm: 'gzip',
// test: /\.js$|\.html$|\.json$|\.css/,
test: /\.js$|\.json$|\.css/,
threshold: 10240, // 只有大小大于该值的资源会被处理
minRatio: 0.8, // 只有压缩率小于这个值的资源才会被处理
// deleteOriginalAssets: true // 删除原文件
})
],
},
}
压缩后也会节省一部分空间,单后端要对nginx修改,配合前端
其他说明
gzip_static配置优先级高于gzip
开启nginx_static后,对于任何文件都会先查找是否有对应的gz文件
gzip_types设置对gzip_static无效
nginx配置示例:
location ~ .*\.(js|json|css)$ {
gzip on;
gzip_static on; # gzip_static是nginx对于静态文件的处理模块,该模块可以读取预先压缩的gz文件,这样可以减少每次请求进行gzip压缩的CPU资源消耗。
gzip_min_length 1k;
gzip_http_version 1.1;
gzip_comp_level 9;
gzip_types text/css application/javascript application/json;
root /dist;
}
修改配置文件后验证配置文件的正确定
/usr/local/nginx/sbin/nginx -t
service nginx restart
通过上述手段应该可以让 HTTPS 访问的体验优化不少,而且会比没做 HTTPS 的网站访问可能更快。
这样的模式比较适合云服务器单机或者简单集群上搭建,如果有应用 SLB 七层代理、WAF、CDN 这样的产品可能会让我们的这些操作都白费。 我们的这几项操作都是自建的 Web 七层服务,如果有设置 SLB 七层代理、WAF、CDN 这样设置在云服务器之前就会被覆盖掉。
由于 SLB 七层和CDN这样的产品会更加追求广泛的兼容性和稳定性并不会第一时间就用上上述的这些新特性(HTTP/2 是普遍有的),但是他们都配备了阿里云的 Tengine 的外部专用算法加速硬件如 Intel® QuickAssist Technology(QAT) 加速器可以显著提高SSL/TLS握手阶段性能。 所有 HTTPS 的加密解密都在 SLB 或 CDN 上完成,而不会落到ECS上,可以显著降低 ECS 的负载压力,并且提升访问体验。
目前云上的网络产品中能支持四层的都是可以继续兼容我们这套设计的,例如:SLB 的四层转发(TCP UDP)、DDOS高防的四层转发。
在做了这些优化后,我们对前端vue页面文件也进行了优化,同时让访问暂时跳过WAF系统(七层),直接到slb上。
页面加载时间控制在2~4秒。
参考资料
https://mp.weixin.qq.com/s/ZPF6rSJ9jjjCGXl99Q8sqQ
https://geekufo.com/hi-2269.html
https://segmentfault.com/a/1190000002866627
http://www.ttlsa.com/nginx/nginx-ngx_http_gzip_static_module/
https://blog.csdn.net/weixin_43638968/article/details/109093199