目录
nginx介绍
Nginx最大的特点就是性能稳定,支持的并发数高,占用的资源较少。除此之外,还包含如下的功能:
- HTTP服务器:可以作为一个HTTP服务器提供HTTP请求访问;
- 反向代理:代理用户要访问的目标服务器;
- 负载均衡:高并发场景下分流请求到多个服务器上;支持的方式有轮询、权重、ip哈希、url哈希、按响应时间等;
- 动静分离:将静态资源请求与动态后端请求相分离;
正向代理&反向代理
正向代理
在客户端部署代理服务器,代替客户端对外部网络发送和接收消息。客户端发送一个指定目标的请求给代理服务器,代理服务器再发送给目标服务器,目标服务器收到请求后,将响应的内容发送给代理服务器,代理服务器发给客户端。
在正向代理的过程中,代理服务器代替客户端向目标服务器发送请求,目标服务器不知道谁是真正的客户端,不知道访问自己的是一个代理服务器还是客户端。服务器只负责将响应包发送给请求方。
反向代理
在服务器端部署代理服务器(为了区分,将真正响应的服务器称为业务服务器),让代理服务器替业务服务器接收请求或发送响应。客户端发送一个请求给代理服务器,代理服务器接收请求并将请求发送给业务服务器,业务服务器将响应发送给代理服务器,代理服务器再将响应发送给客户端。
对我们来说,客户端对代理是无感知的,客户端不需要任何配置就可以访问,我们只需要把请求发送给反向代理服务器,由反向代理服务器去选择目标服务器获取数据后,再返回给客户端,此时反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器地址,隐藏了真实服务器的地址。(在服务器中配置代理服务器)
在反向代理的过程中,客户端不知道自己请求的是代理服务器还是业务服务器。
简单的说,一般给客户端做代理的都是正向代理,给服务器做代理的就是反向代理。
负载均衡
负载均衡建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。
负载均衡(Load Balance)其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。
简单来说就是:现有的请求使服务器压力太大无法承受,所有我们需要搭建一个服务器集群,去分担原先一个服务器所承受的压力,那现在我们有ABC等等多台服务器,我们需要把请求分给这些服务器,但是服务器可能大小也有自己的不同,所以怎么分?如何分配更好?又是一个问题。
nginx的负载均衡策略可以划分为两大类:内置策略和扩展策略,扩展策略为第三方提供。
内置策略
- 轮询(默认):Nginx根据请求次数,将每个请求均匀分配到每台服务器;
- weight:加权轮询,加权轮询则是在第一种轮询的基础上对后台的每台服务赋予权重,服务器的权重比例越大,被分发到的概率也就越大。
- least_conn:最少连接,将请求分配给连接数最少的服务器。Nginx会统计哪些服务器的连接数最少。
- ip_hash:IP哈希,绑定处理请求的服务器。第一次请求时,根据该客户端的IP算出一个HASH值,将请求分配到集群中的某一台服务器上。后面该客户端的所有请求,都将通过HASH算法,找到之前处理这台客户端请求的服务器,然后将请求交给它来处理。
扩展策略
- fair:按后端服务器的响应时间来分配请求,响应时间短的优先分配。
- url_hash:按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。
默认轮询可能存在一个问题,假如用户在某台服务器上登录了,那么该用户第二次请求的时候,其登录信息会丢失。因为在负载均衡系统中,每次请求都会重新定位到服务器集群中的某一个,那么就会导致已经登录某一个服务器的用户在重新定位到另一个服务器了,所以就会导致丢失登录信息。
因此针对如上问题,可以采用ip_hash方式解决,如果客户已经访问了某个服务器,当用户再次访问时,会将该请求通过哈希算法,自动定位到该服务器。
内置策略
轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
优点:方式简便、成本低廉。
缺点:可靠性低和负载分配不均衡。
upstream mybalance {
server ip地址1;
server ip地址2;
}
weight权重模式(加权轮询)
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。权重越高,在被访问的概率越大
upstream mybalance {
server ip地址1 weight=3;
server ip地址2 weight=7;
}
least_conn
把请求转发给连接数较少的后端服务器。轮询算法是把请求平均的转发给各个后端,使它们的负载大致相同;但是,有些请求占用的时间很长,会导致其所在的后端负载较高。这种情况下,least_conn这种方式就可以达到更好的负载均衡效果。
upstream mybalance {
least_conn;
server ip地址1;
server ip地址2;
}
ip_hash
轮询与权重分配存在一个问题,在系统中,假如用户在某台服务器上登录了,那么该用户第二次请求的时候会导致请求可能会重新定位到服务器集群中的某一个,那么已经登录某一个服务器的用户再重新定位到另一个服务器,其登录信息将会丢失,这样显然是不妥的。基于此,可以采用ip_hash指令解决这个问题,如果客户已经访问了某个服务器,当用户再次访问时,会将该请求通过哈希算法,自动定位到该服务器。(当然前提是计算的哈希值没有冲突的情况下)
每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session不能跨服务器的问题。
upstream mybalance {
ip_hash;
server ip地址1;
server ip地址2;
}
扩展策略
fair
按后端服务器的响应时间来分配请求,响应时间短的优先分配。该负载均衡策略的实现需要安装第三方插件。
upstream dynamic_tsingxin {
fair; #实现响应时间短的优先分配
server ip地址1;
server ip地址2;
}
url_hash
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,要配合缓存命中来使用。同一个资源多次请求,可能会到达不同的服务器上,导致不必要的多次下载,缓存命中率不高,以及一些资源时间的浪费。而使用url_hash,可以使得同一个url(也就是同一个资源请求)会到达同一台服务器,一旦缓存住了资源,再此收到请求,就可以从缓存中读取。
upstream dynamic_tsingxin {
hash $request_uri; #实现每个url定向到同一个后端服务器
hash_method crc32;
server ip地址1;
server ip地址2;
}
Nginx的动静分离
Nginx的静态处理能力很强,但是动态处理能力不足,因此,在企业中常用动静分离技术。动静分离技术其实是采用代理的方式,在server{}
段中加入带正则匹配的location来指定匹配项针对PHP的动静分离:静态页面交给Nginx处理,动态页面交给PHP-FPM模块或Apache处理。
在Nginx的配置中,是通过location配置段配合正则匹配实现静态与动态页面的不同处理方式。
一般来说,静态资源是指JavaScript、CSS、Img等文件,动态资源则是通过PHP、Java等后端语言运行一系列的代码逻辑来获取的。如果是静态资源的请求,就直接让nginx在静态资源目录下面读取,然后返回给客户端。如果是动态资源的请求,则nginx利用反向代理把请求转发给后端应用去处理,然后后端应用将结果返回给nginx,nginx再返回给客户端。
在使用前后端分离之后,可以很大程度的提升静态资源的访问速度,同时减轻后端应用的处理压力。拿PHP之Laravel框架来说,简单的获取一个静态文件,就需要初始化框架代码,这个过程也比较耗时,性价比是很低的
动静分离方式主要有两种,一种是纯粹把静态文件独立成单独的域名,放在独立的服务器上,也是目前主流推崇的方案。另外一种方法就是动态跟静态文件混合在一起发布, 通过 Nginx 配置来分开。
# 动态内容
location ~ \.php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
include fastcgi_params;
}
# 静态内容
location ~ \.(png|jpeg|jpg|js|css|woff|ttf)$ {
expires 1h;
}
Nginx配置文件详解
Nginx 的主配置文件是nginx.conf
,其配置结构如下图所示。
配置文件内容可以有很多很复杂,但是总体来说分为如下几个模块:
- 全局块,配置影响nginx全局的指令。一般有运行nginx服务器的用户组,nginx进程pid存放路径,日志存放路径,配置文件引入,允许生成worker process数等;
- 事件块,配置影响nginx服务器或与用户的网络连接。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等;
- HTTP块,可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type定义,日志自定义,是否使用sendfile传输文件,连接超时时间,单连接请求数等;
nginx.conf 结构图可以这样概括:
main # 全局配置,对全局生效
├── events # 配置影响 Nginx 服务器或与用户的网络连接
├── http # 配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置
│ ├── upstream # 配置后端服务器具体地址,负载均衡配置不可或缺的部分
│ ├── server # 配置虚拟主机的相关参数,一个 http 块中可以有多个 server 块
│ ├── server
│ │ ├── location # server 块可以包含多个 location 块,用于匹配 uri
│ │ ├── location
│ │ └── ...
│ └── ...
└── ...
一个 Nginx 配置文件的结构就像 nginx.conf 显示的那样,配置文件的语法规则:
- 配置文件由指令与指令块构成;
- 每条指令以 ; 分号结尾,指令与参数间以空格符号分隔;
- 指令块以 {} 大括号将多条指令组织在一起;
- include 语句允许组合多个配置文件以提升可维护性;
- 使用 # 符号添加注释,提高可读性;
- 使用 $ 符号使用变量;
- 部分指令的参数支持正则表达式;
过多配置可参考nginx documentation
Nginx配置文件详解 - 程序员自由之路 - 博客园 (cnblogs.com)
下载与安装
下载地址:http://nginx.org/en/download.html
windows
直接将下载的 nginx.zip
解压到目录即可使用,然后在该目录下直接使用命令开启:
> # 测试指定配置文件语法的正确性
> nginx.exe -t -c conf/nginx.conf
> # 开启 nginx 服务
> start nginx
Linux
在下载地址下载 nginx.tar.gz
文件,并将其传到Linux服务器上(以CentOS为例),并进行解压操作:
# 进入 nginx 压缩包所在目录
cd nginx 压缩包所在目录
# 解压
tar -zxvf nginx-1.20.2.tar.gz
在编译之前安装nginx所需要的依赖项:
# 安装 nginx 的相关依赖
yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel
注意,如果不安装依赖,直接编译nginx的话,会出现如下报错:
checking for OS + Linux 3.10.0-1160.71.1.el7.x86_64 x86_64 checking for C compiler … not found(编译环境没找到,也就是gcc
)
./configure: error: the HTTP rewrite module requires the PCRE library. You can either disable the module by using --without-http_rewrite_module option, or install the PCRE library into the system, or build the PCRE library statically from the source with nginx by using --with-pcre= path option.(zlib zlib-devel pcre-devel openssl openssl-devel等依赖项没找到
)
生成 Makefile 可编译文件
# 进入解压后的 nginx 目录
cd nginx 目录
# 执行configure脚本,设置安装nginx的初始化配置(--with-http_ssl_module:启动 SSL 的支持),生成 Makefile 可编译文件
./configure --with-http_ssl_module
其他参数设置:
–prefix=PATH:指定 nginx 的安装目录(默认/usr/local/nginx)
–conf-path=PATH:指定 nginx.conf 配置文件路径
–user=NAME:nginx 工作进程的用户
–with-pcre:开启 PCRE 正则表达式的支持
–with-http_ssl_module:启动 SSL 的支持
–with-http_stub_status_module:用于监控 Nginx 的状态
–with-http-realip_module:允许改变客户端请求头中客户端 IP 地址
–with-file-aio:启用 File AIO
–add-module=PATH:添加第三方外部模块
编译和安装
# 编译与安装,&&表示前面执行完,没有错误就继续执行后面的指令
make && make install
常用命令
Windows
nginx -s stop 快速关闭Nginx,可能不保存相关信息,并迅速终止web服务。
nginx -s quit 平稳关闭Nginx,保存相关信息,有安排的结束web服务。
nginx -s reload 因改变了Nginx相关配置,需要重新加载配置而重载。
nginx -s reopen 重新打开日志文件。
nginx -c filename 为 Nginx 指定一个配置文件,来代替缺省的。
nginx -t 不运行,仅仅测试配置文件。nginx 将检查配置文件的语法的正确性,并尝试打开配置文件中所引用到的文件。
nginx -v 显示 nginx 的版本。
nginx -V 显示 nginx 的版本,编译器版本和配置参数
如果想在window上设置为脚本启动,可以在 nginx 安装目录下新添一个启动批处理文件startup.bat,双击即可运行。内容如下:
@echo off
# 如果启动前已经启动nginx并记录下pid文件,会kill指定进程
nginx.exe -s stop
# 测试配置文件语法正确性
nginx.exe -t -c conf/nginx.conf
# 显示版本信息
nginx.exe -v
# 按照指定配置去启动nginx
nginx.exe -c conf/nginx.conf
或者将Windows下将nginx配置成服务并设置开机自启动:将Windows中的nginx.exe创建一个快捷方式,并将快捷方式放到:C:\Users\用户名\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
目录下即可。
Linux
在Linux系统中,安装完成后,需要在安装目录下(默认安装路径为/usr/local/nginx
)运行./sbin/nginx
开启,而关闭需要使用kill
来杀死进程。并且在运行过程中,如果使用默认的端口号80,需要使用root用户(在Linux系统中,1024端口以下只能被root用户所使用)。由于 nginx 的命令在Linux中执行起来比较繁琐。因此可以考虑:
- 使用systemctl方式管理nginx
- 编写执行脚本,用于管理nginx,并将该脚本用于全局环境变量。
使用systemctl方式管理nginx
首先创建配置文件,如果需要使用systemctl接管nginx,则需要在目录/usr/lib/systemd/system/
目录下创建一个nginx.service
文件
sudo vim /usr/lib/systemd/system/nginx.service
编辑内容如下:
[Unit]
Description=nginx - high performance web server
Documentation=http://nginx.org/en/docs/
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target
[Service]
Type=forking
PIDFile=/usr/local/nginx/logs/nginx.pid
ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
[Install]
WantedBy=multi-user.target
然后就可以通过systemctl
服务来接管了
systemctl status nginx # 查看nginx的状态
systemctl start nginx # 开启nginx服务
systemctl stop nginx # 关闭nginx服务
应用
静态站点
配置静态站点,即将 html文件和一堆静态资源在服务器上进行部署,比如将所有的静态资源都放在了 /app/dist
目录下,只需要在 nginx.conf
中指定首页以及这个站点的 host 即可。配置如下:
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
gzip on;
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/javascript image/jpeg image/gif image/png;
gzip_vary on;
server {
listen 80;
server_name static.zp.cn;
location / {
root /app/dist;
index index.html;
#转发任何请求到 index.html
}
}
}
搭建文件服务器
有时候,团队需要归档一些数据或资料,那么文件服务器必不可少。使用 Nginx 可以非常快速便捷的搭建一个简易的文件服务。
Nginx 中的配置要点:
- 将 autoindex 开启可以显示目录,默认不开启。
- 将 autoindex_exact_size 开启可以显示文件的大小。
- 将 autoindex_localtime 开启可以显示文件的修改时间。
- root 用来设置开放为文件服务的根路径。
- charset 设置为
charset utf-8,gbk;
,可以避免中文乱码问题(windows 服务器下设置后,依然乱码,本人暂时没有找到解决方法)。
一个配置如下:
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8080; # 监听端口
server_name localhost;
# 防止乱码,windows 服务器下设置后,依然乱码,暂时无解
charset utf-8,gbk;
# 登录认证
auth_basic "Restricted site";
auth_basic_user_file /home/user/.httppasswd;
autoindex on; # 显示目录
autoindex_exact_size on; # 显示文件大小
autoindex_localtime on; # 显示文件时间
# 配置根目录为本地的/home/user/my_pdf
# root /home/user/my_pdf;
# 等价于
location / {
root /home/user/my_pdf;
}
}
}
在用户认证过程中,需要生成密钥文件,然后在nginx配置文件中进行配置,如下:
使用openssl算法或者arp1算法生成加密密码,并保存到/home/user/.httppasswd
# 使用openssl加密算法
> echo -n 'admin1:' >> /home/user/.httppasswd
> openssl passwd >> /home/user/.httppasswd
# 使用apr1加密算法对密码加密
> echo -n 'admin2:' >> /home/user/.httppasswd
> openssl passwd -apr1 >> /home/user/.httppasswd
当然也可以使用docker容器来将host主机上的目录的资料进行映射。对于上传业务,可以通过官网上传 |(nginx.com)j教程来学习。
解决跨域
web 领域开发中,经常采用前后端分离模式。这种模式下,前端和后端分别是独立的 web 应用程序,例如:后端是 Java 程序,前端是 React 或 Vue 应用。
各自独立的 web app 在互相访问时,势必存在跨域问题。解决跨域问题一般有两种思路:
- CORS
在后端服务器设置 HTTP 响应头,把你需要允许访问的域名加入 Access-Control-Allow-Origin
中。
- jsonp
把后端根据请求,构造 json 数据,并返回,前端用 jsonp 跨域。
这两种思路,本文不展开讨论。
需要说明的是,nginx 根据第一种思路,也提供了一种解决跨域的解决方案。
举例:www.helloworld.com 网站是由一个前端 app ,一个后端 app 组成的。前端端口号为 9000, 后端端口号为 8080。
前端和后端如果使用 http 进行交互时,请求会被拒绝,因为存在跨域问题。来看看,nginx 是怎么解决的吧:
首先,在 enable-cors.conf 文件中设置 cors :
# allow origin list
set $ACAO '*';
# set single origin
if ($http_origin ~* (www.helloworld.com)$) {
set $ACAO $http_origin;
}
if ($cors = "trueget") {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Credentials' 'true';
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';
}
if ($request_method = 'OPTIONS') {
set $cors "${cors}options";
}
if ($request_method = 'GET') {
set $cors "${cors}get";
}
if ($request_method = 'POST') {
set $cors "${cors}post";
}
接下来,在你的服务器中 include enable-cors.conf
来引入跨域配置:
# ----------------------------------------------------
# 此文件为项目 nginx 配置片段
# 可以直接在 nginx config 中 include(推荐)
# 或者 copy 到现有 nginx 中,自行配置
# www.helloworld.com 域名需配合 dns hosts 进行配置
# 其中,api 开启了 cors,需配合本目录下另一份配置文件
# ----------------------------------------------------
upstream front_server{
server www.helloworld.com:9000;
}
upstream api_server{
server www.helloworld.com:8080;
}
server {
listen 80;
server_name www.helloworld.com;
location ~ ^/api/ {
include enable-cors.conf;
proxy_pass http://api_server;
rewrite "^/api/(.*)$" /$1 break;
}
location ~ ^/ {
proxy_pass http://front_server;
}
}
其他
Nginx的Master-Worker模式
启动Nginx后,其实就是在80端口启动了Socket服务进行监听,如图所示,Nginx涉及Master进程和Worker进程。
Master进程的作用是?
读取并验证配置文件nginx.conf;管理worker进程;
Worker进程的作用是?
每一个Worker进程都维护一个线程(避免线程切换),处理连接和请求;注意Worker进程的个数由配置文件决定,一般和CPU个数相关(有利于进程切换),配置几个就有几个Worker进程。