10.Nginx性能优化

徐亮伟, 江湖人称标杆徐。多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。擅长Web集群架构与自动化运维,曾负责国内某大型电商运维工作。
个人博客"徐亮伟架构师之路"累计受益数万人。

老男孩Linux云计算运维QQ交流群: 384467551 226199307
老男孩教育官网 http://www.oldboyedu.com

1.性能优化概述

在做性能优化前, 我们需要对如下进行考虑

  • 1.当前系统结构瓶颈
    • 观察指标
    • 压力测试
  • 2.了解业务模式
    • 接口业务类型
    • 系统层次化结构
  • 3.性能与安全
    • 性能好安全弱
    • 安全好性能低

1.压力测试工具实战

1.安装压力测试工具ab

[root@nginx-lua ~]# yum install httpd-tools -y

2.了解压测工具使用方式

[root@nginx-lua ~]# ab -n 200 -c 2 http://127.0.0.1/

//-n总的请求次数
//-c并发请求数
//-k是否开启长连接

3.配置Nginx静态网站与tomcat动态网站环境

[root@nginx-lua conf.d]# cat jsp.conf 
server {
        server_name localhost;
        listen 80;
        location / {
                root /soft/code;
                try_files $uri @java_page;
                index index.jsp index.html;
        }
        location @java_page{
                proxy_pass http://192.168.56.20:8080;
        }
}

//分别给Nginx准备静态网站
[root@nginx-lua ~]# cat /soft/code/bgx.html 
<h1> Ab Load </h1>
//给Tomcat准备静态网站文件
[root@tomcat-node1-20 ROOT]# cat /soft/tomcat-8080/webapps/ROOT/bgx.html 
<h1> Ab Load </h1>

4.使用ab工具进行压力测试

//进行压力测试
[root@Nginx conf.d]# ab -n2000 -c2  http://127.0.0.1/bgx.html
...
Server Software:        nginx/1.12.2
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /bgx.html
Document Length:        19 bytes

