一、性能优化概述
1、首先需要了解我们当前系统的结构和瓶颈,了解当前使用的是什么,运行的是什么业务,都有哪些服务,了解每个服务最大能支撑多少并发。比如nginx作为静态资源服务并发是多少,最高瓶颈在哪里,能支持多少qps(每秒查询率)的访问请求,那我们怎么得出这组系统结构瓶颈呢,比如top查看系统的CPU负载、内存使用率、总得运行进程等,也可以通过日志去分析请求的情况,当然也可以通过我们前面介绍到的stub_status模块查看当前的连接情况,也可以对线上的业务进行压力测试(低峰期),去了解当前这套系统能承担多少的请求和并发,已做好响应的评估。这个是我们做性能优化最先考虑的地方。
2、其次我们需要了解业务模式,虽然我们是做性能优化,但每一个性能的优化都是为业务所提供的服务的,我们需要了解每个业务接口的类型,比如:电商网站中的抢购模式,这种情况下面,平时没什么流量,但到了抢购时间流量会突增。
我们还需要了解系统层次化的结构,比如:我们使用nginx做的是代理、还是动静分离、还是后端直接服务用户,那么这个就需要我们对每一层做好相应的梳理。以便更好的服务业务。
3、最后我们需要考虑性能与安全,往往注重了性能,但是忽略了安全。往往过于注重安全,对性能又会产生影响。比如:我们在设计防火墙功能时,检测过于严密,这样就会给性能带来影响。那么如果对于性能完全追求,却不顾服务的安全,这个也会造成很大的隐患,所以需要评估好两者的关系,把握好两者的孰重孰轻。以及整体的相关性,权衡好对应的点。
从哪些方面入手进行优化
OSI 七层模型:物理层,数据链路层,网络层,传输层,会话层,表示层,应用层
了解影响性能的指标
1)网络
a.网络流畅
b.网络是否丢包
c.这些会影响http的请求与调用
2)系统
a.硬件有没有损坏。磁盘速率
b.系统的负载,内存,系统稳定性
3)服务
a.连接优化,请求优化
b.根据业务形态做对应的服务设置
4)程序
a.接口性能
b.处理速度
c.程序执行效率
5)数据库
❤每个服务于服务之间都或多或少有一些关联,我们需要将整个架构进行分层,找到对应的系统或服务的短板,然后进行优化。
二、压力测试工具实战
在系统业务量没有增长之前,我们就要做好相应的准备工作,以防患业务量突增带来的接口压力,所以对于接口压力测试就显得非常重要了,我们首先要评估好系统压力,然后使用工具检测当前系统情况,是否能满足对应压力的需求。
1)安装 压力测试工具
[root@lb02 ~]# yum -y install httpd-tools
2)了解压测工具使用方式
[root@lb02 ~]# ab -n 2000 -c 2000 http://www.uyino.xyz/
#-n 总的请求次数哦
#-c并发请求数
#-k 是否开启长连接
3)使用ab工具进行压力测试
[root@lb02 ~]# ab -n 2000 -c 2000 http://www.uyino.xyz/ This is ApacheBench, Version 2.3 <$Revision: 1430300 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking www.uyino.xyz (be patient) socket: Too many open files (24) [root@lb02 ~]# ab -n 2000 -c 20 http://www.uyino.xyz/ This is ApacheBench, Version 2.3 <$Revision: 1430300 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking www.uyino.xyz (be patient) Completed 200 requests Completed 400 requests Completed 600 requests Completed 800 requests Completed 1000 requests Completed 1200 requests Completed 1400 requests Completed 1600 requests Completed 1800 requests Completed 2000 requests Finished 2000 requests Server Software: nginx/1.16.1 Server Hostname: www.uyino.xyz Server Port: 80 Document Path: / Document Length: 13433 bytes Concurrency Level: 20 Time taken for tests: 78.272 seconds #总花费总时长 Complete requests: 2000 #总的请求数 Failed requests: 0 #请求失败数 Write errors: 0 Total transferred: 27442000 bytes #总的传输大小 HTML transferred: 26866000 bytes #页面传输大小 Requests per second: 25.55 [#/sec] (mean) #每秒多少请求/s(总请求/总共完成的时间) Time per request: 782.717 [ms] (mean) #客户端访问服务器,单个请求所需花费的时间 Time per request: 39.136 [ms] (mean, across all concurrent requests) #服务器处理请求的时间 Transfer rate: 342.38 [Kbytes/sec] received #判断网络传输速率,观察网络是否存在瓶颈 Connection Times (ms) min mean[+/-sd] median max Connect: 26 67 479.4 31 9042 Processing: 73 712 136.0 724 1692 Waiting: 64 633 107.3 655 1035 Total: 103 779 493.1 757 9810 Percentage of the requests served within a certain time (ms) 50% 757 66% 785 75% 804 80% 818 90% 850 95% 892 98% 1064 99% 1483 100% 9810 (longest request)
三、系统性能优化
文件句柄,linux一切皆文件,文件句柄可以理解为就是一个索引,文件句柄会随着我们进程的调用频繁增加,系统默认文件句柄是有限制的, 一个进程无限的调用,所以我们需要限制每隔进程和每个服务使用多大的为文件句柄,文件句柄也是必须要调整的优化参数。
文件句柄的设置方式
1)系统全局性修改
2)用户局部性修改
3)进程局部性修改
[root@lb01 ~]# vim /etc/security/limits.conf
1、系统全局性修改。
# * 代表所有用户
* soft nofile 25535 * hard nofile 25535
2.用户局部性修改
#针对root用户,soft仅提醒,hard限制,nofile打开最大文件数
root soft nofile 65535 root hard nofile 65535
3.进程局部性修改
#针对nginx进程,nginx自带配置
worker_rlimit_nofile 30000
4.调整内核参数:让time_wait状态重用(端口重用)[flag]
[root@web01 ROOT]# vim /etc/sysctl.conf net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭 net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭 net.ipv4.tcp_fin_timeout = 60 表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间(可改为30,一般来说FIN-WAIT-2的连接也极少) [root@web01 ROOT]# sysctl -p #可以查看我们添加的内核参数 [root@web01 ROOT]# sysctl -a #可以查看所有内核参数
net.ipv4.tcp_timestamps = 1 该参数控制RFC 1323 时间戳与窗口缩放选项; net.ipv4.tcp_sack = 1 选择性应答(SACK)是 TCP 的一项可选特性,可以提高某些网络中所有可用带宽的使用效率; net.ipv4.tcp_fack = 1 打开FACK(Forward ACK) 拥塞避免和快速重传功能; net.ipv4.tcp_retrans_collapse = 1 打开重传重组包功能,为0的时候关闭重传重组包功能; net.ipv4.tcp_syn_retries = 5 对于一个新建连接,内核要发送多少个SYN 连接请求才决定放弃; net.ipv4.tcp_synack_retries = 5 tcp_synack_retries显示或设定Linux在回应SYN要求时尝试多少次重新发送初始SYN,ACK封包后才决定放弃; net.ipv4.tcp_max_orphans = 131072 系统所能处理不属于任何进程的TCP sockets最大数量; net.ipv4.tcp_max_tw_buckets = 5000 系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息; 默认为180000,设为较小数值此项参数可以控制TIME_WAIT套接字的最大数量,避免服务器被大量的TIME_WAIT套接字拖死; net.ipv4.tcp_keepalive_time = 30 net.ipv4.tcp_keepalive_probes = 3 net.ipv4.tcp_keepalive_intvl = 3 如果某个TCP连接在空闲30秒后,内核才发起probe(探查); 如果probe 3次(每次3秒既tcp_keepalive_intvl值)不成功,内核才彻底放弃,认为该连接已失效; net.ipv4.tcp_retries1 = 3 放弃回应一个TCP 连接请求前﹐需要进行多少次重试; net.ipv4.tcp_retries2 = 15 在丢弃激活(已建立通讯状况)的TCP连接之前﹐需要进行多少次重试; net.ipv4.tcp_fin_timeout = 30 表示如果套接字由本端要求关闭,这个参数决定了它保持在 FIN-WAIT-2状态的时间; net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭; net.ipv4.tcp_max_syn_backlog = 8192 表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数; net.ipv4.tcp_syncookies = 1 TCP建立连接的 3 次握手过程中,当服务端收到最初的 SYN 请求时,会检查应用程序的syn_backlog队列是否已满,启用syncookie,可以解决超高并发时的Can’t Connect问题。但是会导致 TIME_WAIT 状态fallback为保持2MSL时间,高峰期时会导致客户端无可复用连接而无法连接服务器; net.ipv4.tcp_orphan_retries = 0 关闭TCP连接之前重试多少次; net.ipv4.tcp_mem = 178368 237824 356736 net.ipv4.tcp_mem[0]: 低于此值,TCP没有内存压力; net.ipv4.tcp_mem[1]: 在此值下,进入内存压力阶段; net.ipv4.tcp_mem[2]: 高于此值,TCP拒绝分配socket; net.ipv4.tcp_tw_reuse = 1 表示开启重用,允许将TIME-WAIT sockets重新用于新的TCP连接; net.ipv4.ip_local_port_range = 1024 65000 表示用于向外连接的端口范围; net.ipv4.ip_conntrack_max = 655360 在内核内存中netfilter可以同时处理的“任务”(连接跟踪条目); net.ipv4.icmp_ignore_bogus_error_responses = 1 开启恶意icmp错误消息保护; net.ipv4.tcp_syncookies = 1 开启SYN洪水攻击保护。
四、代理服务优化
为什么我们要关注这个高并发短连接呢?有俩个方面需要注意:
a.高并发可以让服务器在短时间范围内同时占用大量端口,而端口有0~65535的范围,并不是很多,刨除系统和其他服务要用的,剩下的就更少了,
b.在这个场景中,短连接表示“业务处理+传输数据的时间 远远小于TIMEWAIT超时的时间”的连接。
这里有一个相对长的概念,比如取一个web页面,1秒钟的http短连接处理完业务,在关闭连接之后,这个业务用过的端口会停留在TIMEWAIT状态几分钟,而这几分钟,其他http请求来临的时候是无法占用此端口的(占着茅坑不拉屎)。单用这个业务计算服务的利用率会发现,服务器干正经事的时间和端口(资源)被挂着无法被使用的时间的比例是1:几百,服务器资源严重浪费。(说个题外话,从这个意义出发来考虑服务器性能优化的话,长连接对应的业务并发量不会很高。)
通常nginx作为代理服务,负责转发用户的请求,那么在转发的过程中建议开启http长连接,用于减少握手次数,降低服务器损耗。
配置nginx代理服务使用长连接方式
upstream tomcat { server 127.0.0.1:8080; keepalive 16; #开启长连接 } server { listen 80; server_name daili.linux.com; location /tomcat/ { proxy_pass http://tomcat; proxy_http_version 1.1; #指定http协议为1.1版本 proxy_set_header Connection ""; #清除“connection”头字段 include proxy_params; #调用优化配置 proxy_next_upstream error timeout http_500 http_502 http_503 http_504; #平滑过渡到另一台机器 } }
代理到php服务保持长连接
upstream php_conn { server 127.0.0.1:9000; keepalive 16; } server { listen 80; server_name php.linux.com; location /php/ { root /code; fastcgi_pass php_conn; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; #开启长连接 fastcgi_keep_conn on; #长连接超时时间 fastcgi_connect_timeout 60s; include fastcgi_params; } }
五、静态资源优化
Nginx作为静态资源Web服务器,用于静态资源处理,传输非常的高效
静态资源指的是非WEB服务器端运行处理而生成的文件
静态资源类型 | 种类 |
浏览器渲染 | HTML、CSS、JS |
图片文件 | JPEG、GIF、PNG |
视频文件 | FLV、Mp4、AVI |
其他文件 | TXT、DOC、PDF |
1.静态资源缓存
浏览器缓存设置用于提高网站性能,尤其是新闻网站,图片一旦发布,改动的可能是非常小的,所以我们希望能否用户访问一次后,图片缓存在用户的浏览器长时间缓存。 浏览器是有自己的缓存机制,他是基于HTTP协议缓存机制来实现的,在HTTP协议中有很多头信息,那么实现浏览器的缓存就需要依赖特殊的头信息来与服务器进行特殊的验证,如Expires(http/1.0);Cache-control(http/1.1)。
ETag: "5e6864a0-c" Last-Modified: Wed, 11 Mar 2020 04:10:08 GMT Cache-Control: max-age=315360000 Expires: Wed, 02 Jan 2030 12:14:50 GMT If-Modified-Since: Wed, 11 Mar 2020 04:10:08 GMT If-None-Match: "5e6864a0-c"
2.浏览器读取缓存流程
1.浏览器请求服务器会先进行Expires、Cache-Control的检查,检查缓存是否过期,如果没有过期则直接从缓存文件中读取。
2.如果缓存过期首先检查是否存在etag,如果存在那么客户端会带着If-None-Match向web服务器请求,与etag值进行比对,如果值一致则走缓存,如果不一致则不走缓存。
3.如果etag不存在,则进行last-Modified检查,客户端会带着if-Modified-Since向web服务器请求,与last-Modified进行比对,如果值一致则走缓存,如果不一致则不走缓存。
Last-Modified:服务器上文件的最后修改时间
Etag:服务器上的文件标识
Expires:本地缓存目录中,文件过期的时间
Cache-control:本地缓存目录中,文件过期的时间
3.配置静态资源缓存场景
[root@web01 static]# vim /etc/nginx/conf.d/huancun.conf server { listen 80; server_name static.linux.com; root /code/static; location ~* \.(jpg|png|gif)$ { expires 7d; } }
4.如果开发代码没有正式上线,希望静态文件不被缓存
取消js、css、html等静态文件缓存
location ~ .*\.(js|css|html)$ { add_header Cache-Control no-store; add_header Pragma no-cache; }
5.静态资源读取
高效传输(传输效率高)
将多个数据带个包,一次推送,大文件适合打开此配置,必须要打开sendfile
Syntax: tcp_nopush on | off; Default: tcp_nopush off; Context: http, server, location
长连接传输(传输实时性高)
来一个数据就推一个数据,需要开启keepalive
Syntax: tcp_nodelay on | off; Default: tcpnodelay off; Context: http, server, location
6.静态资源压缩
nginx将响应报文发送至客户端之前启用压缩功能,然后进行传输,这能够有效地节约带宽,并提高响应至客户端的速度
1)gzip传输压缩,传输前压缩,传输后解压
Syntax: gzip on | off;
Default: gzip off;
Context: http, server, location, if in location
2)gzip压缩哪些文件
Syntax: gzip_types mime-type …;
Default: gzip_types text/html;
Context: http, server, location
3)gzip压缩比率,加快传输,但压缩本身比较消耗服务性能
参数项:gzip_comp_level(压缩比)
参数值:1-9的正整数,1最低,压缩时间短,9最高,压缩时间长,吃cpu,但是压缩效果好,实质是用cpu换流量,视情况而用。
默认值:1
作用域:http,server,location
Syntax: gzip_comp_level level;
Default:gzip_comp_level level 1;
Context: http, server, location
4)gzip压缩协议版本,压缩使用http那个协议,主流选择1.1版本
Syntax: gzip_http_version 1.0 | 1.1;
Default:gzip_http_version 1.1;
Context: http, server, location
5)静态文件压缩案例
[root@web01 conf.d]# vim gzip.conf server { listen 80; server_name gzip.linux.com; root /code/static; location ~* \.(jpg|png|gif)$ { gzip on; gzip_types image/gif image/jpeg image/png; gzip_comp_level 5; gzip_http_version 1.1; } location ~* \.(css|js|txt)$ { gzip on; gzip_types text/css application/javascript text/plain; gzip_comp_level 5; gzip_http_version 1.1; } }
六、防资源盗链
防盗链,指的是防止资源被其他网站恶意盗用。
基础防盗链设置思路:主要是针对客户端请求过程中所携带的一些header信息来验证请求的合法性,比如客户端在 请求的过程中都会携带referer信息。优点是规则简单,配置和使用都很方便,缺点是防盗链所依赖的referer验证信息是可以伪造的,所以通过referer信息防盗链并非是100%可靠,但是他能够限制大部分的盗链情况。
location ~* \.(jpg|png|gif)$ { valid_referers none blocked www.lixian.fun; if ($invalid_referer) { return 500; #直接返回错误 #rewrite (.*) /2.jpg; #跳转到另一张图片 } }
七、允许跨域访问
什么是跨域访问,当我们通过浏览器访问a网站时,同事会利用到ajax或其他方式,同时也请求b网站,这样的话就出现了请求一个页面,使用了两个域名,这种方式对浏览器来说默认是禁止的。
案例
<html lang="en"> <head> <meta charset="UTF-8" /> <title>测试ajax和跨域访问</title> <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script> </head> <script type="text/javascript"> $(document).ready(function(){ $.ajax({ type: "GET", url: "http://beikuayu.linux.com", success: function(data) { alert("sucess 成功了!!!"); }, error: function() { alert("fail!!,跨不过去啊!"); } }); }); </script> <body> <h1>测试跨域访问</h1> </body> </html>
配置允许跨域访问
server { listen 80; server_name beikuayu.linux.com; root /code; index index.html; location ~* \.html$ { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS; } }
八、CPU亲和
CPU亲和(affinity)减少进程之间不断频繁切换,减少性能损耗,其实现原理是建CPU核心和Nginx工作进程绑定方式,把每个worker进程固定到对应的cpu上执行,减少切换CPU的cache miss,获得更好的性能。
查看CPU状态
[root@web02 ~]# lscpu Thread(s) per core: 1 CPU(s): 4 On-line CPU(s) list: 0-3 NUMA node0 CPU(s): 0-3 #一个物理CPU上面有四个内核,分别是 0 1 2 3
配置cpu亲和
[root@web02 ~]# vim /etc/nginx/nginx.conf user www; worker_processes auto; worker_cpu_affinity auto;
九、nginx通用优化配置
[root@nginx ~]# cat nginx.conf user www; #nginx用户 worker_processes auto; #自动根据CPU核心数启动worker数量 worker_cpu_affinity auto; #开启亲和性 error_log /var/log/nginx/error.log warn; #错误日志 pid /run/nginx.pid; #指定pid文件 worker_rlimit_nofile 35535; #nginx最大打开文件句柄数 events { use epoll; #使用epoll网络模型 worker_connections 10240; #每个worker处理的连接数 } http { include mime.types; #nginx可是别的文件类型 default_type application/octet-stream; #nginx不认识的文件类型就默认下载 charset utf-8; #定义日志格式 log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; #定义json格式日志 log_format json_access '{"@timestamp":"$time_iso8601",' '"host":"$server_addr",' '"clientip":"$remote_addr",' '"size":$body_bytes_sent,' '"responsetime":$request_time,' '"upstreamtime":"$upstream_response_time",' '"upstreamhost":"$upstream_addr",' '"http_host":"$host",' '"url":"$uri",' '"domain":"$host",' '"xff":"$http_x_forwarded_for",' '"referer":"$http_referer",' '"status":"$status"}'; access_log /var/log/nginx/access.log main; #访问日志 server_tokens off; #关闭显示nginx版本 client_max_body_size 200m; #文件上传大小限制 sendfile on; #高效传输 tcp_nopush on; #高效传输 tcp_nodelay on; #实时传输 keepalive_timeout 65; 长连接 gzip on; #开启压缩 gzip_disable "MSIE [1-6]\."; #针对IE浏览器不进行压缩 gzip_http_version 1.1; #压缩传输版本 gzip_comp_level 2; #压缩的等级 gzip_buffers 16 8k; #压缩的缓冲区 gzip_min_length 1024; #最小1024字节才开始压缩 gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript image/jpeg; #压缩的文件类型 include /etc/nginx/conf.d/*.conf; #虚拟主机配置文件 }
十、nginx安全优化总结
1、CPU亲和、worker进程数、调整nginx进程打开的文件句柄数 2、使用Epool网络模型、调整每个worker进程的最大连接数 3、文件的高效读取sendfile、nopush 4、文件的传输实时性、nodealy 5、开启tcp长连接,以及长连接超时时间keepalived 6、开启文件传输压缩gzip 7、开启静态文件expires缓存 8、异常nginx版本号 9、禁止通过ip地址访问,禁止恶意域名解析,只允许域名访问 10、配置防盗链、以及跨域访问 11、防DDOS、cc攻击,限制单IP并发连接,以及http请求 12、优雅限制nginx错误页面 13、nginx加密传输https优化 14、nginx proxy_cache、fastcgi_cache、uwsgi_cache 缓存,第三方工具(squid、varnish)