Nginx-包教包会-入门

前言

Nginx 作为 web 服务器 以低内存,高扩展,并且轻松单机支持 1-3w (据说可以单机 10w,但没有看到具体的机器配置)的并发链接的特性广受开发人员的青睐。

推荐在 linux 系统上使用 Nginx ,这会充分利用 linux 的特性,性能比在 windows 上会更好。

本文主要内容:

  • Nginx 简单配置
  • root 和 alias 的区别
  • location 的优先级及验证
  • Nginx 内置变量介绍
  • if
  • rewrite 转发
  • try_files
  • 配置 gzip
  • 协商缓存和强缓存的介绍和配置

后续章节,我会使用 ab 压测来一步一步优化 Nginx 的配置,Nginx 知道原理,懂得常用配置即可。有的性能优化需要了解 linux 内核 、http、tcp 相关的东西,如果你不想了解,可以记录一份自己的配置即可,不必纠结为什么。

本文内容在 nginx 1.16.1 上测试,Centos 7 4核 8g 内存的虚拟机。

Nginx 安装

Nginx 安装步骤

根据 阿里 CentOS 镜像 配置 yum 源,提高下载速度。

阿里 epel 镜像 配置我们常用软件的包,Nginx 也在其中。

<code># 运行一下命令,更新 yum 源
yum clean all
yum makecache/<code>

刷新 yum 仓库信息之后,运行以下命令就可以找到 nginx

<code>yum list | grep nginx/<code>

安装 nginx

<code>sudo yum install nginx/<code>

配置 nginx 开机启动

<code>sudo systemctl enable nginx/<code>

启动 nginx

<code>sudo systemctl start nginx/<code>

检查 nginx 是否启动

<code>sudo systemctl status nginx/<code>
Nginx-包教包会-入门

如果想查看 nginx包都安装了哪些文件,可以使用

<code>rpm -qvl nginx/<code>
Nginx-包教包会-入门

Nginx 命令

<code># 强制立即关闭,不建议做
nginx -s stop

# 正常关闭,会处理已经接到的请求,但不会接受新的请求
nginx -s quit

# 重新加载配置文件
nginx -s reload

# 重新打开日志文件
nginx -s reopen

# 检查配置文件是否有误
nginx -t

# 检查配置文件是否有误,会将配置文件内容打印
nginx -T

# 查看 nginx 版本
nginx -v

# 查看 nginx 版本和编译配置致残
nginx -V/<code>

系统开启、关闭、重启、查看 nginx 命令

<code>sudo systemctl enable nginx
sudo systemctl start nginx
sudo systemctl restart nginx
sudo systemctl stop nginx
sudo systemctl status nginx/<code>

Nginx 简单配置

Nginx 介绍

部署的 Nginx 使用一个 master 进程管理多个 worker 进程。master 进程不处理请求,提供管理服务,包括启动、停止、重载配置文件等服务,通常以 root 用户启动, worker 进程进行请求的处理,一般用非管理员账户启用,worker 进程数量和 cpu 核心设置一直,降低进程切换带来的 cpu 切换。

Nginx-包教包会-入门

http 上下文中的配置是我们重点需要知道的,其余的了解会配置即可。

Server 配置

<code>http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80 ;
server_name _;
root /usr/share/nginx/html;
location / {
}
}
}/<code>

Server 既配置一个服务。

listen 80 用于配置监听 80 端口的服务。

root 指定静态资源存放的位置。

location 进行资源匹配。location / {} 匹配所有的资源。

listen 和 server_name 配置

匹配规则:

  • 先匹配 listen 再匹配 server_name
  • server_name 匹配请求头中的 Host
  • 当都没有匹配成功,由配置default_server 的处理
  • 以上都没有匹配成功,由第一个配置处理
<code>server {
listen 9099 default_server;
server_name "localhost";

location / {
return 200 "server_name 为 localhost";

}
}
server {
listen 9099;
server_name 127.0.0.1;

location / {
return 200 "server_name 为 127.0.0.1";
}
}
server {
listen 9099;
server_name "localhost77";

location / {
return 200 "server_name 为 localhost77";
}
}/<code>