Concurrency Level:      200
# 总花费总时长
Time taken for tests:   1.013 seconds
# 总请求数
Complete requests:      2000
# 请求失败数
Failed requests:        0
Write errors:           0
Total transferred:      510000 bytes
HTML transferred:       38000 bytes
# 每秒多少请求/s(总请求出/总共完成的时间)
Requests per second:    9333.23 [#/sec] (mean)
# 客户端访问服务端, 单个请求所需花费的时间
Time per request:       101.315 [ms] (mean)
# 服务端处理请求的时间
Time per request:       0.507 [ms] (mean, across all concurrent requests)
# 判断网络传输速率, 观察网络是否存在瓶颈
Transfer rate:          491.58 [Kbytes/sec] received

5.将nginx下的bgx文件移走, 再次压测会由tomcat进行处理

Concurrency Level:      200
Time taken for tests:   1.028 seconds
Complete requests:      2000
Failed requests:        0
Write errors:           0
Total transferred:      510000 bytes
HTML transferred:       38000 bytes
Requests per second:    1945.09 [#/sec] (mean)
Time per request:       102.823 [ms] (mean)
Time per request:       0.514 [ms] (mean, across all concurrent requests)
Transfer rate:          484.37 [Kbytes/sec] received

2.了解影响性能指标

  • 1.网络
    • 网络的流量
    • 网络是否丢包
    • 这些会影响http的请求与调用
  • 2.系统
    • 硬件有没有磁盘损坏,磁盘速率
    • 系统负载、内存、系统稳定性
  • 3.服务
    • 连接优化、请求优化
    • 根据业务形态做对应的服务设置
  • 4.程序
    • 接口性能
    • 处理速度
    • 程序执行效率
  • 5.数据库

每个架构服务与服务之间都或多或少有一些关联, 我们需要将整个架构进行分层, 找到对应系统或服务的短板, 然后进行优化

2.系统性能优化

  • 文件句柄, Linux一切皆文件,文件句柄可以理解为就是一个索引
    • 文件句柄会随着我们进程的调用频繁增加
    • 系统默认对文件句柄有限制,不能让一个进程无限的调用
    • 需要限制每个进程和每个服务使用多大的文件句柄
    • 文件句柄是必须要调整的优化参数
  • 设置方式
    • 系统全局性修改
    • 用户局部性修改
    • 进程局部性修改
vim /etc/security/limits.conf
//针对root用户
root soft nofile 65535
root hard nofile 65535
//所有用户, 全局
* soft nofile 25535
* hard nofile 25535

//对于Nginx进程
worker_rlimit_nofile 45535;

//root用户 
//soft提醒
//hard限制 
//nofile文件数配置项
//65535最大大小

3.Nginx性能优化

Nginx作为静态资源Web服务器, 传输非常的高效, 常常用于静态资源处理, 请求, 动静分离(非服务器动态运行生成的文件属于静态资源)

Nginx静态解析的类型

静态资源类型 种类
浏览器端渲染 HTML、CSS、JS
图片 JPEG、GIF、PNG
视频 FLV、Mp4
文件 TXT、任意下载文件

Nginx静态资源传输延迟最小化

3.文件读入

1.文件读取高效sendfile

Syntax: sendfile on | off;
Default: sendfile off;
Context: http, server, location, if in location

2.提高网络传输效率nopush

Syntax: tcp_nopush on | off;
Default: tcp_nopush off;
Context: http, server, location

作用: sendfile开启情况下, 提高网络包的'传输效率'

2.与tcp_nopush之对应的配置tcp_nodelay

Syntax: tcp_nodelay on | off;
Default: tcp_nodelay on;
Context: http, server, location

作用: 在keepalive连接下,提高网络的传输'实时性'

4.文件压缩

Nginx将响应报文发送至客户端之前可以启用压缩功能,这能够有效地节约带宽,并提高响应至客户端的速度。

1.gzip压缩配置语法

Syntax: gzip on | off;
Default: gzip off;
Context: http, server, location, if in location

作用: 传输压缩

2.gzip压缩比率配置语法

Syntax: gzip_comp_level level;
Default: gzip_comp_level 1;
Context: http, server, location

作用: 压缩本身比较耗费服务端性能

3.gzip压缩协议版本

Syntax: gzip_http_version 1.0 | 1.1;
Default: gzip_http_version 1.1;
Context: http, server, location

作用: 压缩使用在http哪个协议, 主流版本1.1

4.扩展压缩模块

Syntax: gzip_static on | off | always;
Default: gzip_static off;
Context: http, server, location

作用: 预读gzip功能

5.图片压缩案例

[root@Nginx conf.d]# mkdir -p /soft/code/images
[root@Nginx conf.d]# cat static_server.conf 
server {
        listen 80;
        server_name 192.168.56.11;
        sendfile on;
        access_log /var/log/nginx/static_access.log main;

        location ~ .*\.(jpg|gif|png)$ {
                gzip on;
                gzip_http_version 1.1;
                gzip_comp_level 2;
                gzip_types text/plain application/json application/x-javascript application/css application/xml application/xml+rss text/javascript application/x-httpd-php image/jpeg image/gif image/png;
                root /soft/code/images;
        }
}

没有开启gzip图片压缩

启用gzip压缩图片后(由于图片之前压缩过, 所以压缩比率不太明显)

6.文件压缩案例

[root@Nginx conf.d]# mkdir -p /soft/code/doc
[root@Nginx conf.d]# cat static_server.conf 
server {
    listen 80;
    server_name 192.168.56.11;
    sendfile on;
    access_log /var/log/nginx/static_access.log main;
    location ~ .*\.(txt|xml)$ {
        gzip on;
        gzip_http_version 1.1;
        gzip_comp_level 1;
        gzip_types text/plain application/json application/x-javascript application/css application/xml application/xml+rss text/javascript application/x-httpd-php image/jpeg image/gif image/png;
        root /soft/code/doc;
    }
}

没有启用gzip文件压缩

启用gzip压缩文件

5.静态缓存

HTTP协议定义的缓存机制(如: Expires; Cache-control 等)

1.浏览器无缓存

浏览器请求->无缓存->请求WEB服务器->请求响应->呈现

2.浏览器有缓存

浏览器请求->有缓存->校验过期->是否有更新->呈现

校验是否过期 Expires HTTP1.0, Cache-Control(max-age) HTTP1.1
协议中Etag头信息校验 Etag ()
Last-Modified头信息校验 Last-Modified (具体时间)

1.缓存配置语法expires

Syntax: expires [modified] time;
expires epoch | max | off;
Default: expires off;
Context: http, server, location, if in location

作用: 添加Cache-Control Expires头

2.配置静态资源缓存

location ~ .*\.(js|css|html)$ {
    root /soft/code/js;
    expires      1h;
}

location ~ .*\.(jpg|gif|png)$ {
    root /soft/code/images;
    expires      7d;
}

3.开发代码没有正式上线时, 希望静态文件不被缓存

//取消js css html等静态文件缓存
location ~ .*\.(css|js|swf|json|mp4|htm|html)$ {
add_header Cache-Control no-store;
add_header Pragma no-cache;
}

阿里云缓存策略帮助手册
Nginx静态资源缓存

6.跨域访问

浏览器禁止跨域访问, 主要不安全, 容易出现CSRF攻击

Nginx跨域访问配置

Syntax: add_header name value [always];
Default: —
Context: http, server, location, if in location

Access-Control-Allow-Origin

1.准备html文件

//在www.xuliangwei.com网站添加跨越访问文件
[root@Nginx ~]# cat /soft/code/http_origin.html 
<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://kt.xuliangwei.com/index.html",
        success: function(data) {
                alert("sucess!!!");
        },
        error: function() {
                alert("fail!!,请刷新再试!");
        }
        });
});
</script>
        <body>
                <h1>测试跨域访问</h1>
        </body>
