参考Nginx学习 | 何叨叨的个人博客 (heyingjiee.github.io)和尚硅谷Nginx教程-bilibili,感谢作者和尚硅谷。
Nginx介绍
虚拟机安装就略过了。
-
Nginx开源版 http://nginx.org/en/
官方原始的Nginx版本
-
Nginx plus商业版
开箱即用,集成了大量功能
-
Open Resty https://openresty.org/cn/
OpenResty是一个基于Nginx与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。更适用于需要大量二次开发的场景,有极强的扩展性
-
Tengine https://tengine.taobao.org/
由淘宝网发起的Web服务器项目。它在Nginx 的基础上,针对大访问量网站的需求,添加了很多高级功能和特性。Tengine的性能和稳定性已经在大型的网站如淘宝网 ,天猫商城 等得到了很好的检验。相比于Open Resty,扩展性不够强,但是能够满足绝多数使用场景
Nginx安装
下载Nginx官方安装包http://nginx.org/en/download.html
1.解压安装包
上传到虚拟机目录里,我将安装包解压到了/usr/software/
下,使用命令tar -zxvf
进行解压,得到/usr/software/nginx-1.23.3
。
2.安装依赖库
#安装C编译器
yum install -y gcc
#安装pcre库
yum install -y pcre pcre-devel
#安装zlib
yum install -y zlib zlib-devel
3.安装
./configure --prefix=/usr/local/nginx #使用prefix选项指定安装的目录
make
make install
4.启动
cd /usr/local/nginx/sbin
ls # 里面是一个nginx的可执行文件
./nginx # 启动这个可执行
5.关闭防火墙
systemctl stop firewalld
6.补充Nginx命令
./nginx -s stop #快速停止
./nginx -s quit #完成已接受的请求后,停止
./nginx -s reload #重新加载配置
./nginx -t #检查nginx配置是否正确
7.查看nginx状态
ps -ef | grep nginx
启动时:
可以看到master进程和worker进程都存在。如果nginx处在停止状态,则只会有最下面一个的进程,master
和worker
进程都不会存在。
8.注册系统服务,通过系统服务的方式启动nginx
(1)创建服务脚本:vim /usr/lib/systemd/system/nginx.service
(2)其中的内容为:
如果安装的目录不是在/usr/local/nginx下的话,需要修改目录地址。
[Unit]
Description=nginx
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
PIDFile=/usr/local/nginx/logs/nginx.pid
ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s stop
ExecQuit=/usr/local/nginx/sbin/nginx -s quit
PrivateTmp=true
[Install]
WantedBy=multi-user.target # 多用户
(3)重新加载系统服务:systemctl daemon-reload
之后启动nginx服务就可以使用systemctl start nginx
,我们可以用systemctl status nginx
查看nginx状态。
设置成开机启动,使用命令systemctl enable nginx.service
Nginx目录
Nginx一般安装在/usr/local/nginx
目录下(安装时--prefix可指定安装目录),目录结构如下。
conf #配置文件
|-nginx.conf # 主配置文件
|-其他配置文件 # 可通过那个include关键字,引入到了nginx.conf生效
html #静态页面
logs
|-access.log #访问日志(每次访问都会记录)
|-error.log #错误日志
|-nginx.pid #进程号,会标记出当前nginx进程的进程号
sbin
|-nginx #主进程文件
*_temp #运行时,生成临时文件
Nginx运行流程
1.首先启动/sbin/nginx进程,开启master进程。
2.master进程会读取并校验配置文件,检查配置文件是否正确。
3.启动worker子进程,此时worker已经获取到了配置文件(conf文件)
4.请求过来之后,worker会根据请求的路径以及配置文件去找对应的响应信息。
worker是进程,会有多个,master进程只有一个用于协调各个worker之间的工作。
Nginx配置文件
后面学习Nginx配置,每次修改配置文件,一定要重载才能生效。systemctl reload nginx
。
将配置文件的注释部分去掉之后,配置文件如下所示:
worker_processes 1; # 启动的worker进程数
events {
worker_connections 1024; #每个worker进程的连接数
}
http {
include mime.types; #include是引入关键字,这里引入了mime.types这个配置文件(同在conf目录下,mime.types是用来定义,请求返回的content-type)
default_type application/octet-stream; #mime.types未定义的,使用默认格式application/octet-stream
sendfile on; #详情,见下文
keepalive_timeout 65; #长链接超时时间
#一个nginx可以启用多个server(虚拟服务器 vhost),每个server就是一个主机
server {
listen 80;#监听端口80
server_name localhost; #接收的域名 或者 主机名
location / {
root html; #根目录指向html目录
index index.html index.htm; #域名/index 指向 index.html index.htm文件
}
error_page 500 502 503 504 /50x.html; # 服务器错误码为500 502 503 504,转到"域名/50x.html"
location = /50x.html {# 指定到html文件夹下找/50x.htm
root html;
}
}
}
核心配置
1.worker_processes:启动的worker子进程的数量,基本是对应到CPU物理的内核数量。
2.events
3.http
(1)include表示引入文件,这里引入了同目录下的mime.types配置文件,其内容为:
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
application/javascript js;
application/atom+xml atom;
application/rss+xml rss;
text/mathml mml;
text/plain txt;
text/vnd.sun.j2me.app-descriptor jad;
text/vnd.wap.wml wml;
text/x-component htc;
image/avif avif;
image/png png;
image/svg+xml svg svgz;
image/tiff tif tiff;
image/vnd.wap.wbmp wbmp;
image/webp webp;
image/x-icon ico;
image/x-jng jng;
image/x-ms-bmp bmp;
font/woff woff;
font/woff2 woff2;
application/java-archive jar war ear;
application/json json;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
application/postscript ps eps ai;
application/rtf rtf;
application/vnd.apple.mpegurl m3u8;
application/vnd.google-earth.kml+xml kml;
application/vnd.google-earth.kmz kmz;
application/vnd.ms-excel xls;
application/vnd.ms-fontobject eot;
application/vnd.ms-powerpoint ppt;
application/vnd.oasis.opendocument.graphics odg;
application/vnd.oasis.opendocument.presentation odp;
application/vnd.oasis.opendocument.spreadsheet ods;
application/vnd.oasis.opendocument.text odt;
application/vnd.openxmlformats-officedocument.presentationml.presentation
pptx;
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
xlsx;
application/vnd.openxmlformats-officedocument.wordprocessingml.document
docx;
application/vnd.wap.wmlc wmlc;
application/wasm wasm;
application/x-7z-compressed 7z;
application/x-cocoa cco;
application/x-java-archive-diff jardiff;
application/x-java-jnlp-file jnlp;
application/x-makeself run;
application/x-perl pl pm;
application/x-pilot prc pdb;
application/x-rar-compressed rar;
application/x-redhat-package-manager rpm;
application/x-sea sea;
application/x-shockwave-flash swf;
application/x-stuffit sit;
application/x-tcl tcl tk;
application/x-x509-ca-cert der pem crt;
application/x-xpinstall xpi;
application/xhtml+xml xhtml;
application/xspf+xml xspf;
application/zip zip;
application/octet-stream bin exe dll;
application/octet-stream deb;
application/octet-stream dmg;
application/octet-stream iso img;
application/octet-stream msi msp msm;
audio/midi mid midi kar;
audio/mpeg mp3;
audio/ogg ogg;
audio/x-m4a m4a;
audio/x-realaudio ra;
video/3gpp 3gpp 3gp;
video/mp2t ts;
video/mp4 mp4;
video/mpeg mpeg mpg;
video/quicktime mov;
video/webm webm;
video/x-flv flv;
video/x-m4v m4v;
video/x-mng mng;
video/x-ms-asf asx asf;
video/x-ms-wmv wmv;
video/x-msvideo avi;
}
对应的分别是类型以及文件后缀名。default_type application/octet-stream; mime.types未定义的,使用默认格式application/octet-stream,即以数据流的方式进行下载,比如exe文件,浏览器是无法加载exe文件的,这个时候就会显示下载。
(2)snedfile表示数据零拷贝,打开sendfile,用户请求的数据不用再加载到nginx的内存中,而是直接发送
若没有开启零拷贝,则是会先拷贝到nginx应用程序的内存中:
nginx将文件读到自己的应用程序内存后,再从自己的应用程序内存里读取文件到网络接口缓存,再由OS调度读取网络接口缓存从而返回给用户。
若开启了零拷贝,则流程为:
nginx直接发送一个带有套接字或者文件地址的信号sendfile()给系统内核,系统内核直接读取文件到网络接口缓存中后返回给用户。
虚拟主机
一台主机可以绑定多个域名,这样可以节省服务器资源,nginx可以根据域名来区分访问的内容,从而到对应的文件夹中寻找响应文件。
为了模拟域名的效果,我们可以先修改本机host,写一些域名绑定到虚拟机上:
为了模拟出多站点的情况,我们可以先创建对应的文件夹,比如在根目录下创建/www
文件夹,其下再创建vod和www文件夹来模拟多站点的情况,每个单独的文件夹下都有一个index.html:
接下来编辑nginx的配置文件(nginx自己有一个nginx.conf.default
文件,如果有写错了的情况,可以用默认的配置文件代替):
worker_processes 1; # 启动的worker进程数
events {
worker_connections 1024; #每个worker进程的连接数
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
#一个nginx可以启用多个server(虚拟服务器 vhost),每个server就是一个主机
server {
listen 80;#监听端口80
server_name localhost;
location / {
root /www/www;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {# 指定到html文件夹下找/50x.htm
root html;
}
}
server {
listen 88;#监听端口88
server_name localhost;
location / {
root /www/vod;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {# 指定到html文件夹下找/50x.htm
root html;
}
}
}
比如80端口监听主站点的,88端口监听的是vod站点的。
修改配置文件后需要reload一下:systemctl reload nginx
那假如说我想都用80端口,根据域名来区分返回的内容,只需要把server_name
改成对应的域名即可,总之只要保证每个server的listen(端口号)+server_name(域名)保持唯一性即可。
多个域名配置到同一个文件夹
可以在server_name后面加上多个域名,每个域名、主机名之间用空格区分开来。比如server_name vod1.test.com vod2.test.com;
,比如这个配置就是将两个域名都配置到一个文件夹下了。
server {
listen 80;#监听端口80
server_name vod1.test.com vod2.test.com;
location / {
root /www/www;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {# 指定到html文件夹下找/50x.htm
root html;
}
}
除了配置固定的主机名外,还可以使用通配符的方式进行配置,比如*.test.com
,www.test.*
,这个可以查看专门的文档
反向代理
反向代理的概念非常简单,用户访问反向代理服务器(nginx)后,把请求转发给对应的业务服务器,随后业务服务器返回结果给nginx,nginx将结果再返回给用户,这是一种典型的隧道式代理,隧道式服务器中,用户与业务服务器不能直接产生连接。
因为隧道式代理非常考验nginx的能力,所以又提出了一种LVS的代理模式,与隧道式的区别在于,nginx转发请求给业务服务器后,业务服务器直接返回结果给用户,即返回结果的时候不再经过nginx。LVS代理模式中,业务服务器只接受从nginx进入的流量,但是发出数据时不受限制,可以发给任意的一个用户。
反向代理的实现方式使用proxy_pass
实现,使用了proxy_pass
后,root
和index
就不需要再配置了。
server {
listen 80;#监听端口80
server_name localhost;
location / {
proxy_pass http://www.atguigu.com;
#root /www/www;
#index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {# 指定到html文件夹下找/50x.htm
root html;
}
}
将本机反向代理至http://www.atguigu.com
,这个时候访问本机地址192.168.247.128时,就会转到尚硅谷的主页并且地址栏不会变化。
不过反向代理是不支持反向代理至https的,不过服务端一般都会对http请求进行重定向至https,所以这个问题不大。
负载均衡
刚刚我们提到nginx作为反向代理服务器时转发请求给对应的业务服务器,这个过程中,nginx可以做负载均衡的功能。负载均衡:把请求,按照一定算法规则,分配给多台业务服务器(即使其中一个坏了/维护升级,还有其他服务器可以继续提供服务)
轮询
负载均衡的配置要与proxy_pass
配置项搭配的使用,我们可以定义一个地址别名,随后使用upstream来配置这个别名的负载均衡。
http{
upstream httpds{
server 192.168.174.133:80; #如果是80端口,可以省略不写
server 192.168.174.134:80;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://httpds;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
使用upstream定义一组地址【在server字段下】,访问localhost,访问都会代理到192.168.174.133:80
和192.168.174.134:80
这两个地址之一,每次访问这两个地址轮着切换(后面讲到,因为默认权重相等)。
设置权重
访问使用哪个地址的权重
upstream httpds{
server 192.168.174.133:80 weight=10;
server 192.168.174.134:80 weight=80;
}
设置down后,可以让这台机器不参与负载均衡,也就是休息。(使用不多)
upstream httpds{
server 192.168.174.133:80 weight=10 down;
server 192.168.174.134:80 weight=80;
}
备用服务器,如果192.168.174.133:80
出现故障,无法提供服务,就用使用backup的这个机器。(使用不多)
upstream httpds{
server 192.168.174.133:80 weight=10;
server 192.168.174.134:80 weight=80 backup;
}
动静分离
当用户请求时,动态请求分配到Tomcat业务服务器,静态资源请求放在Nginx服务器中。可用于中小型的应用。
例子:
- 如果请求的资源地址是
location/
,/
的优先级比较低,如果下面的location没匹配到,就会走http://xxx这个地址的机器 - 如果请求的资源地址是
location/css/*
,就会被匹配到nginx的html目录下的css文件夹中(我们把css静态资源放在这个位置)
server {
listen 80;
server_name localhost;
location / { # /的优先级比较低,如果下面的location没匹配到,就会走http://xxx这个地址的机器
proxy_pass http://xxx;
}
location /css { # root指的是html,location/css指的是root下的css,所以地址就是html/css
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
还可以使用正则表达式来进行匹配,这样把/js
,/css
以及/img
的请求都进行了反向代理。~表示开始正则,*表示任意字符。
location ~*/(js|css|img){
root html;
index index.html index.htm;
}
不过现在项目基本上都是前后端分离了,前端部署到nginx上,后端再另外启动一个服务。
URLRewrite
这个配置在前后端分离中可能是会用到的。rewrite是URL重写的关键指令,根据regex(正则表达式)部分内容,重定向到replacement,结尾是flag标记。
rewrite <regex> <replacement> [flag];
关键字 正则 替代内容 flagt标记
正则:使用正则表达式语句进行规则匹配
替代内容:将正则匹配的内容替换成replacement
flag标记说明:
last #本条规则匹配完成后,继续向下匹配新的1ocation URI规则
break #本条规则匹配完成即终止,不再匹配后面的任何规则
redirect #返回302临重定向,游览器地址会显示跳转后的URL地址
permanent #返回301永久重定向,测览器地址栏会显示跳转后的URL地址
浏览器地址栏访问 xxx/123.html
实际上是访问xxx/index.jsp?pageNum=123
server {
listen 80;
server_name localhost;
location / {
rewrite ^/([0-9]+).html$ /index.jsp?pageNum=$1 break;
proxy_pass http://xxx;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
网关服务器配置
上图中,应用服务器,不能直接被外网访问到,只能通过Nginx服务器进行访问(使用proxy_pass),这时候这台Nginx服务器就成为了网关服务器(承担入口的功能)
所以,我们启动应用服务器的防火墙,设置其只能接受这台Nginx服务器的请求
添加rich规则
firewall-cmd --permanent --add-rich-rule="rule family="ipv4" source address="192.168.174.135" port protocol="tcp" port="8080" accept"
#这里的192.168.174.135是网关 服务器地址
移除rich规则
firewall-cmd --permanent --remove-rich-rule="rule family="ipv4" source address="192.168.174.135" port port="8080" protocol="tcp" accept"
移除和添加规则后都要重启才能生效:firewall-cmd --reload
;可以使用该命令查看所有规则:firewall-cmd --list-all
。
防盗链
当我们请求到一个页面后,这个页面一般会再去请求其中的静态资源,这时候请求头中,会有一个refer字段,表示当前这个请求的来源,我们可以限制指定来源的请求才返回,否则就不返回,这样可以节省资源。
我们可以通过valid_referers none|server_name
,设置有效的refer值。
- none:检测地址没有refer,则有效;比如直接请求图片的时候(即不是在另一个页面里请求图片的情况),配置示例:
valid_referes none 192.168.44.101
,这种情况下资源只允许来自192.168.44.101的页面引用或者直接请求该页面。 - server_name:检测主机地址,refer显示是从这个地址来的,则有效(server_name必须是完整的
http://xxxx
)
注意:if ($invalid_referer)
中if后有个空格,不写就会报错,报错信息如下:
nginx: [emerg] unknown directive "if($invalid_referer)" in /usr/local/nginx/conf/nginx.conf:27
例子:这里设置nginx服务器中的img目录下的图片必须refer为http:192.168.174/133才能访问。
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://xxx;
}
location /img{
valid_referers http:192.168.174/133;
if ($invalid_referer){#无效的
return 403;#返回状态码403
}
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
如果引用这张图片的页面且refer并没有被设置,图片无法加载出来。如果直接访问图片地址,因为没有refer字段指向来源,会直接显示Nginx的页面:
设置盗链图片
将提示图片放在html/img/x.png,访问设置防盗链图片时,就返回这x.png张图,if
和(
之间必须有一个空格。
location /img{
valid_referers http:192.168.174/133;
if ($invalid_referer){#无效的
rewrite ^/ /img/x.png break;
}
root html;
index index.html index.htm;
}
高可用(High Availablity)
我们之前用到的nginx的负载均衡,考虑到的场景是后端服务器如果挂掉的话,把请求转发到正常运作的服务器上,但是设想一个场景:如果nginx挂掉了,那么就没办法把请求转发给后端服务器了,自然也没法儿进行负载均衡了。
那我们拓展一下,在nginx前面再放一层nginx,第一层nginx对第二层nginx做负载均衡,但是这样就会陷入到逻辑怪圈中,万一第一层nginx挂了,那第二层的nginx也访问不到了,自然也访问不到后面的应用服务器了。这个时候我们就提出了高可用的概念。
用户访问时,访问的是一个虚拟IP,keepalived会选定一个主服务器使用这个虚拟IP。
每台机器上的keepalived会相互通信,根据其他机器上的keepalived进程是否存在,判断服务器状态,如果默认的Master停止了,就会在剩下的Backup机器中,竞选出一台Nginx服务器作为Master
安装keepalived
通过yum命令安装:
yum install -y keepalived
yum安装后的配置文件在/etc/keepalived/keepalived.conf
;vrrp_instance
、authentication
、virtual_router_id
、virtual_ipaddress
这几个一样的机器,才算是同一个组里。这个组才会选出一个作为Master机器。
这里我们设置两台机器,分别下载好keepalived,然后进行配置
机器一:
! Configuration File for keepalived
global_defs {
router_id lb1 # 名字与其他配置了keepalive的机器不重复就行
}
vrrp_instance heyingjie {#vrrp实例名可以随意取
state MASTER #只能有一个默认的Master,其他写BACKUP
interface ens33 # ip addr查看下网卡名,默认时ens33
virtual_router_id 51
priority 100 # 多台安装了keepalived的机器竞争成为Master的优先级
advert_int 1 #通信时间
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.200.16 #虚拟IP
}
}
机器二:
! Configuration File for keepalived
global_defs {
router_id lb2
}
vrrp_instance heyingjie {
state BACKUP #只能有一个默认的Master,其他写BACKUP
interface ens33
virtual_router_id 51
priority 50
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.200.16 #虚拟IP
}
}
通过命令ip addr
查看机器一的ip信息,可以看到虚拟IP