在 Postman中设置请求头 Host 模拟访问。

http://localhost:9099 Host:127.0.0.1 返回 server_name 为 127.0.0.1

http://localhost:9099 Host:localhost 返回 server_name 为 localhost

http://localhost:9099 Host:localhost77 返回 server_name 为 localhost77

http://localhost:9099 Host:localhost779 返回 server_name 为 localhost

再添加一个配置

<code>server {
listen localhost:9099 default_server;
server_name "localhost";

location / {
return 200 "server_name 为 localhost:9099";
}
}/<code>

当 listen 访问 localhost:9099 的时候,返回 server_name 为 localhost:9099 ,因为只有这一个匹配上了。

如果想禁止没有 Host 请求头的访问。

<code>server {
listen 80;
server_name "";
# 表示 nginx 会关闭连接
return 444;
}/<code>

return 配置

return 用于定义返回的状态码,或者内容。

介绍 return 主要是为了好描述 location 配置

- 说明 语法 return code [text];
return code URL;
return URL ; 默认 - 上下文 server、location、if

code 为状态码。text 为字符串。

<code>location /a {
default_type application/json;
return 200 "访问 9088/a";
}/<code>
<code># 重定向
location = /b {
return 301 http://www.baidu.com;
}/<code>

location 配置

location 用于匹配资源。

数字越小,优先级越高。

规则符号 描述 优先级 location = /a{} 精准完全匹配,匹配到之后 1 location ^~ /a{} 前缀匹配,匹配到之后 2 location ~ /a.*{} 正则匹配,区分大小写,检查到之后,还会检查有没有优先级跟高的 3 location ~* /a.* 正则匹配,不区分字母大小写,检查到之后,还会检查有没有优先级跟高的 4 location /a {} 也表示前缀匹配,但是优先级低于 正则匹配。 /a 和 ^~/a 会冲突,报错 5 location / {} 任何没有匹配成功的,都会匹配这里处理 6

<code>server {
listen 9088 default_server;
server_name _ ;

location = /a {
default_type application/json;
return 200 "= /a,优先级第一";
}
location ^~ /a {
default_type application/json;
return 200 "^~ /a 匹配 /a 开头的路径,优先级第二";
}

location ~ /a\\.* {
default_type application/json;
return 200 " /a\\.* 匹配 /a...路径,优先级第三";
}
location ~* /a\\.* {
default_type application/json;
return 200 "~* /a\\.* 匹配 /a...路径,优先级第四";
}
# /a 会和 ^~ /a 冲突
location /a/ {
default_type application/json;
return 200 "/a/ 匹配 /a/...路径,优先级第五";
}
}/<code>

访问 http://localhost:9088/a ,依次注释优先级较高的,可以验证这个规律。

还有一类特殊的 location 用于配置跳转的,以 @ 开头

<code>location @pass {

}/<code>

add_header 添加响应头

- 说明 语法 add_header name value [always]; 默认 - 上下文 http、server、location、location 中的 if

如果响应代码等于 200、201、204、206、301、302、303、304、307或 308,则将指定的字段添加到响应报头中。

加上 always 不管什么状态码都加上。

<code>location ~ /a\\.* {
default_type application/json;
add_header test1 "asdfasdf" always;
return 200 " /a\\.* 匹配 /a...路径,优先级第三";
}/<code>

error_page

- 说明 语法 error_page code …[=[response code]] uri; 默认 - 上下文 http、server、location、location 中的 if

配置错误状态码跳转页面。

<code>error_page 404 /404.html; 
error_page 500 502 503 504 /50x.html;/<code>

以上不会改变响应状态码。

<code># 改变响应状态吗。
error_page 404 =200 /404.html;/<code>
<code>server {
location / {
error_page 404 = @ops-coffee;
}

location @ops-coffee {
rewrite .* / permanent;
}
}/<code>

root 和 alias 的区别

alias 理解为:路径替换,location 以 / 结尾,alias 必须以 / 结尾。严格匹配。alias 替换掉 location 路径。