</html>

2.配置Nginx跨域访问

//运行www.xuliangwei.com域名跨域访问
[root@Nginx conf.d]# cat origin.conf 
server {
    listen 80;
    server_name kt.xuliangwei.com;
    sendfile on;
    access_log /var/log/nginx/kuayue.log main;
    location ~ .*\.(html|htm)$ {
        add_header Access-Control-Allow-Origin http://www.xuliangwei.com;
        add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
        root /soft/code;
    }
}

没启动header头部访问

启动header头部访问

7.防止盗链

盗链指的是在自己的界面展示不在自己服务器上的内容,通过技术手段获得他人服务器的资源地址,绕过别人资源展示页面,在自己页面向用户提供此内容,从而减轻自己服务器的负担,因为真实的空间和流量来自别人服务器

防盗链设置思路: 区别哪些请求是非正常用户请求

基于http_refer防盗链配置模块

Syntax: valid_referers none | blocked | server_names | string ...;
Default: —
Context: server, location

1.准备html文件

<html>
    <head>
        <meta charset="utf-8">
        <title>pachong<title>
    </head>
    <body style="background-color:red;">
    <img src="http://192.168.69.113/test.jpg">
    </body>
</html>

2.启动防盗链

//支持IP、域名、正则方式
location ~ .*\.(jpg|gif|png)$ {
valid_referers none blocked www.xuliangwei.com;
    if ($invalid_referer) {
        return 403;
    }
root /soft/code/images;
}

3.验证

//伪造协议头访问
[root@C-Server ~]# curl -e "http://www.baidu.com" -I http://192.168.69.113/test.jpg
HTTP/1.1 403 Forbidden
Server: nginx/1.12.2
Date: Tue, 17 Apr 2018 04:55:18 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive


//伪造协议头访问
[root@C-Server ~]# curl -e "http://www.xuliangwei.com" -I http://192.168.69.113/test.jpg
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Tue, 17 Apr 2018 04:55:27 GMT
Content-Type: image/jpeg
Content-Length: 174315
Last-Modified: Wed, 29 Nov 2017 03:16:08 GMT
Connection: keep-alive
ETag: "5a1e2678-2a8eb"
Expires: Tue, 17 Apr 2018 16:55:27 GMT
Cache-Control: max-age=43200
Accept-Ranges: bytes

