Nginx
Nginx的最重要的几个使用场景
- 静态资源服务,通过本地文件提供服务
- 反向代理服务,延伸出包括缓存,负载均衡等
- API服务,OpenResty
相关概念
简单请求和非简单请求
- 请求方法是HEAD,GET,POST三种之一
- HTTP 头信息不超过右边着几个字段:Accept、Accept-Language、Content-Language、Last-Event-ID
Content-Type 只限于三个值application/x-www-form-urlencoded
、multipart/form-data
、text/plain
;
凡是不同时满足这两个条件的,都属于非简单请求
浏览器处理简单请求和非简单请求的方式不一样:
简单请求:
对于简单请求,浏览器会在头信息中增加 Origin 字段后直接发出,Origin 字段用来说明,本次请求来自的哪个源(协议+域名+端口)。
如果服务器发现 Origin 指定的源不在许可范围内,服务器会返回一个正常的 HTTP 回应,浏览器取到回应之后发现回应的头信息中没有包含 Access-Control-Allow-Origin 字段,就抛出一个错误给 XHR 的 error 事件;
如果服务器发现 Origin 指定的域名在许可范围内,服务器返回的响应会多出几个 Access-Control- 开头的头信息字段。
非简单请求
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是 PUT 或 DELETE,或 Content-Type 值为 application/json。浏览器会在正式通信之前,发送一次 HTTP 预检 OPTIONS 请求,先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 请求方法和头信息字段。只有得到肯定答复,浏览器才会发出正式的 XHR 请求,否则报错。
跨域
在浏览器上当前访问的网站向另一个网站发送请求获取数据的过程就是跨域请求。
跨域是浏览器的同源策略决定的,是一个重要的浏览器安全策略,用于限制一个origin的文档或者它加载的脚本与另一个源的资源进行交互,它能够帮助租个恶意文档,减少可能被攻击的媒介,可以使用CORS配置解除这个限制。
正向代理和反向代理
反向代理:使用反向代理后,直接收到请求的服务器是代理服务器,然后将请求转发给内部网络上真正进行处理的服务器,得到的结果返回给客户端。反向代理隐藏了真实的服务器,为服务器发送请求。一般在处理跨域时比较常用
负载均衡
请求爆发式增长的情况下,单个机器性能再强也无法满足要求了,这个时候集群的概念产生了,单个服务器解决不了问题,可以使用多个服务器,然后将请求分发到各个服务器上,将负载分发到不同的服务器。Nginx实现负载均衡,一般来说指的是将请求转发给服务器集群。
Nginx安装
yun install nginx
nginx -V
查看Nginx版本信息
systemctl start firewalld # 开启防火墙
systemctl stop firewalld # 关闭防火墙
systemctl status firewalld # 查看防火墙开启状态,显示running则是正在运行
firewall-cmd --reload # 重启防火墙,永久打开端口需要reload一下
# 添加开启端口,--permanent表示永久打开,不加是临时打开重启之后失效
firewall-cmd --permanent --zone=public --add-port=8888/tcp
# 查看防火墙,添加的端口也可以看到
firewall-cmd --list-all
设置Nginx开机启动
systemctl enable nginx
启动Nginx
systemctl start nginx
Nginx操作常用命令
Nginx 的命令在控制台中输入 nginx -h 就可以看到完整的命令,这里列举几个常用的命令:
nginx
nginx -s reload # 向主进程发送信号,重新加载配置文件,热重启
nginx -s reopen # 重启 Nginx
nginx -s stop # 快速关闭
nginx -s quit # 等待工作进程处理完成后关闭
nginx -T # 查看当前 Nginx 最终的配置
nginx -t -c # 检查配置是否有问题,如果已经在配置目录,则不需要-c
systemctl 是 Linux 系统应用管理工具 systemd 的主命令,用于管理系统,我们也可以用它来对 Nginx 进行管理,相关命令如下:
nginx
systemctl start nginx # 启动 Nginx
systemctl stop nginx # 停止 Nginx
systemctl restart nginx # 重启 Nginx
systemctl reload nginx # 重新加载 Nginx,用于修改配置后
systemctl enable nginx # 设置开机启动 Nginx
systemctl disable nginx # 关闭开机启动 Nginx
systemctl status nginx # 查看 Nginx 运行状态
nginx配置
nginx 的典型配置
nginx
user nginx; # 运行用户,默认即是nginx,可以不进行设置
worker_processes 1; # Nginx 进程数,一般设置为和 CPU 核数一样
error_log /var/log/nginx/error.log warn; # Nginx 的错误日志存放目录
pid /var/run/nginx.pid; # Nginx 服务启动时的 pid 存放位置
events {
use epoll; # 使用epoll的I/O模型(如果你不知道Nginx该使用哪种轮询方法,会自动选择一个最适合你操作系统的)
worker_connections 1024; # 每个进程允许最大并发数
}
http { # 配置使用最频繁的部分,代理、缓存、日志定义等绝大多数功能和第三方模块的配置都在这里设置
# 设置日志模式
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; # Nginx访问日志存放位置
sendfile on; # 开启高效传输模式
tcp_nopush on; # 减少网络报文段的数量
tcp_nodelay on;
keepalive_timeout 65; # 保持连接的时间,也叫超时时间,单位秒
types_hash_max_size 2048;
include /etc/nginx/mime.types; # 文件扩展名与类型映射表
default_type application/octet-stream; # 默认文件类型
include /etc/nginx/conf.d/*.conf; # 加载子配置项
server {
listen 80; # 配置监听的端口
server_name localhost; # 配置的域名
location / {
root /usr/share/nginx/html; # 网站根目录
index index.html index.htm; # 默认首页文件
deny 172.168.22.11; # 禁止访问的ip地址,可以为all
allow 172.168.33.44; # 允许访问的ip地址,可以为all
}
error_page 500 502 503 504 /50x.html; # 默认50x对应的访问页面
error_page 400 404 error.html; # 同上
}
}
server块可以包含多个Location块,Location指令用于配置uri,语法:
nginx
location [ = | ~ | ~* | ^~] uri {
...
}
指令后面:
=
精确匹配路径,用于不含正则表达式的uri前,如果匹配成功,不再进行后续的查找^~
用于处理不含正则表达式的uri前,如果该符号后面的字符是最佳匹配,采用该规则,不再进行后续的查找。~
表示用该符号后面的正则去匹配路径,区分大小写。~*
表示用该符号后面的去正则匹配路径,不区分大小写。跟~
优先级都比较低
配置反向代理
反向代理是工作中最常用的服务器功能,经常被用来解决跨域问题。
- 首先进入Nginx的主配置文件
Nginx.conf
- 为了看起来方便,把行号显示出来
:set nu
- 然后我们去 http 模块的 server 块中的 location /,增加一行将默认网址重定向到最大学习网站 Bilibili 的 proxy_pass 配置 🤓 :
改完保存退出,nginx -s reload 重新加载,进入默认网址,那么现在就直接跳转到 B 站了,实现了一个简单的代理。
实际使用中,可以将请求转发到本机另一个服务器上,也可以根据访问的路径跳转到不同端口的服务中。
比如我们监听 9001 端口,然后把访问不同路径的请求进行反向代理:
把访问 http://127.0.0.1:9001/edu 的请求转发到 http://127.0.0.1:8080
把访问 http://127.0.0.1:9001/vod 的请求转发到 http://127.0.0.1:8081
这种要怎么配置呢,首先同样打开主配置文件,然后在 http 模块下增加一个 server 块:
nginx
server {
listen 9001;
server_name *.sherlocked93.club;
location ~ /edu/ {
proxy_pass http://127.0.0.1:8080;
}
location ~ /vod/ {
proxy_pass http://127.0.0.1:8081;
}
}
反向代理还有一些其他的指令,可以了解一下:
- proxy_set_header:在将客户端请求发送给后端服务器之前,更改来自客户端的请求头信息;
- proxy_connect_timeout:配置 Nginx 与后端代理服务器尝试建立连接的超时时间
- proxy_read_timeout:配置 Nginx 向后端服务器组发出 read 请求后,等待相应的超时时间;
- proxy_send_timeout:配置 Nginx 向后端服务器组发出 write 请求后,等待相应的超时时间;
- proxy_redirect:用于修改后端服务器返回的响应头中的 Location 和 Refresh。
跨域CORS配置
使用反向代理解决跨域
在前端服务地址为 fe.sherlocked93.club 的页面请求 be.sherlocked93.club 的后端服务导致的跨域,可以这样配置:
nginx
server {
listen 9001;
server_name fe.sherlocked93.club;
location / {
proxy_pass be.sherlocked93.club;
}
}
这样就将对前一个域名 fe.sherlocked93.club 的请求全都代理到了 be.sherlocked93.club,前端的请求都被我们用服务器代理到了后端地址下,绕过了跨域。
这里对静态文件的请求和后端服务的请求都以 fe.sherlocked93.club 开始,不易区分,所以为了实现对后端服务请求的统一转发,通常我们会约定对后端服务的请求加上 /apis/ 前缀或者其他的 path 来和对静态资源的请求加以区分,此时我们可以这样配置:
nginx
# 请求跨域,约定代理后端服务请求path以/apis/开头
location ^~/apis/ {
# 这里重写了请求,将正则匹配中的第一个分组的path拼接到真正的请求后面,并用break停止后续匹配
rewrite ^/apis/(.*)$ /$1 break;
proxy_pass be.sherlocked93.club;
# 两个域名之间cookie的传递与回写
proxy_cookie_domain be.sherlocked93.club fe.sherlocked93.club;
}
这样,静态资源我们使用 fe.sherlocked93.club/xx.html,动态资源我们使用 fe.sherlocked93.club/apis/getAwo,浏览器页面看起来仍然访问的前端服务器,绕过了浏览器的同源策略,毕竟我们看起来并没有跨域。
配置Header解决跨域
当浏览器在访问跨源的服务器时,也可以在跨域的服务器上直接设置Nginx,从而前端就可以无感地开发,不用把实际访问的地址改成前端服务地址
nginx
# /etc/nginx/conf.d/be.sherlocked93.club.conf
server {
listen 80;
server_name be.sherlocked93.club;
add_header 'Access-Control-Allow-Origin' $http_origin; # 全局变量获得当前请求origin,带cookie的请求不支持*
add_header 'Access-Control-Allow-Credentials' 'true'; # 为 true 可带上 cookie
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; # 允许请求方法
add_header 'Access-Control-Allow-Headers' $http_access_control_request_headers; # 允许请求的 header,可以为 *
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000; # OPTIONS 请求的有效期,在有效期内不用发出另一条预检请求
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204; # 200 也可以
}
location / {
root /usr/share/nginx/html/be;
index index.html;
}
}
配置负载均衡
主要配置如下:
nginx
http {
upstream myserver {
# ip_hash; # ip_hash 方式
# fair; # fair 方式
server 127.0.0.1:8081; # 负载均衡目的服务地址
server 127.0.0.1:8080;
server 127.0.0.1:8082 weight=10; # weight 方式,不写默认为 1
}
server {
location / {
proxy_pass http://myserver;
proxy_connect_timeout 10;
}
}
}
Nginx 提供了好几种分配方式,默认为轮询,就是轮流来。有以下几种分配方式:
- 轮询,默认方式,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务挂了,能自动剔除;
- weight,权重分配,指定轮询几率,权重越高,在被访问的概率越大,用于后端服务器性能不均的情况;
- ip_hash,每个请求按访问 IP 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决动态网页 session 共享问题。负载均衡每次请求都会重新定位到服务器集群中的某一个,那么已经登录某一个服务器的用户再重新定位到另一个服务器,其登录信息将会丢失,这样显然是不妥的;
- fair(第三方),按后端服务器的响应时间分配,响应时间短的优先分配,依赖第三方插件 nginx-upstream-fair,需要先安装;
配置高可用集群(双机热备)
当主Nginx服务器宕机之后,切换到备份Nginx服务器
-
首先安装keepalived
yum install keepalived -y
-
然后编辑 /etc/keepalived/keepalived.conf 配置文件,并在配置文件中增加 vrrp_script 定义一个外围检测机制,并在 vrrp_instance 中通过定义 track_script 来追踪脚本执行过程,实现节点转移:
nginxglobal_defs{ notification_email { acassen@firewall.loc } notification_email_from Alexandre@firewall.loc smtp_server 127.0.0.1 smtp_connect_timeout 30 // 上面都是邮件配置,没卵用 router_id LVS_DEVEL // 当前服务器名字,用hostname命令来查看 } vrrp_script chk_maintainace { // 检测机制的脚本名称为chk_maintainace script "[[ -e/etc/keepalived/down ]] && exit 1 || exit 0" // 可以是脚本路径或脚本命令 // script "/etc/keepalived/nginx_check.sh" // 比如这样的脚本路径 interval 2 // 每隔2秒检测一次 weight -20 // 当脚本执行成立,那么把当前服务器优先级改为-20 } vrrp_instanceVI_1 { // 每一个vrrp_instance就是定义一个虚拟路由器 state MASTER // 主机为MASTER,备用机为BACKUP interface eth0 // 网卡名字,可以从ifconfig中查找 virtual_router_id 51 // 虚拟路由的id号,一般小于255,主备机id需要一样 priority 100 // 优先级,master的优先级比backup的大 advert_int 1 // 默认心跳间隔 authentication { // 认证机制 auth_type PASS auth_pass 1111 // 密码 } virtual_ipaddress { // 虚拟地址vip 172.16.2.8 } }
其中检测脚本
nginx_check.sh
nginx#!/bin/bash A=`ps -C nginx --no-header | wc -l` if [ $A -eq 0 ];then /usr/sbin/nginx # 尝试重新启动nginx sleep 2 # 睡眠2秒 if [ `ps -C nginx --no-header | wc -l` -eq 0 ];then killall keepalived # 启动失败,将keepalived服务杀死。将vip漂移到其它备份节点 fi fi
复制一份到备份服务器,备份 Nginx 的配置要将 state 后改为 BACKUP,priority 改为比主机小。
设置完毕后各自 service keepalived start 启动,经过访问成功之后,可以把 Master 机的 keepalived 停掉,此时 Master 机就不再是主机了 service keepalived stop,看访问虚拟 IP 时是否能够自动切换到备机 ip addr。
再次启动 Master 的 keepalived,此时 vip 又变到了主机上。