<code># /bieming/     替换  /usr/local/var/www/alias2/
# 访问 /bieming/1.jpg 去寻找 /usr/local/var/www/alias2/1.jpg
location /bieming/ {
alias /usr/local/var/www/alias2/;
}/<code>

root 理解为:root 路径加上 + location 路径。会将两个或更多个 / 压缩成一个。

<code># 当 location 和 root 路径的最后一部分匹配时,更好的方式是使用 root
## 以下配置都可以。
# 访问 /data2/1.jpg 去寻找 /usr/local/var/www/data2/1.jpg
location /data2/ {
root /usr/local/var/www;
}
location /data2/ {
root /usr/local/var/www/;
}
location /data2 {
root /usr/local/var/www;
}
location /data2 {
root /usr/local/var/www/;
}
location /data2/ {
root /usr/local/var/www////;
}/<code>

内置变量

通过内置变量,我们可以通过判断请求头、query string 等值来转发或者拒绝访问。

$arg_name 获取请求参数

获取请求query string 中的 name 参数。

<code>location /arg/ {
default_type application/json;
return 200 "$arg_q1";

}/<code>

/arg/a?q1=334 返回的内容为 334。

$args 获取请求 query_string 参数

<code>location /arg/ {
default_type application/json;
return 200 "$arg_q1 _ $args";
}/<code>

浏览器访问 /arg/a?q1=3334&aa=2&bb=33 返回的内容为 3334 _ q1=3334&aa=2&bb=33

$cookie_name 获取cookie的值

获取请求中的名称为 name 的 cookie。

$http_name 获取请求头

name 为请求头中的字段名称,请求头名称全部小写,并将破折号- 替换为下划线 _

$http_user_agent 获取请求头中的 User-Agent 字段。

$uri 获取请求路径中的 path

path 为端口后面的路径,不包括 query string。优化之后的路径,特殊字符转译及 / 压缩。

http://localhost:8888/arg/a?q1=q1canshu&bb=2323 中的 path 为 /arg/a

$host 获取请求的 ip

首先会获取请求头 Host ,如果没有请求头中没有 Host 请求头,那么获取的是 url 中的 ip。

$request_uri 获取 path 和 query string

访问 http://localhost:8888/arg/a/?q1=q1canshu&bb=2323

$request_uri 为/arg/a/?q1=q1canshu&bb=2323

$scheme 获取请求协议

值为 http 或 https

$request_method 获取请求方法

获得的值字母全大写。GET,POST,DELETE,PUT 等

其他变量

- 描述 $content_length 获取 Content-Length 请求头字段。 $content_type 获取 Content-Type 请求头字段 $https 如果连接以 SSL 模式运行,则为 on ,否则为空字符串 $is_args 如果请求行有参数则为 ? ,否则为空字符串 $pid 获取处理当前请求的 worker pid $nginx_version 获取 nginx 的版本

if

- 说明 语法 if (condition){} 默认 - 上下文 server、location

指定的 condition 求值之后,如果为 true ,则执行在大括号内指定的该模块的指令,并在 if 指令内为该请求分配配置。 if 指令内的配置继承自上一层的配置级别。

condition 可以是以下任何一种:

  • 变量名,如果变量的值为空字符串或 0 ,则为 false
  • 使用 = 和 != 运算符比较变量和字符串
  • 使用 ~ (区分大小写的匹配)和 ~* (不区分大小写的匹配)运算符,变量将与正则表达式进行匹配。正则表达式可以包含可供以后在 $1..$9 变量中重用的捕获。
  • 反操作符 !~ 和 !~* 也可用。如果正则表达式包含 } 或 ; 字符,则整个表达式应使用单引号 或双引号包围起来。
  • 使用 -f 和 !-f 运算符检查文件是否存在
  • 使用 -d 和 !-d 运算符检查目录是否存在
  • 使用 -e 和 !-e 运算符检查文件、目录或符号链接是否存在
  • 使用 -x 和 !-x 运算符检查是否为可执行文件

if 与小括号之间需要有空格

<code>location = /a {
default_type application/json;
if ($request_uri ~* "/(a).*") {
return 200 "正则表达式捕获的值:$1";
}
return 200 "= /a,优先级第一";
}/<code>