8.CPU亲和

1.CPU亲和(affinity)减少进程之间不断频繁迁移, 减少性能损耗

原理: 将CPU核心和Nginx工作进程绑定方式,把每个worker进程固定在一个cpu上执行,减少切换cpu的cache miss,获得更好的性能。

1.查看当前CPU物理状态

[root@nginx ~]# lscpu |grep "CPU(s)"
CPU(s):                24
On-line CPU(s) list:   0-23
NUMA node0 CPU(s):     0,2,4,6,8,10,12,14,16,18,20,22
NUMA node1 CPU(s):     1,3,5,7,9,11,13,15,17,19,21,23

//2颗物理cpu,没颗cpu12核心, 总共24核心

2.将Nginx worker进程绑到不同的核心上

//启动多少worker进程, 官方建议和cpu核心一致, 第一种绑定组合方式
#worker_processes 24;
#worker_cpu_affinity 000000000001 000000000010 000000000100 000000001000 000000010000 000000100000 000001000000 000010000000 000100000000 001000000000 010000000000 10000000000;

//第二种方式
#worker_processes 2;
#worker_cpu_affinity 101010101010 010101010101;

//最佳方式绑定方式
worker_processes auto;
worker_cpu_affinity auto;

3.查看nginx worker进程绑定至对应cpu

ps -eo pid,args,psr|grep [n]ginx

4.Nginx优化总结

Nginx通用优化配置文件

[root@nginx ~]# cat nginx.conf
user nginx;
worker_processes auto;
worker_cpu_affinity auto;

error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
#调整至1w以上,负荷较高建议2-3w以上
worker_rlimit_nofile 35535;

events {
    use epoll;
#限制每个进程能处理多少个连接请求,10240x16
    worker_connections 10240;
}

