Nginx
简介
内容参考于:原文链接:https://www.nginx.org.cn/article/detail/545
部分内容参考于:B站尚硅谷
nginx
课程
什么是nginx
?
Nginx
是高性能的HTTP
和反向代理的服务器,处理高并发能力是十分强大的,能经受高负载的考验,有报告表明能支持高达50,000
个并发连接数。
正向代理
一般的访问流程是客户端直接向目标服务器发送请求并获取内容,使用正向代理后,客户端改为向代理服务器发送请求,并指定目标服务器(原始服务器),然后由代理服务器和原始服务器通信,转交请求并获得的内容,再返回给客户端。正向代理隐藏了真实的客户端,为客户端收发请求,使真实客户端对服务器不可见;
举个具体的例子 ,你的浏览器无法直接访问谷哥,这时候可以通过一个代理服务器来帮助你访问谷哥,那么这个服务器就叫正向代理。
反向代理
与一般访问流程相比,使用反向代理后,直接收到请求的服务器是代理服务器,然后将请求转发给内部网络上真正进行处理的服务器,得到的结果返回给客户端。反向代理隐藏了真实的服务器,为服务器收发请求,使真实服务器对客户端不可见。一般在处理跨域请求的时候比较常用。现在基本上所有的大型网站都设置了反向代理。
举个具体的例子 ,去饭店吃饭,可以点川菜、粤菜、江浙菜,饭店也分别有三个菜系的厨师 ,但是你作为顾客不用管哪个厨师给你做的菜,只用点菜即可,小二将你菜单中的菜分配给不同的厨师来具体处理,那么这个小二就是反向代理服务器。
简单的说,一般给客户端做代理的都是正向代理,给服务器做代理的就是反向代理。
简单请求
对于简单请求,浏览器会在头信息中增加 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
配置解除这个限制。
# 同源的例子
http://example.com/app1/index.html # 只是路径不同
http://example.com/app2/index.html
http://Example.com:80 # 只是大小写差异
http://example.com
# 不同源的例子
http://example.com/app1 # 协议不同
https://example.com/app2
http://example.com # host 不同
http://www.example.com
http://myapp.example.com
http://example.com # 端口不同
http://example.com:8080
安装
安装pcre
依赖
wget http://downloads.sourceforge.net/project/pcre/pcre/8.37/pcre-8.37.tar.gz
# 解压
tar –xvf pcre-8.37.tar.gz
./configure
make & make install
pcre-config --version
安装其他依赖
yum -y install make zlib zlib-devel gcc-c++ libtool openssl openssl-devel
安装nginx
,需要提前下载安装文件
- 将文件放在指定的安装目录下
- 解压压缩文件
- 进入解压之后的目录,执行
./configure
- 使用
make & make install
- 安装成功之后,会在
/usr/local/nginx/sbin
下有启动脚本、 - 进入
sbin
文件夹,./nginx
启动
指定防火墙的访问规则
查看开放的端口号
firewall-cmd --list-all
设置开放的端口号
firewall-cmd --add-service=http –permanent
firewall-cmd --add-port=80/tcp --permanent
重启防火墙
firewall-cmd –reload
常用命令
基本命令
# 进入 nginx 目录中
cd /usr/local/nginx/sbin
# 查看 nginx 版本号
./nginx -v
# 启动 nginx
./nginx
# 停止 nginx
./nginx -s stop
# 重新加载 nginx
./nginx -s reload
# 设置自启动
systemctl enable nginx
# 启动
systemctl start 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
进行管理,相关命令如下
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 运行状态
配置文件
主要关注的文件夹有两个:
/etc/nginx/conf.d/
文件夹,是我们进行子配置的配置项存放处,/etc/nginx/nginx.conf
主配置文件会默认把这个文件夹中所有子配置项都引入;
/usr/share/nginx/html/
文件夹,通常静态文件都放在这个文件夹,也可以根据你自己的习惯放其他地方;
配置文件包含三部分内容
-
全局块:从配置文件开始到
events
之间的内容,主要会设置一些影响nginx
服务器整体运行的配置命令配置服务器整体运行的配置指令 比如
worker_processes 1
;处理并发数的配置 -
events
块:events
块涉及的指令主要影响nginx
服务器与用户的网络连接影响
Nginx
服务器与用户的网络连接 比如worker_connections 1024
; 支持的最大连接数为 1024 -
http
块 :nginx
服务器配置中最频繁的部分,代理、缓存和日志定义等绝大多数功能和第三方模块的配置都在这里。还包含两部分:
http
全局块、erver
块
结构图
main # 全局配置,对全局生效
├── events # 配置影响 Nginx 服务器或与用户的网络连接
├── http # 配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置
│ ├── upstream # 配置后端服务器具体地址,负载均衡配置不可或缺的部分
│ ├── server # 配置虚拟主机的相关参数,一个 http 块中可以有多个 server 块
│ ├── server
│ │ ├── location # server 块可以包含多个 location 块,location 指令用于匹配 uri
│ │ ├── location
│ │ └── ...
│ └── ...
└── ...
一个 Nginx
配置文件的结构就像 nginx.conf
显示的那样,配置文件的语法规则:
- 配置文件由指令与指令块构成;
- 每条指令以
;
分号结尾,指令与参数间以空格符号分隔; - 指令块以
{}
大括号将多条指令组织在一起; include
语句允许组合多个配置文件以提升可维护性;- 使用
#
符号添加注释,提高可读性; - 使用
$
符号使用变量; - 部分指令的参数支持正则表达式;
配置参考
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
,语法:
location [ = | ~ | ~* | ^~] uri {
...
}
指令后面:
=
精确匹配路径,用于不含正则表达式的uri
前,如果匹配成功,不再进行后续的查找;^~
用于不含正则表达式的uri
前,表示如果该符号后面的字符是最佳匹配,采用该规则,不再进行后续的查找;~
表示用该符号后面的正则去匹配路径,区分大小写;~*
表示用该符号后面的正则去匹配路径,不区分大小写。跟 ~ 优先级都比较低,如有多个location
的正则能匹配的话,则使用正则表达式最长的那个;
如果 uri
包含正则表达式,则必须要有 ~
或 ~*
标志。
全局变量
Nginx
有一些常用的全局变量,你可以在配置的任何位置使用它们,如下表:
全局变量名 | 功能 |
---|---|
$host | 请求信息中的 Host ,如果请求中没有 Host 行,则等于设置的服务器名,不包含端口 |
$request_method | 客户端请求类型,如 GET 、POST |
$remote_addr | 客户端的 IP 地址 |
$args | 请求中的参数 |
$arg_PARAMETER | GET 请求中变量名 PARAMETER 参数的值,例如:$http_user_agent(Uaer-Agent 值), $http_referer... |
$content_length | 请求头中的 Content-length 字段 |
$http_user_agent | 客户端agent 信息 |
$http_cookie | 客户端cookie 信息 |
$remote_addr | 客户端的IP 地址 |
$remote_port | 客户端的端口 |
$http_user_agent | 客户端agent 信息 |
$server_protocol | 请求使用的协议,如 HTTP/1.0、HTTP/1.1 |
$server_addr | 服务器地址 |
$server_name | 服务器名称 |
$server_port | 服务器的端口号 |
$scheme HTTP | 方法(如http ,https ) |
配置反向代理
首先进入 Nginx
的主配置文件:
vim /etc/nginx/nginx.conf
将对应的地址重定向到B站
server {
listen 80; # 配置监听的端口
server_name localhost; # 配置的域名
location / {
proxy_pass http://www.bilibili.com;
}
}
然后进行:nginx -s reload
重新加载
如果我们需要将不同路径的请求进行反向代理,则可以这样做
- 把访问
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
server {
listen 9001;
server_name *.123.com;
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
。
使用反向代理解决跨域请求
server {
listen 9001;
server_name www.a123.com;
location / {
proxy_pass www.b123.com;
}
}
这里对静态文件的请求和后端服务的请求都以 www.123.com
开始,不易区分,所以为了实现对后端服务请求的统一转发,通常我们会约定对后端服务的请求加上 /apis/
前缀或者其他的 path 来和对静态资源的请求加以区分,此时我们可以这样配置:
# 请求跨域,约定代理后端服务请求path以/apis/开头
location ^~/apis/ {
# 这里重写了请求,将正则匹配中的第一个分组的path拼接到真正的请求后面,并用break停止后续匹配
rewrite ^/apis/(.*)$ /$1 break;
proxy_pass be.sherlocked93.club;
# 两个域名之间cookie的传递与回写
proxy_cookie_domain www.a123.com www.b123.com;
}
配置header
解决跨域问题
# /etc/nginx/conf.d/www.b123.com.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
开启gzip
gzip
是一种常用的网页压缩技术,传输的网页经过gzip
压缩之后大小通常可以变为原来的一半甚至更小(官网原话),更小的网页体积也就意味着带宽的节约和传输速度的提升,特别是对于访问量巨大大型网站来说,每一个静态资源体积的减小,都会带来可观的流量与带宽的节省。
使用 gzip
不仅需要 Nginx
配置,浏览器端也需要配合,需要在请求消息头中包含 Accept-Encoding: gzip
(IE5
之后所有的浏览器都支持了,是现代浏览器的默认设置)。一般在请求 html
和 css
等静态资源的时候,支持的浏览器在 request
请求静态资源的时候,会加上 Accept-Encoding: gzip
这个 header
,表示自己支持 gzip
的压缩方式,Nginx
在拿到这个请求的时候,如果有相应配置,就会返回经过 gzip
压缩过的文件给浏览器,并在 response
相应的时候加上 content-encoding: gzip
来告诉浏览器自己采用的压缩方式(因为浏览器在传给服务器的时候一般还告诉服务器自己支持好几种压缩方式),浏览器拿到压缩的文件后,根据自己的解压方式进行解析。
在 /etc/nginx/conf.d/
文件夹中新建配置文件 gzip.conf
:
# /etc/nginx/conf.d/gzip.conf
gzip on; # 默认off,是否开启gzip
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
# 上面两个开启基本就能跑起了,下面的愿意折腾就了解一下
gzip_static on;
gzip_proxied any;
gzip_vary on;
gzip_comp_level 6;
gzip_buffers 16 8k;
# gzip_min_length 1k;
gzip_http_version 1.1;
gzip_types
:要采用gzip
压缩的MIME
文件类型,其中text/html
被系统强制启用;gzip_static
:默认off
,该模块启用后,Nginx
首先检查是否存在请求静态文件的gz
结尾的文件,如果有则直接返回该.gz
文件内容;gzip_proxied
:默认off
,nginx
做为反向代理时启用,用于设置启用或禁用从代理服务器上收到相应内容gzip
压缩;gzip_vary
:用于在响应消息头中添加Vary:Accept-Encoding
,使代理服务器根据请求头中的Accept-Encoding
识别是否启用gzip
压缩;gzip_comp_level
:gzip
压缩比,压缩级别是1-9
,1
压缩级别最低,9
最高,级别越高压缩率越大,压缩时间越长,建议4-6
;gzip_buffers
:获取多少内存用于缓存压缩结果,16 8k
表示以8k*16
为单位获得;gzip_min_length
:允许压缩的页面最小字节数,页面字节数从header
头中的Content-Length
中进行获取。默认值是 0,不管页面多大都压缩。建议设置成大于1k
的字节数,小于1k
可能会越压越大;gzip_http_version
:默认1.1
,启用gzip
所需的HTTP
最低版本;
负载均衡配置
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
,需要先安装;
配置高可用集群
安装 keepalived
yum install keepalived -y
然后编辑 /etc/keepalived/keepalived.conf
配置文件,并在配置文件中增加 vrrp_script
定义一个外围检测机制,并在 vrrp_instance
中通过定义 track_script
来追踪脚本执行过程,实现节点转移:
global_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
}
}
常用小技巧
配置HTTPS
server {
listen 443 ssl http2 default_server; # SSL 访问端口号为 443
server_name sherlocked93.club; # 填写绑定证书的域名
ssl_certificate /etc/nginx/https/1_sherlocked93.club_bundle.crt; # 证书文件地址
ssl_certificate_key /etc/nginx/https/2_sherlocked93.club.key; # 私钥文件地址
ssl_session_timeout 10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #请按照以下协议配置
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
写完 nginx -t -q
校验一下,没问题就 nginx -s reload
一般还可以加上几个增强安全性的命令:
add_header X-Frame-Options DENY; # 减少点击劫持
add_header X-Content-Type-Options nosniff; # 禁止服务器自动解析资源类型
add_header X-Xss-Protection 1; # 防XSS攻击
静态服务
server {
listen 80;
server_name static.sherlocked93.club;
charset utf-8; # 防止中文文件名乱码
location /download {
alias /usr/share/nginx/html/static; # 静态资源目录
autoindex on; # 开启静态资源列目录
autoindex_exact_size off; # on(默认)显示文件的确切大小,单位是byte;off显示文件大概大小,单位KB、MB、GB
autoindex_localtime off; # off(默认)时显示的文件时间为GMT时间;on显示的文件时间为服务器时间
}
}
图片防盗链
server {
listen 80;
server_name *.sherlocked93.club sherlocked93.club;
# 图片防盗链
location ~* \.(gif|jpg|jpeg|png|bmp|swf)$ {
valid_referers none blocked server_names ~\.google\. ~\.baidu\. *.qq.com; # 只允许本机 IP 外链引用,感谢 @木法传 的提醒,将百度和谷歌也加入白名单
if ($invalid_referer){
return 403;
}
}
}
请求过滤
# 非指定请求全返回 403
if ( $request_method !~ ^(GET|POST|HEAD)$ ) {
return 403;
}
location / {
# IP访问限制(只允许IP是 192.168.0.2 机器访问)
allow 192.168.0.2;
deny all;
root html;
index index.html index.htm;
}