rewrite

- 说明 语法 rewrite regex replacement [flag]; 默认 - 上下文 server、location、if

flag 可选参数:

  • last

停止匹配,发送一个新的请求去匹配 location。

  • break

停止匹配,在当前 location 去搜索资源。

  • redirect

临时重定向。返回状态码 302。

  • permanent

永久重定向。返回状态码 301 。

指定的 regex 能匹配,uri 将根据 replacement 来处理。

验证 break 和 last

以下三张图片都存在,但是内容不一样。

/Users/zhangpanqin/stduy_app/break2/test/1.jpg

/Users/zhangpanqin/stduy_app/last2/test/1.jpg

/Users/zhangpanqin/stduy_app/test/1.jpg

<code>location /break2 {
root /Users/zhangpanqin/stduy_app/break2;
rewrite /break2/(.*) /test/$1 break;
}


location /last2 {
root /Users/zhangpanqin/stduy_app/last2;
rewrite /last2/(.*) /test/$1 last;
}

location /test/ {
root /Users/zhangpanqin/stduy_app;
}/<code>

当访问 /break2/1.jpg 实际匹配第一个 location,然后在当前上下文处理 。

/break2/1.jpg 被替换为 /test/1.jpg 来处理了,然后和 root 指定的路径相结合,返回 /Users/zhangpanqin/stduy_app/break2/test/1.jpg 数据。

当访问 /last2/1.jpg ,uri 被替换为 /test/1.jpg 去匹配新的 location 进行处理。

返回 /Users/zhangpanqin/stduy_app/test/1.jpg 的内容。

验证 redirect 和 permanent

<code>location /redirect2 {
rewrite ^/redirect2 http://www.baidu.com redirect;
}

location /permanent2 {
rewrite ^/permanent2 http://www.baidu.com permanent;
}/<code>

二者匹配成功之后,都会改变浏览器地址栏地址。浏览器根据响应头 Location 跳转对应地址。

二者的区别在于,永久重定向 (permanent),浏览器会保存记录,当再访问 http://localhost:9088/permanent2 而不会询问 nginx 直接跳转。

临时重定向,浏览器每次都要询问 nginx 需要跳转到哪里。可以关闭 nginx 就知道验证结果了。

Nginx-包教包会-入门

try_files

- 说明 语法 try_files file … uri;
try_files file … =code; 默认 - 上下文 server、location

以指定顺序检查文件是否存在,并使用第一个找到的文件进行请求处理。如果找不到内容内部转发到最后一个参数 uri 。文件位置为 root + file。

<code>location /try/ {
root /usr/local/var/www/data2/data2/;
try_files $uri $uri/ @pass2;
}
location @pass2 {
default_type application/json ;

return 200 "没到到页面代理的数据" ;
}/<code>

访问 /try/1.jpg 时,$uri 为 /try/1.jpg 。

root + $uri 为 /usr/local/var/www/data2/data2/try/1.jpg 找到返回,没有找到继续匹配返回。都没有匹配内部转发至 @pass2。

如想验证跳转使用 /try/test 之类的,不要使用后缀名,因为使用后缀名的话,浏览器会返回content-type,导致内容与解析不一致,图片出不来。

配置 gzip

<code># 开启 gzip
gzip on;

# 在响应头中增加,Vary: Accept-Encoding
gzip_vary on;

# gzip压缩级别1-9,数字越大压缩效果越好,压缩所用时间也就越长,占用CPU越高
gzip_comp_level 6;

# 申请内存时大小,如果源文件 9k,超过了 8K,那会申请 16*8K。
gzip_buffers 16 8k;

gzip_min_length 2K;
gzip_proxied any;
gzip_disable "msie6";

gzip_http_version 1.1;

# 文本(js、text、css、xml、json)压缩比较好,图片已经进行过压缩,在压缩,效果不是很明显,还浪费 cpu

gzip_types text/plain text/css text/xml text/javascript application/javascript application/json application/xml+rss application/rss+xml application/atom+xml image/svg+xml;
/<code>