http {
    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;
# 统一使用utf-8字符集
    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"';
    access_log  /var/log/nginx/access.log  main;

# Core module
    sendfile            on;
# 静态资源服务器建议打开
    tcp_nopush          on;
# 动态资源服务建议打开,需要打开keepalived
    tcp_nodelay         on;
    keepalive_timeout   65;

# Gzip module
    gzip on;
    gzip_disable "MSIE [1-6]\.";
    gzip_http_version 1.1;

# Virtal Server
    include /etc/nginx/conf.d/*.conf;
}

Nginx优化

1.gzip压缩
2.expires静态文件缓存
3.调整网络IO模型,调整Nginx worker进程的最大连接数
5.隐藏Nginx名称和版本号
6.配置防盗链,防止资源被盗用
7.禁止通过IP地址访问,禁止恶意域名解析,只允许域名访问
8.防DDOS、cc攻击, 限制单IP并发请求连接
9.配置错误页面,根据错误代码指定网页反馈用户
10.限制上传资源目录被程序访问,防止木马入侵系统
11.Nginx加密传输优化

5.LNMP架构优化

1.php-ini常见优化

# 打开php的安全模式,控制php执行危险函数, 默认是Off,改为On
sql.safe_mode = Off
# 关闭php头部信息, 隐藏版本号, 默认是On,该为Off
expose_php = On

#错误信息输出控制
display_error = Off
error_reporting = E_WARNING & E_ERROR
# 记录错误日志至后台, 方便追溯
log_errors = On
error_log = /var/log/php_error.log

# 时区调整,默认PRC, 建议调整为Asia/Shanghai
date.timezone = PRC

# 是否开启文件上传功能
file_uploads = On
# 文件上传的临时存放目录(如果不指定,使用系统默认的临时目录)
;upload_tmp_dir =

# 允许客户端单个POST请求发送的最大数据
post_max_size = 300M
# 允许单个请求上传的最大文件大小, 默认2M
upload_max_filesize = 300M
# 允许单个POST请求同时上传的最大文件数量
max_file_uploads = 20
# 每个脚本执行最大内存
memory_limit = 128M

2.php-ini优化配置如下

sql.safe_mode = Off
expose_php = Off
display_error = Off
error_reporting = E_WARNING & E_ERROR
log_errors = On
error_log = /var/log/php_error.log
post_max_size = 300M
upload_max_filesize = 300M
max_file_uploads = 20
memory_limit = 128M
allow_url_fopen = Off
date.timezone = Asia/Shanghai

2.php-fpm优化

PHP-FPM配置文件 4核16G、8核16G

[root@nginx ~]# cat /etc/php-fpm.d/www.conf
[global]
pid = /var/run/php-fpm.pid
#php-fpm程序错误日志
error_log = /var/log/php/php-fpm.log
log_level = warning
rlimit_files = 655350
events.mechanism = epoll

[www]
user = nginx
group = nginx
listen = 127.0.0.1:9000
listen.owner = www
listen.group = www
listen.mode = 0660
 
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 512
pm.start_servers = 10
pm.min_spare_servers = 10
pm.max_spare_servers = 30
pm.process_idle_timeout = 15s;
 
pm.max_requests = 2048

#php-www模块错误日志
php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/php/php-www.log
php_admin_flag[log_errors] = on

#php慢查询日志
request_slowlog_timeout = 5s
slowlog = /var/log/php/php-slow.log

PHP5-FPM配置详解释

[global]
#pid设置, 记录程序启动后pid
pid = /var/run/php-fpm.pid
#php-fpm程序启动错误日志路径
error_log = /soft/log/php/php-fpm_error.log
# 错误级别. 可用级别为: alert(必须立即处理),error(错误情况), warning(警告情况), notice(一般重要信息), debug(调试信息). 默认: notice.
log_level = warning
#设置文件打开描述符的rlimit限制.
rlimit_files = 65535
events.mechanism = epoll

#启动进程的用户和组
[www]
user = www
group = www

# fpm监听端口
listen = 127.0.0.1:9000
# unix socket设置选项,如果使用tcp方式访问,这里注释即可。
listen.owner = www
listen.group = www
# 允许访问FastCGI进程的IP,any不限制
listen.allowed_clients = 127.0.0.1

# pm设置动态调度
pm = dynamic
# 同一时刻最大的php-fpm子进程数量
pm.max_children = 200
# 动态方式下的起始php-fpm进程数量
pm.start_servers = 20
# 动态方式下服务器空闲时最小php-fpm进程数量
pm.min_spare_servers = 10
# 动态方式下服务器空闲时最大php-fpm进程数量
pm.max_spare_servers = 30
# 最大请求
pm.max_requests = 1024
pm.process_idle_timeout = 15s;

# FPM状态页面,用于监控php-fpm状态使用
pm.status_path = /status
# 错误日志
php_flag[display_errors] = off
php_admin_value[error_log] = /soft/log/php/php-www_error.log
php_admin_flag[log_errors] = on

# 配置php慢查询, 以及慢查询记录日志位置
request_slowlog_timeout = 5s
slowlog = /soft/log/php/php-slow.log

6.Nginx架构总结

基于Nginx中间件的架构

  • 1.了解需求(定义Nginx在服务体系中的角色)
    • 静态资源服务的功能设计
      • 类型分类(视频、图片、html)
      • 浏览器缓存
      • 防盗链
      • 流量限制
      • 防资源盗用
      • 压缩(压缩模式, 压缩比例, 压缩类型)
    • 代理服务
      • 协议类型
      • 正向代理
      • 反向代理
      • 负载均衡
      • 代理缓存
      • 头信息处理
      • Proxy_Pass
      • LNMP
      • 动静分离
  • 2.设计评估
    • 硬件 CPU、内存、磁盘
    • 系统(用户权限、日志目录存放)
    • 代理服务/负载均衡 (CPU、内存)
    • 静态资源服务(硬盘容量、硬盘转速)
    • 动态资源服务(硬盘转速、读写效率)
    • 缓存资源服务(SSD固态)
  • 3.配置注意事项
    • 合理配置
    • 了解原理
      • http协议原理
      • http状态原理
      • 操作系统原理
    • 关注日志
      • 日志是否有打开
      • 是否有对应请求
      • 请求状态码信息符合
      • 错误日志信息吐出来
      • 错误日志内容和含义