nginx 学习笔记
简介
nginx是俄罗斯人Igor Sysoev编写的轻量级Web服务器。
它不仅是一个高性能的HTTP和反向代理服务器,同时也是一个IMAP/POP3/SMTP 代理服务器
nginx只是一个静态文件服务器或者http请求转发器,它可以把静态文件的请求直接返回静态文件资源,把动态文件的请求转发给后台服务。
特点
- 轻量
- 事件驱动的异步非阻塞处理
- 占用内存少、启动速度快、并发能力强
- 可靠
- 热部署:通过master管理进程与worker工作进程的分离设计,使的Nginx具有热部署的功能,那么在7×24小时不间断服务的前提下,升级Nginx的可执行文件。也可以在不停止服务的情况下修改配置文件,更换日志文件等功能
整体结构
Nginx 主配置文件 /etc/nginx/nginx.conf 是一个纯文本类型的文件,整个配置文件是以区块的形式组织,通常每一个区块以一对大括号{}来表示开始与结束(可看下方的代码注释)。
- Main 位于 nginx.conf 配置文件的最高层;
user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; ......
- Main 层下可以有 Event、HTTP 层;
events {} http {}
- Http 层下面允许有多个 Server 层,用于对不同的网站做不同的配置;
- Server 层下面允许有多个 Location,用于对不同的路径进行不同模块的配置。
应用
- 动静分离
- 定义:将网站静态资源(HTML,JavaScript,CSS,img等文件)与后台应用分开部署,提高用户访问静态代码的速度,降低对后台应用访问
- 优点:
- api接口服务化:动静分离之后,后端应用更为服务化,只需要通过提供api接口即可,可以为多个功能模块甚至是多个平台的功能使用,可以有效的节省后端人力,更便于功能维护
- 前后端开发并行:前后端只需要关心接口协议即可,各自的开发相互不干扰,并行开发,并行自测,可以有效的提高开发时间,也可以有些的减少联调时间
- 减轻后端服务器压力,提高静态资源访问速度:后端不用再将模板渲染为html返回给用户端,且静态服务器可以采用更为专业的技术提高静态资源的访问速度。
- 反向代理
- 定义:指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
简单来说就是真实的服务器不能直接被外部网络访问,所以需要一台代理服务器,而代理服务器能被外部网络访问的同时又跟真实服务器在同一个网络环境,当然也可能是同一台服务器,端口不同而已。
对于前端而言,请求后端的接口容易报跨域问题,这就是前端所处于的ip和后端接口的ip不一致,这就用到了反向代理,通过nginx将后端的接口改成浏览器一样的的ip,(跨域除了nginx配置外,可以通过nodejs设置中转,后端设置白名单)
反向代理是为服务端服务的,反向代理可以帮助服务器接收来自客户端的请求,帮助服务器做请求转发,负载均衡等。
反向代理对服务端是透明的,对我们是非透明的,即我们并不知道自己访问的是代理服务器,而服务器知道反向代理在为他服务。 - 关键命令:proxy_pass
- 作用:
- 安全:隐藏服务节点的IP,将服务节点置于防火墙之后,避免直接攻击业务节点服务器
- 服务节点更专注于业务,同时提升性能(去让nginx实现gzip压缩,https等等;动静分离,缓存机制,)
- 三种模式
- 基于IP(路径path)代理
location后的path带不带/没有区别,proxy_pass后的路径带不带‘/’ 区别很大.- target服务路径需要context-path(location: /docs)时
location /docs{ proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } #http://aaa.test/docs #代理访问后端服务:http://localhost:8080/docs
- target服务路径不需要context-path(location:/a)时
location /a { proxy_pass http://localhost:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } #http://localhost:1111/a/docs #代理访问后端服务:http://localhost:8080/docs
- 基于域名代理
server { listen 80; server_name a.local; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; location / { proxy_pass http://127.0.0.1:8080; } }
- 基于端口代理
server { listen 16010; server_name localhost; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; location / { proxy_pass http://192.192.192.192:16010; } }
- 基于IP(路径path)代理
- eg:这三种模式的反向代理除了基于ip其他的情况都是根据locaiton的根域名为/来设置的,这样的可以通过访问域名/端口代理了。
- 定义:指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。
- 正向代理
- 定义:
与反向代理相反,FQ,就是所谓的正向代理。有个恰当的例子:多个人找代购购买同一个商品,代购找到买这个的店后一次性给买了。这个过程中,该店主是不知道代购是帮别代买买东西的。那么代购对于多个想买商品的顾客来讲,他就充当了正向代理。
正向代理是为我们服务的,即为客户端服务的,客户端可以根据正向代理访问到它本身无法访问到的服务器资源。
向代理对我们是透明的,对服务端是非透明的,即服务端并不知道自己收到的是来自代理的访问还是来自真实客户端的访问 - 缺点:不能直接应用于https,需要特制配置,见示例
- 代码示例(未实践,回头实践)看 (Nginx服务器---正向代理)[https://blog.csdn.net/weixin_42751488/article/details/124148392]
- 定义:
- 负载均衡
- 定义:简单而言就是当有2台或2台以上服务器时,根据规则随机的将请求分发到指定的服务器上处理,以来减轻服务器的压力。
负载均衡配置一般都需要同时配置反向代理,通过反向代理跳转到负载均衡。 - 关键命令: upstream
- 作用:
- 分摊服务器集群压力
- 保证客户端访问的稳定性(nginx自带心跳检查,会定期轮询向所有的服务器发起请求,用来检查某个服务器是否异常,若有异常,则停止请求到这个服务器,直到这个服务器正常)
- 策略:Nginx目前支持自带3种负载均衡策略,两种常见的第三方负载均衡策略
- 默认:按照时间一次分配到不同的机器上
upstream test { server localhost:8080; server localhost:8081; } server { listen 80; server_name localhost; client_max_body_size 1024M; location / { proxy_pass http://test; proxy_set_header Host $host:$server_port; } }
- 权重 weight:指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
upstream test { server localhost:8080 weight=9; #请求的 90% 进入到8080服务器 server localhost:8081 weight=1; #请求的 10% 进入到8081服务器 }
- ip_hash:每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题,通过session/cookie共享,来指定跳到对应有session/cookie的服务器,(如果客户已经访问了某个服务器,当用户再次访问时,会将该请求通过哈希算法,自动定位到该服务器。)
upstream test { ip_hash; server localhost:8080 weight=9; #请求的 90% 进入到8080服务器 server localhost:8081 weight=1; #请求的 10% 进入到8081服务器 }
- fair(第三方):按后端服务器的响应时间来分配请求,响应时间短的优先分配。
upstream test { fair; server localhost:8080 weight=9; #请求的 90% 进入到8080服务器 server localhost:8081 weight=1; #请求的 10% 进入到8081服务器 }
- url_hash(第三方):按访问url的hash结果来分配请求,使每个url定向到同一个(对应的)后端服务器,后端服务器为缓存时比较有效。
upstream test { server squid1:3128; server squid2:3128; hash $request_uri; hash_method crc32; }
- 默认:按照时间一次分配到不同的机器上
- 定义:简单而言就是当有2台或2台以上服务器时,根据规则随机的将请求分发到指定的服务器上处理,以来减轻服务器的压力。
- 虚拟主机
- 虚拟主机就是将一台服务器分割成多个“虚拟服务器”,每个站点使用各自的硬盘空间,由于省资源,省钱,众多网站都使用虚拟主机来部署网站
- 虚拟主机的概念就是在web服务里的一个独立的网站站点,这个站点对应独立的域名(IP),具有独立的程序和资源目录,可以独立的对外提供服务
- 这个独立的站点配置是在nginx.conf中使用server{}代码块标签来表示一个虚拟主机
- Nginx支持多个server{}标签,即支持多个虚拟主机站点
- 本地测试的话需要配置host
- 类型
- 基于 IP 的虚拟主机
server { listen 80;#监听端口 server_name 192.168.1.1;#配置虚拟主机名和IP location / { root /home/wwwroot/ipsite01/;#请求匹配路径 index index.html;#指定主页 access_log /home/wwwlog/ipsite01.access.log main; error_log /home/wwwlog/ipsite01.error.log warn; } } server { listen 80; server_name 192.168.1.2; location / { root /home/wwwroot/ipsite02/;#请求匹配路径 index index.html; access_log /home/wwwlog/ipsite02.access.log main; error_log /home/wwwlog/ipsite02.error.log warn; } }
- 基于域名的虚拟主机
server { listen 80;#监听端口 server_name www.cainiaojc.com;#配置虚拟主机域名 location / { root /home/wwwroot/domainsite01/;#请求匹配路径 index index.html;#指定主页 access_log /home/wwwlog/domainsite01.access.log main; error_log /home/wwwlog/domainsite01.error.log warn; } } server { listen 80; server_name man.niaoge.com; location / { root /home/wwwroot/domainsite02/;#请求匹配路径 index index.html; access_log /home/wwwlog/domainsite02.access.log main; error_log /home/wwwlog/domainsite02.error.log warn; } }
- 基于端口的虚拟主机
server { listen 8080;#监听端口 server_name www.cainiaojc.com;#配置虚拟主机域名 location / { root /home/wwwroot/portsite01/;#请求匹配路径 index index.html;#指定主页 access_log /home/wwwlog/portsite01.access.log main; error_log /home/wwwlog/portsite01.error.log warn; } } server { listen 8090; server_name www.cainiaojc.com; location / { root /home/wwwroot/portsite02/;#请求匹配路径 index index.html; access_log /home/wwwlog/portsite02.access.log main; error_log /home/wwwlog/portsite02.error.log warn; } }
nginx内置全局变量
以下都是nginx的部分内置全局变量,可以在配置的任何位置使用
- $host : 请求信息中的Host,若请求中没有host行,则等于设置的服务器名
- $request_mothed: 客户端的请求类型,如get/post/put
- $remote_addr: 客户端的ip
- $args: 请求中的参数
- $content_length: 请求头中的content-length字段
- $http_user_agent: 客户端的agent信息
- $http_cookie: 客户端的cookie信息
- $remote_port: 客户端的port端口
- $server_protocol: 请求使用的协议,如HTTP/1.0 HTTP/2.0
- $server_addr: 服务器地址
- $server_name: 服务器名称
- $server_port: 服务器的端口号
- $uri: 请求中的当前 URI,已标准化。我们可以在请求处理期间更改 $uri 的值,例如在进行内部重定向或使用索引文件时。
- $request: 完整的原始请求行
- $request_body: 当请求正文被读取到 memory_buffer 时,该变量的值在由 proxy_pass 和 scgi_pass 指令处理的位置中可用。
- $request_body_file: 带有请求正文的临时文件的名称。
- $request_uri: 带有参数的完整原始请求 URI(统一资源标识符)。
nginx变量
在配置文件中,可以通过变量,来上下文使用
用法就是用 $ 来命名变量
set $a "hello world";
#这样在nginx.conf文件中都能用到
例子:移动端和pc端切换.
location / {
# 适配移动端/PC端配置
set $type "pc";
if ($http_user_agent ~* (mobile|nokia|iphone|ipad|android|samsung|htc|blackberry)) {
set $type "mobile";
}
root /usr/local/var/www/project/$type; # 根据设备类型选择设定根目录文件夹名(pc/mobile)
index index.html index.htm;
}
多个server同一个server_name匹配规则
客户端发出一个http请求时,nginx收到后会取出header头中的host,与nginx.conf中每个server的server_name进行匹配,以此决定到底由哪一个server块来处理这个请求。
-
匹配规则:
- 完全匹配
- 通配符在前的,如*.test.com
- 通配符在后的,如www.test.*
- 正则匹配,如~^.www.test.com$
如果都不匹配 - 优先选择listen配置项后有default或default_server的
- 再不匹配的话,找到匹配listen端口的第一个server块
-
调试代码:
server{ default_type text/plain; listen 80; server_name _; return 200 "no1 match"; } server{ default_type text/plain; listen 80; server_name *.test.com; return 200 "通配符在前"; } server{ default_type text/plain; listen 80; server_name www.test.*; return 200 "通配符在后"; } server{ default_type text/plain; listen 80; server_name ~^www.test.com$; return 200 "正则匹配"; } server{ default_type text/plain; listen 80; server_name www.test.com; return 200 "完全匹配"; } # 把这些放到nginx.conf中,重启nginx -s reload # 配置host文件 127.0.0.1 www.test.com # 打开www.test.com # 结果就如匹配规则一样,从前依次删除server测试,重启nginx,即可发现规律 # 同样的,可以改变server的顺序,也可得出第一种匹配规则和顺序无关
server{ default_type text/plain; listen 80 default_server; server_name 127.0.0.1; return 200 "default"; } server{ default_type text/plain; listen 80; server_name *.test.com; return 200 "通配符在前"; } # 这个我测试的老是报a duplicate default server for 0.0.0.0:82 in /usr/local/etc/nginx/servers/learn.conf:65, 原因未解 # 网上说的即使有defualt,但也会命中通配符在前,这说明了第一种情况的优先级高于第二种
server{ default_type text/plain; listen 83; return 200 "first"; } server{ default_type text/plain; listen 83; return 200 "last"; } #若都不匹配的话,直接取第一个
location匹配规则
- 基础
- location 是在 server 块中配置
- 可以根据不同的 URI 使用不同的配置(location 中配置),来处理不同的请求
- location 是有顺序的,会被 第一个 匹配的location 处理。
- 规则
- / 代表任意匹配
- /api 要求必须以指定模式开始
location /api{ #规则 } # 以下访问都是正确的 # http://127.0.0.1/api # http://127.0.0.1/api?p1=TOM # http://127.0.0.1/api/ # http://127.0.0.1/apiapi
- = : 用于不包含正则表达式的uri前,必须与指定的模式精确匹配
location =/api{ #规则 } # 可以匹配到 # http://127.0.0.1/api # http://127.0.0.1/api?p1=TOM # 匹配不到 # http://127.0.0.1/api/ # http://127.0.0.1/apiapi
- ~ :大小写敏感
location ~ /Example/ { #规则 } # 可以匹配到 #http://127.0.0.1/Example/ # 匹配不到 #http://127.0.0.1/example/
- ~* : 大小写忽略
location ~* /Example/ { #规则 } # 可以匹配到 #http://127.0.0.1/Example/ #http://127.0.0.1/example/
- ^~ : 只匹配以 uri 开头,用于不包含正则表达式的uri前,功能和不加符号的一致,唯一不同的是,如果模式匹配,那么就停止搜索其他模式了
location ^~ /img/ { #规则 } #以 /img/ 开头的请求,都会匹配上 #http://local.learn.com/img/a.jpg #http://local.learn.com/img/b.mp4
- @ nginx内部跳转
location /img/ { error_page 404 @img_err; } location @img_err { # 规则 } #以 /img/ 开头的请求,如果链接的状态为 404。则会匹配到 @img_err 这条规则上。
- 匹配顺序
多个location配置的情况下匹配顺序为(当有匹配成功时候,停止匹配,按当前匹配规则处理请求):- 优先匹配 =
- 其次匹配 ^~
- 按照文件中的匹配顺序执行
- 最后匹配 /
代码注释
#运行用户
user nobody;
#启动进程,通常设置成和cpu的数量相等
worker_processes 1;
#全局错误日志及PID文件
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
#工作模式及连接数上限
events {
#epoll是多路复用IO(I/O Multiplexing)中的一种方式,
#仅用于linux2.6以上内核,可以大大提高nginx的性能
use epoll;
#单个后台worker process进程的最大并发链接数
worker_connections 1024;
# 并发总数是 worker_processes 和 worker_connections 的乘积
# 即 max_clients = worker_processes * worker_connections
# 在设置了反向代理的情况下,max_clients = worker_processes * worker_connections / 4 为什么
# 为什么上面反向代理要除以4,应该说是一个经验值
# 根据以上条件,正常情况下的Nginx Server可以应付的最大连接数为:4 * 8000 = 32000
# worker_connections 值的设置跟物理内存大小有关
# 因为并发受IO约束,max_clients的值须小于系统可以打开的最大文件数
# 而系统可以打开的最大文件数和内存大小成正比,一般1GB内存的机器上可以打开的文件数大约是10万左右
# 我们来看看360M内存的VPS可以打开的文件句柄数是多少:
# $ cat /proc/sys/fs/file-max
# 输出 34336
# 32000 < 34336,即并发连接总数小于系统可以打开的文件句柄总数,这样就在操作系统可以承受的范围之内
# 所以,worker_connections 的值需根据 worker_processes 进程数目和系统可以打开的最大文件总数进行适当地进行设置
# 使得并发总数小于操作系统可以打开的最大文件数目
# 其实质也就是根据主机的物理CPU和内存进行配置
# 当然,理论上的并发总数可能会和实际有所偏差,因为主机还有其他的工作进程需要消耗系统资源。
# ulimit -SHn 65535
}
http {
#设定mime类型,类型由mime.type文件定义
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"';
#日志保存地址 格式代码 main
access_log logs/access.log main;
#sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件,
#对于普通应用,必须设为 on,
#如果用来进行下载等应用磁盘IO重负载应用,可设置为 off,
#以平衡磁盘与网络I/O处理速度,降低系统的uptime.
sendfile on;
#tcp_nopush on;
#连接超时时间
#keepalive_timeout 0;
keepalive_timeout 65;
tcp_nodelay on;
#开启gzip压缩
gzip on;
gzip_disable "MSIE [1-6].";
# 设定压缩的临界点
gzip_min_length 1000;
# 压缩级别
gzip_comp_level 3;
# 要压缩的文件类别
gzip_types text/plain application/xml;
#设定请求缓冲
client_header_buffer_size 128k;
large_client_header_buffers 4 128k;
#开启错误页面跳转404
proxy_intercept_errors on;
#设定虚拟主机配置
server {
#侦听80端口
listen 80;
#定义使用 www.nginx.cn访问
server_name www.nginx.cn;
#定义服务器的默认网站根目录位置
root html;
#设定本虚拟主机的访问日志
access_log logs/nginx.access.log main;
#默认请求
location / {
#定义首页索引文件的名称
index index.php index.html index.htm;
try_files $uri index.html =404;
}
# 定义错误提示页面
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
#静态文件,nginx自己处理
location ~ ^/(images|javascript|js|css|flash|media|static)/ {
#过期30天,静态文件不怎么更新,过期可以设大一点,
#如果频繁更新,则可以设置得小一点。
expires 30d;
}
#PHP 脚本请求全部转发到 FastCGI处理. 使用FastCGI默认配置.
location ~ .php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
#禁止访问 .htxxx 文件
location ~ /.ht {
deny all;
}
}
}
代码示例
- 重定向
301 是永久重定向,302 是临时跳转,
主要的区别在于搜索引擎对此的对待方式:- 301:搜索引擎会将权重和 PR 值进行转移
- 302:搜索引擎不会进行额外处理
server {
listen 80;
server_name ~^(?:www\.)?(.+)$;
return 301 https://$1$request_uri;
}
代码解释:在 http 对应的 sever 中,把 server_name 也改为正则模式,并将 $host 用捕获的根域名 $1 取代www 在这里会直接弃掉,所以不需要捕获,使用 ?: 标示实现只分组不捕获,于是后面的根域名就成了 $1这样的结果是不管原来是否带 www,都统一跳转到不带 www 的 https 根域名
2. 防盗链
location ~* \.(gif|jpg|jpeg|png|bmp|swf)$ {
valid_referers none blocked 192.168.0.103; # 只允许本机IP外链引用
if ($invalid_referer){
return 403;
}
}
- 解决跨域
server {
listen 8080;
server_name localhost;
location / {
# 跨域代理设置
proxy_pass http://www.baidu.com; # 要实现跨域的域名
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
}
}
- 设置访问白名单
server {
listen 8080;
server_name localhost;
location / {
# IP访问限制(只允许IP是 12.12.12.12 的机器才能访问)
allow 12.12.12.12;
deny all;
root html;
index index.html index.htm;
}
}
注意事项
- 配置server时,server_name所绑定的ip或者域名需要在host文件中添加,要不然会报找不到文件。
- 当访问时出现502时,代表所代理的服务没有启动/或者FQ软件开启了。
- alias和root的区别
- alias
- alias不会拼接location后面配置的路径,会把它丢弃掉,把当前匹配到的目录指向到指定的目录
location /test { alias /myTest/nginxTest; } # 访问地址为:localhost/test/nginxTest.html-->文件目录:/myTest/nginxTest/nginxTest.html
- 使用alias时,目录名后面一定要加"/",否则会找不到文件
- alias在使用正则匹配时,必须捕捉要匹配的内容并在指定的内容处使用
- alias只能位于location块中
- root
- root会拼接location后面配置的路径
location /test { root /myTest/nginxTest; } # 访问地址为:localhost/test/nginxTest.html-->文件目录:/myTest/nginxTest/test/nginxTest.html # 说明root把匹配的字符/test拼接到了文件路径中 # 当root下没有test的文件夹的话会直接报404
- root可以不放在location中,也可以放在http、server、if上下文中
- alias
- Invalid Host header
由于 Vue 的主机检查配置导致的,1. 找到 Vue 项目中的 build 目录下的 webpack.dev.js 文件。2. 在 devServer 下添加 disableHostCheck: true ,即跳过检查,如此访问 Vue 项目时就不会进行主机检查。3.重启项目。
参考资料
(8分钟带你深入浅出搞懂Nginx)[https://zhuanlan.zhihu.com/p/34943332]
(Nginx中文文档)[https://www.nginx.cn/doc/index.html]
(Nginx 教程)[https://www.cainiaojc.com/nginx/nginx-index.html]