gzip 压缩对文本效果比较好,推荐只对文本之类的压缩。

配置缓存

为了减轻服务器压力,节省带宽,可以配置缓存。

Nginx-包教包会-入门

memory cache:它是将资源文件缓存到内存中,缓存有效直接从内存加载。

disk cache: 它是将资源文件缓存到硬盘中,缓存有效直接从硬盘中加载。

先从 memory cache 找,找不到从 disk cache 找,再找不到,请求网络资源。

缓存分为 协商缓存 和 强缓存。

协商缓存 每次都要去服务器询问缓存是否过期,没有过期使用本地的缓存。

强缓存 会有缓存过期时间,在有效期内不会去服务端校验缓存,直接使用本地缓存。

现在的 webpack 可以根据文件内容的 hash 生产类似 app.asdfa21342.js 这样的文件。其实就是想使用强缓存,当网站更新,新的页面会解析加载不一样的资源,从而降低缓存校验对服务器性能的损耗。

协商缓存

协商缓存有:ETag/if-None-Match 和 Last-Modified/if-Modify-Since 两种。

http 协议规定,当这两种响应头都存在的时候,必须都要满足,才能使用缓存。

ETag/if-None-Match

- 说明 语法 etag on|off; 默认 etag on; 上下文 http、server、location

nginx 有个 etag 配置属性,会给每个静态资源生成 Etag 响应头,值为文件内容 hash。

当浏览器第一次访问资源的时候,返回的响应头中携带 Etag 。

后续的正常访问(不强制刷新缓存)相同的资源,都会带上请求头 if-None-Match ,值为 Etag 去 nginx 校验是否一样,一样说明缓存没有过期,返回状态码 304,直接访问浏览器中的缓存,否则从浏览器返回资源,返回状态码 200。

Last-Modified/if-Modify-Since

- 说明 语法 if_modified_since off|exact|before; 默认 if_modified_since exact; 上下文 http、server、location

指定如何比较文件的修改时间与请求头 If-Modified-Since 进行比较:

  • off

忽略 If-Modified-Since 请求头字段(0.7.34)

  • exact

完全匹配

  • before

资源的修改时间小于或等于 If-Modified-Since 请求头字段中的时间

浏览器第一次访问一个资源的时候,响应头 Last-Modified 返回,标识文件的最后修改时间。

当浏览器再次正常访问(不强制刷新资源)相同资源,请求头会加上 If-Modified-Since,该值为之前返回的 Last-Modified 。nginx 收到 If-Modified-Since 后,根据配置 if_modified_since 属性比较资源的最后修改时间(Last-Modified)和该值If-Modified-Since进行比较,匹配成功,则命中缓存,返回304,否则返回 资源,状态码为 200,并更新缓存时间。

强制缓存

Expires

Expires 是 http1.0 的规范,它的值是一个绝对时间的GMT格式的时间字符串。这个时间代表的该资源的失效时间,如果在该时间之前请求的话,则都是从缓存里面读取的。如果服务端和客户端时区不一致会导致判断不准确。

Cache-Control

Cache-Control 是 http1.1 的规范,它是利用该字段 max-age 值进行判断的。该值是一个相对时间,比如 Cache-Control: max-age=3600 代表该资源的有效期是3600秒。除了该字段外,我们还有如下字段可以设置:

no-cache: 需要进行协商缓存,发送请求到服务器确认是否使用缓存。

no-store:禁止使用缓存,每一次都要重新请求数据。

public:可以被所有的用户缓存,包括终端用户和 CDN 等中间代理服务器。

private:只能被终端用户的浏览器缓存,不允许 CDN 等中继缓存服务器对其缓存。

配置缓存

<code>location ~* \\.(css|js|png|jpg|jpeg|gif|gz|svg|mp4|mp3|ogg|ogv|webm|htc|xml|woff)$ {
# 关闭访问日志记录
access_log off;
# 强缓存,时间为一年,浏览器和 cdn 中间件可以缓存
add_header Cache-Control "max-age=31536000";
}/<code>

推荐资料

nginx 中文翻译


分享到:


相關文章: