本文将讲解:
- nginx的安装文件映射
- nginx命令及常见错误的解释
- nginx配置文件解析及常用变量
- nginx应用场景
- location匹配规则
- rewrite指令部分
nginx简述与安装
nginx是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。
安装
Homebrew是macOS系统的软件包的管理器,可以用它来安装nginx:
附上Homebrew的官网:https://brew.sh/index
首先安装Homebrow:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
复制代码
成功后安装nginx,终端执行: brew install nginx
Homebrew 会将软件包安装到独立目录,并将其文件软链接至/usr/local
。
nginx安装文件目录:/usr/local/Cellar/nginx
nginx配置文件目录:/usr/local/etc/nginx
服务器默认路径:/usr/local/var/www
复制代码
当我们敲下nginx命令时,实际上是执行了一个脚本,我们可以用which
命令是查找命令是否存在,以及命令的存放位置在哪儿
Mac os 系统(基于Unix系统)一般的应用都会放在/usr/local
文件夹下面,/usr文件夹一般是对用户隐藏的,可以通过命令访问。
这里可以看到nginx是指向的一个软连接,最后执行的文件是/usr/local/Cellar/nginx/1.15.9/bin/nginx(可以通过ll
命令查看文件的软连接信息)
简单说下软连接:软连接类似window的快捷方式,它是可以跨磁盘块,目的为了复用模块,系统中有很多地方都用到软连接。我们可以看到最后执行的脚本文件位于/usr/local/Cellar/nginx/1.15.9/bin/nginx
nginx命令
常用nginx命令(管理员权限加sudo):
nginx #打开 nginx
nginx -t #测试配置文件是否有语法错误
nginx -s reopen #重启Nginx
nginx -s reload #重新加载Nginx配置文件,然后以优雅的方式重启Nginx
nginx -s stop #强制停止Nginx服务
nginx -s quit #优雅地停止Nginx服务(即处理完所有请求后再停止服务)
nginx -c 配置文件地址 #设置配置文件
复制代码
我们执行nginx重启命令有时候会遇到以下错误:
nginx: [error] open() "/usr/local/var/run/nginx.pid" failed (2: No such file or directory)
复制代码
字面大概意思是没有nginx.pid文件,进到/usr/local/var/run/
目录发现确实没有这个文件,大家都知道一般解决办法都是用
sudo nginx -c /usr/local/etc/nginx/nginx.conf
那为什么执行这个命令就有这个文件了呢?
大家都知道 nginx -c 命令是设置配置文件,正常运行之后我们可以执行cat /usr/local/var/run/nginx.pid
查看该文件的内容,发现内容只有一行数字。
这个数字其实是该进程的id,这个文件的作用是为了防止启动多个进程副本
我们可以用ps -ef | grep nginx
查看nginx的进程信息:
可以看到主进程的id跟上面文件内容是一样的,这个时候可能会产生疑问,为什么会有多个id?
nginx遵循Master-Worker设计模式,是以多进程的方式来工作的,nginx在启动后,会有一个master进程和多个worker进程,master进程主要用来管理worker进程(可以用kill -QUIT 主进程号
等方法杀死进程)。
可以得出结论:当主进程存在时,nginx.pid文件就会存在,内容为主进程id,当进程关掉时nginx.pid文件也就自动删除了,所以需要我们去指定配置文件。
Nginx配置文件解析
//定义nginx运行的用户(用户涉及到文件的权限)
#user nobody;
//nginx进程数,可以用ps -ef|grep nginx查看进程
worker_processes 1;
//全局错误日志定义类型, Homebrew放在/usr/local/var/log/nginx/error.log
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
//进程文件
#pid logs/nginx.pid;
//events模块来用指定nginx的工作模式及连接数上限
events {
// 单个进程最大链接数(即接受前端请求的链接数)
worker_connections 1024;
}
//设定http服务器()
http { //这个是协议级别
//文件扩展名与文件类型映射表
include mime.types;
//默认文件类型
default_type application/octet-stream;
//日志格式定义,变量见下面定义
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
//定义访问日志目录,Homebrew放在/usr/local/var/log/nginx/access.log
# access_log logs/access.log main;
//开启高效文件传输模式
sendfile on;
//集中发包,提高效率,sendfile on 情况下才可以打开
#tcp_nopush on;
//长链接超时时间
keepalive_timeout 65;
//开始gzip压缩,服务器压缩,浏览器解压
#gzip on;
//单个虚拟主机的配置
server { //这个是服务器级别
//监听的端口
listen 8080;
//监听的服务域名,可以有多个,用逗号隔开
server_name localhost;
//默认编码
#charset koi8-r;
//该虚拟主机日志的存放位置
#access_log logs/host.access.log main;
//对应的路由展示
location / { //这个是请求级别
//文件目录
root html;
index index.html index.htm;
}
//错误页面
error_page 500 502 503 504 /50x.html;
location = /50x.html {
//相对的路径存放目录
root html;
}
}
// 增加配置可以include其他配置文件
include configarable.conf;
}
复制代码
访问nginx的error.log文件: tail /usr/local/var/log/nginx/error.log (默认查最后十行)
访问nginx的access.log文件: tail /usr/local/var/log/nginx/access.log (默认查最后十行)
nginx内置变量
$uri 请求的URI,可能会经过重定向导致跟最初的值有不同
$http_user_agent 客户端信息
$args 请求参数;
$body_bytes_sent 已发送的消息体字节数
$content_length header头信息里的"Content-Length"
$content_type header头信息里的"Content-Type"
$document_root 针对当前请求的根路径设置值
$document_uri 与$uri相同
$host 请求信息中的"Host",没有Host行,则等于设置的服务器名;
$http_cookie cookie 信息
$http_referer 来源地址
$http_via 最后一个访问服务器的Ip地址
$http_x_forwarded_for 相当于网络访问路径。
$limit_rate 对连接速率的限制
$remote_addr 客户端地址
$remote_port 客户端端口号
$remote_user 客户端用户名,认证用
$request 用户请求信息
$request_body 用户请求主体
$request_body_file 发往后端的本地文件名称
$request_filename 当前请求的文件路径名
$request_method 请求的方法,比如"GET"、"POST"等
$request_uri 请求的URI,带参数
$server_addr 服务器地址
$server_name 请求到达的服务器名
$server_port 请求到达的服务器端口号
$server_protocol 请求的协议版本,"HTTP/1.0"或"HTTP/1.1"
复制代码
nginx的应用场景
- 静态资源web服务器
- 代理服务器
- 负载均衡
静态资源服务器
nginx采用的是异步非阻塞的通信机制(epoll模型),支持更大的并发连接.所谓的epoll模型:当事件没有准备好时,就放入epoll(队列)里面。如果有事件准备好了,那么就去处理;实现由进程循环处理多个准备好的事件,从而实现高并发和轻量级。
预先定义好本地的静态资源:(后面的例子也会用到这些)
/usr/local/test-img/follow.png,
/usr/local/test-img/403.png,
/usr/local/test-html/forward.html,
/usr/local/test-html/taobao/forward.html,
/usr/local/test-html/taobao/taobao.html,
/usr/local/test-html/taobaowang/taobao.html,
/usr/local/test-html/upstream/1.html,
/usr/local/test-html/upstream/2.html,
/usr/local/test-html/upstream/3.html,
/usr/local/test-html/upstream/4.html
复制代码
Gzip压缩
静态资源就会涉及到Gzip压缩问题:
syntax:gzip on | off
default:gzip off
context:http, server, if in location
复制代码
配置语法:
//打开或者关闭gzip压缩的功能
gzip on;
// 最小压缩长度, 被压缩的内容超过这个长度才会被压缩,否则直接输出
gzip_min_length 1024;
// 压缩级别,分为1-9
gzip_comp_level 2;
// 列出来的内容类型才会被压缩,其他类型的内容不会被压缩,类型指的是MIME类型
gzip_types text/plain application/x-javascript text/css application/xml text/javascript image/jpeg image/gif image/png;
// 会在响应头增加vary:Accept-Encoding,代表已经进行服务端压缩
gzip_vary on
//设置nginx 服务器是否对后端返回的结果进行gzip压缩,反向代理的时候有效
gzip_proxine
// 存放静态资源的文件路径
root /usr/local/test-img;
复制代码
在浏览器访问:http://localhost:9090/follow.png
验证:
我们可以看到文件体积已经变小了
防盗链
首先说一下盗链,举个例子:别人把你网站上的图片链接放到自己的网站上,这样在访问别人的网站时,实际上在调用你网站上的图片,还要用你服务器的流量带宽。
防盗链是基于验证referer来实现的,referer表示一个网站的请求来源,伪装referer头部是非常简单的事情,所以这个模块只能用于阻止大部分非法请求.我们应该知道,有些合法的请求是不会带referer来源头部的,所以有时候不要拒绝来源头部(referer)为空的请求。比如直接在浏览器的地址栏中输入一个资源的URL地址,那么这种请求是不会包含referer字段的。
nginx防盗链指令:
syntax: valid_referers none | blocked | server_names | string...;
default: -
context:server, location
复制代码
参数解释:
none:表示来源头部为空的情况
blocked:表示来源头部不为空,但是里面的值被代理或者防火墙删除了,这些值都不以http:// 或者https:// 开头。
`sever_names `: 表示来源头部包含当前的`server_names `
string:任意字符串,定义服务器名或者可选的URI前缀.主机名可以使用` * ` 开头或者结尾,在检测来源头部这个过程中,来源域名中的主机端口将会被忽略掉
正则表达式:`~ `表示排除https://或http://开头的字符串.
复制代码
看下面的配置:
valid_referers blocked server_names ~\.goole\. ~\.baidu\.;
if ($invalid_referer) {
#return 403; // 返回403
rewrite ^/ http://127.0.0.1:7000/403.png; // 链接到403图片
}
复制代码
代理服务器
前提:本次是在一台服务器上做验证,用不同的端口来模拟不同服务器之间的交互。
代理分为正向代理跟反向代理;
- 正向代理是为客户端做代理,代替客户端去访问服务器;
- 反向代理是为服务器做代理,代替服务器接受客户端请求。
前端常用的代理是反向代理,下面讲解下反向代理:
反向代理是指以代理服务器来接受网络上的连接请求,然后将请求转发给内部网络上的服务器,把数据返回给客户端,此时代理服务器对外就表现为一个源服务器。
nginx 反向代理的指令不需要新增额外的模块,默认自带
proxy_pass
指令,只需要修改配置文件就可以实现反向代理。
location / {
// 处理跨域请求
add_header Access-Control-Allow-Origin *;
// 请求头支持的传递字段
add_header Access-Control-Allow-Headers "Origin, Content-Type";
//涉及预检请求,服务器需要允许该方法
add_header Access-Control-Allow-Methods "OPTIONS";
// 代理网路请求到本地3000端口
proxy_pass http://localhost:3000;
// 重写主机名,防止后端真实的服务器设置有类似防盗链或者根据http请求头中的host字段来进行路由或判断功能
proxy_set_header Host $host;
// 重写服务器ip ,防止后端有防攻击策略的话,机器会被封掉
proxy_set_header X-Forwarded-For $remote_addr
// 请求端真实的IP
proxy_add_x_forwarded_for: client ;
}
复制代码
负载均衡
负载均衡的作用:实现在不同地域的服务器间的流量调配,保证使用最佳的服务器服务离自己最近的客户,从而确保访问质量
在http层面下添加upstream节点:
upstream clusters {
server 127.0.0.1:9001;
server 127.0.0.1:9002;
server 127.0.0.1:9003;
server 127.0.0.1:9004;
}
复制代码
本地添加静态资源服务作为被请求服务器,请求服务器配置:
server {
listen 9005;
server_name localhost;
location / {
proxy_pass http://clusters;
}
}
复制代码
用 curl http://localhost:9005
或者在浏览器请求去验证负载均衡是否起作用。
可以看到每次的请求都被均匀的分配到不同的服务器
Upstream可以为每个服务单独设置状态值
down:表示当前server暂时不参与负载
backup: 预留的备份服务器,压力最小
max_fails:允许请求失败的次数
fail_timeout : 经过max_fails失败后,服务暂停的时间
max_conns:限制最大的接收的连接数
复制代码
每个服务的调度算法讲解
轮询:按时间顺序逐一分配到不同的后端服务器
weight:默认为1 weight越大,匹配的机会越多
upstream clusters {
server 127.0.0.1:9001;// 访问比率:20%
server 127.0.0.1:9002; //访问比率: 20%
server 127.0.0.1:9003 weight=2;//访问比率:40%
server 127.0.0.1:9004 weight=1; //访问比率:20%
}
ip_hash:每个请求按访问ip的hash结果分配,这样来自同一个ip的固定访问一个后端服务器,可以解决服务端的用户session问题
upstream clusters {
ip_hash;
server 127.0.0.1:9001;
server 127.0.0.1:9002;
server 127.0.0.1:9003;
server 127.0.0.1:9004;
}
url_hash:按照访问的url的hash结果来分配请求,是每个url定向到同一个后端服务器,可以解决缓存失效问题
upstream clusters {
//$request_uri是nginx内部抛出的变量,指的是除了域名的部分
hash $request_uri;
server 127.0.0.1:9001;
server 127.0.0.1:9002;
server 127.0.0.1:9003;
server 127.0.0.1:9004;
}
least_conn :最少链接数,那个机器连接数少就分发
复制代码
用 curl http://localhost:9005
或者在浏览器请求去验证这些参数的作用。
location部分
1.书写匹配location规则的时候会有一些纠结加不加/的问题,下面讨论下匹配url加/与不加/的区别;转发请求路径(也就是
proxy_pass
后面路径)加/与不加/的区别。
匹配url加不加/的区别
预先在127.0.0.1:9006机器上定义好了静态资源:
/usr/local/test-html/taobao/taobao.html
/usr/local/test-html/taobaowang/taobao.html
复制代码
我们先定义请求路径(本地资源):
http://localhost:9007/taobao/taobao.html,
http://localhost:9007/taobaowang/taobao.html
复制代码
先看下面加/的配置:
location /taobao/ {
proxy_pass http://127.0.0.1:9006;
}
复制代码
分别请求上面两个路径(可在浏览器端也可以用下面的命令):
curl http://localhost:9007/taobao/taobao.html
curl http://localhost:9007/taobaowang/taobao.html
再来看一下不加/的配置:
location /taobao {
proxy_pass http://127.0.0.1:9006;
}
复制代码
分别请求上面两个路径(可在浏览器端也可以用下面的命令):
curl http://localhost:9007/taobao/taobao.html
curl http://localhost:9007/taobaowang/taobao.html
通过比较:加/只能匹配到
/usr/local/test-html/taobao/taobao.html
资源;而/usr/local/test-html/taobaowang/taobao.html
资源匹配不到;不加/两个资源都能得到。
可以得出结论:由于location进行的是模糊匹配,所以对于加/的这种情况只能匹配像/taobao/any
这种url,不加/的情况可以匹配/taobao[any]
这种url
转发的请求路径加不加/的区别
预先在127.0.0.1:9006机器上定义好了静态资源:
/usrl/local/test-html/taobao/forward.html
/usrl/local/test-html/forward.html
复制代码
我们先定义请求路径为:http://localhost:9007/taobao/forward.html
先看下面加/的配置:
location /taobao/ {
proxy_pass http://127.0.0.1:9006/;
}
复制代码
请求定义路径(可在浏览器端也可以用下面的命令):
curl http://localhost:9007/taobao/forward.html
再来看下不加/的配置:
location /taobao/ {
proxy_pass http://127.0.0.1:9006;
}
复制代码
请求定义路径(可在浏览器端也可以用下面的命令):
curl http://localhost:9007/taobao/forward.html
通过比较:加/访问的资源是
/usrl/local/test-html/forward.html
, **不加/**访问的资源是/usrl/local/test-html/taobao/forward.html
可以得出结论:加/的话相当于绝对路径,不会把location中匹配的url代理走,不加/的话会把匹配的路径部分也给代理走
2.实际项目中每个虚拟主机中会有多个location配置,那这样就会涉及到匹配location的顺序问题
location [=|~|~*|^~|@ ] /url/ {config}
= 表示精确匹配
~ 表示正则匹配,区分大小写
~*表示正则匹配 ,不区分大小写
^~表示不匹配正则
@表示internally redirected (内部重定向,表示forward)
首先分类下:分为普通的location跟正则lcoation
正则location: `~ |~*
一般location:`= | ^~|@
复制代码
验证匹配优先级
1.两个普通的location配置
location /taobao/ {
root /usr/local/test-html;
allow all;
}
location /taobao/taobao.html {
root /usr/local/test-html;
deny all;
}
复制代码
执行curl http://localhost:9008/taobao/taobao.html
,得到:
现在把下面的location注释:
location /taobao/ {
root /usr/local/test-html;
allow all;
}
#location /taobao/taobao.html {
#root /usr/local/test-html;
#deny all;
#}
复制代码
执行curl http://localhost:9008/taobao/taobao.html
,得到:
结论:普通location之间的顺序规则是有个最大匹配原则,越精确优先级越高
2.正则location配置
location ~ \.html$ {
root /usr/local/test-html;
allow all;
}
location ~ /taobao.html {
root /usr/local/test-html;
deny all;
}
复制代码
执行curl http://localhost:9008/taobao/taobao.html
,得到:
现在把两个配置换个位置:
location ~ /taobao.html {
root /usr/local/test-html;
deny all;
}
location ~ \.html$ {
root /usr/local/test-html;
allow all;
}
复制代码
执行curl http://localhost:9008/taobao/taobao.html
,得到:
结论:正则location之间会有顺序,写在前面的会被优先匹配到
3.优先级高的普通location跟正则location配置
location /taobao/taobao.html {
root /usr/local/test-html;
deny all;
}
location ~ \.html$ {
root /usr/local/test-html;
allow all;
}
复制代码
执行curl http://localhost:9008/taobao/taobao.html
,得到:
结论:正则location优先级要高于普通的lcoation
4.精确location跟正则location的配置
location = /taobao/taobao.html {
root /usr/local/test-html;
deny all;
}
location ~ \.html$ {
root /usr/local/test-html;
allow all;
}
复制代码
执行curl http://localhost:9008/taobao/taobao.html
,得到:
结论:精确location优先级要高于正则location
5.特殊情况^~
location跟正则location还有普通location的配置
location /taobao/taobao.html {
root /usr/local/test-html;
deny all;
}
location ^~ /taobao/ {
root /usr/local/test-html;
deny all;
}
location ~ \.html$ {
root /usr/local/test-html;
allow all;
}
复制代码
执行curl http://localhost:9008/taobao/taobao.html
,得到:
可以看到起作用的是最后一个配置,好像`^~ `没有起作用
我们把配置改变一下:
#location /taobao/taobao.html {
#root /usr/local/test-html;
#deny all;
#}
location ^~ /taobao/ {
root /usr/local/test-html;
deny all;
}
location ~ \.html$ {
root /usr/local/test-html;
allow all;
}
复制代码
注释第一个配置,执行curl http://localhost:9008/taobao/taobao.html
,得到:
可以看到第二个配置已经起作用`^~ `,代表不匹配正则表达式
那为什么现在起作用呢?根据上面的配置我们把普通location中的优先级高的配置注释掉了。那是不是这个优先级高的配置把
^~
的配置覆盖掉了呢?
我们现在再把配置改变一下:
location ^~ /taobao/taobao.html {
root /usr/local/test-html;
deny all;
}
location /taobao/ {
root /usr/local/test-html;
deny all;
}
location ~ \.html$ {
root /usr/local/test-html;
allow all;
}
复制代码
执行curl http://localhost:9008/taobao/taobao.html
,得到:
现在可以看到这个普通lcoation优先级高的配置把`^~ `覆盖掉了,所以这个`^~ `也属于普通lcoation。
结论:
^~
属于普通的lcoation,遵循普通location的规则,如果被覆盖,后面还有正则location的话,则正则location优先级更高
总体结论:精确匹配 (=) > 正则lcoation(有顺序限制,只匹配第一个) > 普通lcoation (最大匹配原则),这里有个特殊情况是遇到^~
,在不被覆盖的情况下,不匹配后面的正则location。
rewrite模块
先说下rewrite的指令:
break:停止执行该模块的指令集
if:根据条件决定是否执行语句
return: 返回一个状态值给客户端
rewrite: 根据表达式来更改url
set:可以设置一个变量
复制代码
if指令
syntax: if (condition);
default:-
context:server, location,if
复制代码
验证条件逻辑:
表达式只是一个变量时,值为""或任何以0开头的字符串都会当做false
直接时使用=或!=,跟js有区别
正则表达式匹配,~区分大小写,~*不区分大小写的匹配,!~,!~表示不匹配
-f和!-f用来检测一个文件是否存在
-d和!-d用来检测一个目录是否存在
-e和!-e用来检测是否存在一个文件,一个目录或者一个符号链接
-x和!-x用来检测一个文件是否可执行
复制代码
举个例子:(见以下配置)
if ($http_user_agent ~ Safari) {
return 401;
}
复制代码
在浏览器访问http://localhost:9009/,得到:
rewrite指令
syntax: rewrite regex replacement [flag];
default:-
context:server, location,if
regex:正则表达式
replacement: 新的url
flag:包含这几个值:last, break, redirect, permanent
last:停止处理rewrite模块的指令集,并根据replacement继续匹配location
break:停止处理rewrite模块的指令集
redirect:返回302临时重定向
permanent:返回301永久重定向
复制代码
last跟break的区别
配置:
location / {
rewrite ^/code/ /test last;
return 403;
}
location /test {
return 500;
}
复制代码
执行curl http://localhost:9009/code/*
得到:
现在更改下配置:
location / {
rewrite ^/code/ /test break;
return 403;
}
location /test {
return 500;
}
复制代码
执行curl http://localhost:9009/code/*
得到:
再更改下配置:
location / {
rewrite ^/code/ /test;
return 403;
}
location /test {
return 500;
}
复制代码
执行curl http://localhost:9009/code/*
得到:
结论:last跟break都能停止rewrite模块的指令集,但是last会继续匹配location,break就地终止。
另外说下请求参数的问题
下面看个例子:
location / {
rewrite /code /testparams permanent;
}
复制代码
浏览器请求:http://localhost:9009/code?a=1
将会看到浏览器地址被重定向到 http://localhost:9009/testparams?a=1
,旧参数被添加到新的url上了
我们下面来改一下配置:
location / {
rewrite /code /testparams? permanent;
}
复制代码
浏览器请求:http://localhost:9009/code?a=1
将会看到浏览器地址被重定向到 http://localhost:9009/testparams
,旧参数被省略掉了
结论:默认情况下旧的url请求的参数会放在新替换的url 后面,如果想省略旧的请求参数在新的url后面加上?就好了。
好了目前先写到这吧,感觉不错的话留下你的?~