Nginx 使用与配置
# 1 什么是nginx
Nginx 是一个高性能的HTTP和反向代理服务,也是一个IMAP/POP3/SMTP服务。
- 处理响应请求很快
- 高并发连接
- 低的内存消耗
- 具有很高的可靠性
- 高扩展性
- 热部署
master 管理进程与 worker 工作进程的分离设计,使得 Nginx 具有热部署的功能,可以在 7 × 24 小时不间断服务的前提下,升级 Nginx 的可执行文件,也可以在不停止服务的情况下修改配置文件,更换日志文件等功能。
# 1.1 可大量并行处理
Nginx 在官方测试的结果中,能够支持五万个并行连接,而在实际的运作中,可以支持二万至四万个并行连接,作为对比Tomcat的并行连接数只有几百个。
# 1.2 与 Apache 相比
Nginx 的编写有一个明确目标就是超越 Apache Web 服务器的性能
Nginx 提供开箱即用的静态文件,使用的内存比 Apache 少得多,每秒可以处理大约四倍于 Apache 的请求,低并发下性能与 Apache 相当,有时候还低于,但是在高并发下 Nginx 能保持低资源低消耗高性能,还有高度模块化的设计,模块编写简单,简洁。
这种性能提升的代价是降低了灵活性,例如能够以每个文件为基础覆盖系统范围的访问设置( Apache 使用.htaccess 文件来完成这个工作,而 Nginx 并没有内置这样的功能),以前,向 Nginx 添加第三方模块需要使用静态链接的模块从源代码重新编译应用程序。在版本 1.9.11 中部分地克服了这一点,增加了动态模块加载。但是,模块仍然必须与 Nginx 同时编译,而不是所有的模块都与这个系统兼容——有些需要更老的静态链接过程。
# 1.2.1 Apache VS Nginx
横坐标代表并发连接数
# 1.2.2 常用web服务器对比
对比项\服务器 | Apache | Nginx | Lighttpd |
---|---|---|---|
Proxy代理 | 非常好 | 非常好 | 一般 |
Rewriter | 好 | 非常好 | 一般 |
Fcgi | 不好 | 好 | 非常好 |
热部署 | 不支持 | 支持 | 不支持 |
系统压力 | 很大 | 很小 | 比较小 |
稳定性 | 好 | 非常好 | 不好 |
安全性 | 好 | 一般 | 一般 |
静态文件处理 | 一般 | 非常好 | 好 |
反向代理 | 一般 | 非常好 | 一般 |
# 1.3 Nginx 模块
整体采用模块化设计是 Nginx 的一个重大特点,甚至 http 服务器核心功能也是一个模块
旧版本的 Nginx 的模块是静态的,添加和删除模块都要对 Nginx 进行重新编译,1.9.11 以及更新的版本已经支持动态模块加载。
高度模块化的设计是 Nginx 的架构基础,Nginx 服务器被分解为多个模块,每个模块就是一个功能模块,只负责自身的功能,模块之间严格遵循“高内聚,低耦合”的原则。
# 1.3.1 核心模块
核心模块是 Nginx 服务器正常运行必不可少的模块,提供错误日志记录、配置文件解析、事件驱动机制、进程管理等核心功能。
# 1.3.2 标准 HTTP 模块
标准 HTTP 模块提供 HTTP 协议解析相关的功能,如:端口配置、网页编码设置、HTTP 响应头设置等。
# 1.3.3 可选 HTTP 模块
可选 HTTP 模块主要用于扩展标准的 HTTP 功能,让 Nginx 能处理一些特殊的服务,如:Flash 多媒体传输、解析 GeoIP 请求、SSL 支持等。
# 1.3.4 邮件服务模块
邮件服务模块主要用于支持 Nginx 的邮件服务,包括对 POP3 协议、IMAP 协议和 SMTP 协议的支持。
# 1.3.5 第三方模块
第三方模块是为了扩展 Nginx 服务器应用,完成开发者自定义功能,如:Json 支持、Lua 支持等。
# 2 nginx应用场景
Nginx是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器;同时也是一个IMAP、POP3、SMTP代理服务器;Nginx可以作为一个HTTP服务器进行网站的发布处理,另外Nginx可以作为反向代理进行负载均衡的实现。
# 2.1 关于代理
此时就涉及到两个角色,一个是被代理角色,一个是目标角色,被代理角色通过这个代理访问目标角色完成一些任务的过程称为代理操作过程;如同生活中的专卖店~客人到adidas专卖店买了一双鞋,这个专卖店就是代理,被代理角色就是adidas厂家,目标角色就是用户。
# 2.1.1 正向代理
说反向代理之前,我们先看看正向代理,正向代理也是大家最常接触的到的代理模式,我们会从两个方面来说关于正向代理的处理模式,分别从软件方面和生活方面来解释一下什么叫正向代理。
在如今的网络环境下,我们如果由于技术需要要去访问国外的某些网站,此时你会发现位于国外的某网站我们通过浏览器是没有办法访问的,此时大家可能都会用一个操作FQ进行访问,FQ的方式主要是找到一个可以访问国外网站的代理服务器,我们将请求发送给代理服务器,代理服务器去访问国外的网站,然后将访问到的数据传递给我们!
上述这样的代理模式称为正向代理,正向代理最大的特点是客户端非常明确要访问的服务器地址;服务器只清楚请求来自哪个代理服务器,而不清楚来自哪个具体的客户端;正向代理模式屏蔽或者隐藏了真实客户端信息。来看个示意图(我把客户端和正向代理框在一块,同属于一个环境,后面我有介绍):
客户端必须设置正向代理服务器,当然前提是要知道正向代理服务器的IP地址,还有代理程序的端口。如图。
总结来说:正向代理,"它代理的是客户端,代客户端发出请求",是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端,客户端必须要进行一些特别的设置才能使用正向代理。
正向代理的用途:
- 访问原来无法访问的资源,如Google
- 可以做缓存,加速访问资源
- 对客户端访问授权,上网进行认证
- 代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息
# 2.1.2 反向代理
明白了什么是正向代理,我们继续看关于反向代理的处理方式,举例如我大天朝的某宝网站,每天同时连接到网站的访问人数已经爆表,单个服务器远远不能满足人民日益增长的购买欲望了,此时就出现了一个大家耳熟能详的名词:分布式部署;也就是通过部署多台服务器来解决访问人数限制的问题;某宝网站中大部分功能也是直接使用Nginx进行反向代理实现的,并且通过封装Nginx和其他的组件之后起了个高大上的名字:Tengine,有兴趣的童鞋可以访问Tengine的官网查看具体的信息:http://tengine.taobao.org/。那么反向代理具体是通过什么样的方式实现的分布式的集群操作呢,我们先看一个示意图(我把服务器和反向代理框在一块,同属于一个环境,后面我有介绍):
通过上述的图解大家就可以看清楚了,多个客户端给服务器发送的请求,Nginx服务器接收到之后,按照一定的规则分发给了后端的业务处理服务器进行处理了,此时~请求的来源也就是客户端是明确的,但是请求具体由哪台服务器处理的并不明确了,Nginx扮演的就是一个反向代理角色。
客户端是无感知代理的存在的,反向代理对外都是透明的,访问者并不知道自己访问的是一个代理,因为客户端不需要任何配置就可以访问。
反向代理,"它代理的是服务端,代服务端接收请求",主要用于服务器集群分布式部署的情况下,反向代理隐藏了服务器的信息。
反向代理的作用:
- 保证内网的安全,通常将反向代理作为公网访问地址,Web服务器是内网
- 负载均衡,通过反向代理服务器来优化网站的负载
# 2.1.3 项目场景
通常情况下,我们在实际项目操作时,正向代理和反向代理很有可能会存在在一个应用场景中,正向代理代理客户端的请求去访问目标服务器,目标服务器是一个反向单利服务器,反向代理了多台真实的业务处理服务器,具体的拓扑图如下:
# 2.1.4 二者区别
截了一张图来说明正向代理和反向代理二者之间的区别,如图。
- 在正向代理中,Proxy和Client同属于一个LAN(图中方框内),隐藏了客户端信息;
- 在反向代理中,Proxy和Server同属于一个LAN(图中方框内),隐藏了服务端信息;
实际上,Proxy在两种代理中做的事情都是替服务器代为收发请求和响应,不过从结构上看正好左右互换了一下,所以把后出现的那种代理方式称为反向代理了。
# 3 Nginx的安装
# 3.1 解编译安装nginx
在centos下有两种安装nginx方式,一种是
yum install
这种方式,还有一种是编译安装,当然第一种方式更简单,但是有一定的缺点,比如说我们需要使用特定的第三方模块,这个时候就需要用编译安装这种方式了。
# 3.1.1 安装编译环境
一般系统中已经装了了make和g++,无须再装
# 安装make
yum -y install autoconf automake make
# 安装g++
yum -y install gcc gcc-c++
# 安装nginx依赖的库
yum -y install wget pcre pcre-devel zlib zlib-devel openssl openssl-devel
# 3.1.2 下载nginx
wget http://nginx.org/download/nginx-1.23.0.tar.gz
# 3.1.3 解压nginx
tar -zxvf nginx-1.21.1.tar.gz
# 目录结构
解压以后进入到这个目录下面可以看到图中的目录
下面解释一下各个文件的意思
首先我们进入到auto目录
cc是用于编译的,对所有的操作系统的判断在os里面,其他所有文件都是为了辅助configure文件在执行的时候去判定支持哪些模块,当前的操作系统有哪些特性可以供nginx使用
然后我们在看图1中,conf是配置文件的示例文件,方便我们在安装完以后可以直接把conf里面的配置文件复制到安装目录下面,CHANGES这个文件里面描述了nginx的哪些特性,CHANGES.ru是一个俄罗斯版本的描述,因为nginx的作者是一个俄罗斯人,configure是一个用来生成中间文件进行编译前的一个必备动作
接下来我们通过
./configure --help | more
命令来查看一下
如果我们不需要指定安装其他模块的话,我们默认只需要执行–prefix就可以了
如果我们需要安装其他模块就需要在编译的时候执行–with 也就是说你需要安装的这个模块默认是没有安装进nginx的,如果我们不需要某些模块这个时候我们就需要执行–without,也就是说without后面跟着的模块默认是安装进nginx的。
# 3.1.4 编译安装
./configure --prefix=/usr/local/nginx
make && make install
# 3.1.5 编译配置
- –prefix指定安装目录
- –with-http_ssl_module安装https模块
- creating objs/Makefile 代表编译成功
- make编译
- make install安装
# 3.1.6 配置环境变量
编辑/etc/profile
vi /etc/profile
在最后一行加入
export PATH=$PATH:/usr/local/nginx/sbin
生效环境变量
source /etc/profile
# 3.1.7 关闭防火墙
systemctl status firewalld
systemctl stop firewalld
systemctl disable firewalld
# 3.1.8 配置Hosts
因为需要通过虚拟主机配置我们需要配置hosts文件
192.168.245.198 www.abc.com
192.168.245.198 www.abc.cn
192.168.245.198 www.bbs.com
192.168.245.198 www.resources.com
192.168.245.198 www.itcast.com
192.168.245.198 www.yyy.com
# 3.2 yum方式
# 3.2.1 添加源
默认情况Centos7中无Nginx的源,最近发现Nginx官网提供了Centos的源地址,因此可以如下执行命令添加源
sudo rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
# 3.2.2 安装Nginx
通过yum search nginx看看是否已经添加源成功。如果成功则执行下列命令安装Nginx。
sudo yum install -y nginx
# 3.2.3 启动Nginx
sudo systemctl start nginx.service
# 3.2.4 设置开机自动运行
sudo systemctl enable nginx.service
# 4 Nginx常用命令
# 4.1 启动
nginx
# 如果执行配置文件
nginx -c nginx.conf #如果不指定,默认为NGINX_HOME/conf/nginx.conf
# 4.2 停止
nginx -s stop
# 4.3 退出
nginx -s quit
# 4.4 关闭
# 查看nginx进程号
ps -aux | grep nginx
# 杀掉进程
kill -9 nginx
# 4.5 重新加载配置文件
nginx -s reload
# 4.6 检查配置文件是否正确
nginx -t -c /路径/nginx.conf
# 4.7 查看nginx的版本信息
nginx -v
# 5 配置文件结构
Nginx配置文件一般位于Nginx安装目录下的conf目录下,整个文件以block形式组合而成,每一个block都使用"{}"大括号来表示,block中可以嵌套其他block层级,其中main层是最高层次。
Nginx配置文件主要有4部分,main(全局设置)、server(主机设置)、upstream(上游服务器设置,主要为反向代理,负载均衡相关配置)和location(url匹配特定位置的设置),每部分包含若干指令。
- Main部分的设置影响其他所有部分的设置;
- Server部分主要用于指定虚拟机主机域名,ip和端口;
- Upstream的指令用于设置一系列的后端服务器,设置反向代理及后端服务器的负载均衡;
- Location部分用于匹配网页位置(如,跟目录“/”,”/images”等)。
它们之间的关系是,server继承main,location继承server,upstream既不会继承指令也不会被继承。
在这四个部分当中,每个部分都包含若干指令,这些指令主要包含Nginx的主模块指令、事件模块指令、HTTP核心模块指令,同时每个部分还可以使用其他HTTP模块指令,例如Http SSL模块、HttpGzip Static模块和Http Addition模块等。
真实的nginx配置文件可能如下
########### 每个指令必须有分号结束。#################
#user administrator administrators; #配置用户或者组,默认为nobody nobody。
#worker_processes 2; #允许生成的进程数,默认为1
#pid /nginx/pid/nginx.pid; #指定nginx进程运行文件存放地址
error_log log/error.log debug; #制定日志路径,级别。这个设置可以放入全局块,http块,server块,级别以此为:debug|info|notice|warn|error|crit|alert|emerg
events {
accept_mutex on; #设置网路连接序列化,防止惊群现象发生,默认为on
multi_accept on; #设置一个进程是否同时接受多个网络连接,默认为off
#use epoll; #事件驱动模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport
worker_connections 1024; #最大连接数,默认为512
}
http {
include mime.types; #文件扩展名与文件类型映射表
default_type application/octet-stream; #默认文件类型,默认为text/plain
#access_log off; #取消服务日志
log_format myFormat '$remote_addr–$remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for'; #自定义格式
access_log log/access.log myFormat; #combined为日志格式的默认值
sendfile on; #允许sendfile方式传输文件,默认为off,可以在http块,server块,location块。
sendfile_max_chunk 100k; #每个进程每次调用传输数量不能大于设定的值,默认为0,即不设上限。
keepalive_timeout 65; #连接超时时间,默认为75s,可以在http,server,location块。
upstream mysvr {
server 127.0.0.1:7878;
server 192.168.10.121:3333 backup; #热备
}
error_page 404 https://www.baidu.com; #错误页
server {
keepalive_requests 120; #单连接请求上限次数。
listen 4545; #监听端口
server_name 127.0.0.1; #监听地址
location ~*^.+$ { #请求的url过滤,正则匹配,~为区分大小写,~*为不区分大小写。
#root path; #根目录
#index vv.txt; #设置默认页
proxy_pass http://mysvr; #请求转向mysvr 定义的服务器列表
deny 127.0.0.1; #拒绝的ip
allow 172.18.5.54; #允许的ip
}
}
}
# 5.1 配置文件位置
nginx配置为简化日常维护而设计,并且提供了简单的手段用于web服务器将来的扩展。
**配置文件是一些文本文件,通常位于nginx安装路径/etc/nginx
或/etc/nginx
,主配置文件通常命名为nginx.conf
,**为了保持整洁,部分配置可以放到单独的文件中,再自动地被包含到主配置文件,但应该注意的是,nginx目前不支持Apache风格的分布式配置文件(如.htaccess文件),所有和nginx行为相关的配置都应该位于一个集中的配置文件目录中。
# 5.2 Nginx的全局配置
user nobody nobody;
worker_processes 2;
error_log logs/error.log notice;
pid logs/nginx.pid;
events{
use epoll;
worker_connections 65536;
}
# user
user是个主模块指令,指定Nginx Worker进程运行用户以及用户组,默认由nobody账号运行。
这个地方如果写错了就会出现获取不到用户的错误
# worker_processes
是个主模块指令,指定了Nginx要开启的进程数,每个Nginx进程平均耗费10M~12M内存,建议指定和CPU的数量一致即可。
这个地方如果配置配置了
worker_processes 2;
那么他的工作进程就有两个
# error_log
是个主模块指令,用来定义全局错误日志文件,日志输出级别有debug、info、notice、warn、error、crit可供选择,其中,debug输出日志最为最详细,而crit输出日志最少。
日志文件路径一般在nginx安装目录的logs目录中
# pid
是个主模块指令,用来指定进程pid的存储文件位置。
进行成和nginx的master的进程号是一致的,只有nginx运行时才存在,如果nginx停止了 pid也会被删除掉
# 5.3 events事件指令
events事件指令是设定Nginx的工作模式及连接数上限:
# use
use是个事件模块指令,用来指定Nginx的工作模式
Nginx支持的工作模式有select、poll、kqueue、epoll、rtsig和/dev/poll,其中select和poll都是标准的工作模式,kqueue和epoll是高效的工作模式,不同的是epoll用在Linux平台上,而kqueue用在BSD系统中,对于Linux系统,epoll工作模式是首选。
# worker_connections
也是个事件模块指令,用于定义Nginx每个进程的最大连接数,默认是1024。
最大客户端连接数由worker_processes和worker_connections决定,即Max_client=worker_processes*worker_connections
在作为反向代理时,max_clients变为:max_clients = worker_processes * worker_connections/4。 进程的最大连接数受Linux系统进程的最大打开文件数限制,在执行操作系统命令“ulimit -n 65536”后worker_connections的设置才能生效
# 6 HTTP服务器配置
Nginx对HTTP服务器相关属性的配置代码如下:
http {
# 引入文件类型映射文件
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"';
# 启动零拷贝提高性能
sendfile on;
# 设置keepalive长连接超时时间
keepalive_timeout 65;
# 引入子配置文件
include /usr/local/openresty/nginx/conf/conf.d/*.conf;
}
下面的代码实现对日志格式的设定:下面详细介绍下这段代码中每个配置选项的含义。
# include
include是个主模块指令,实现对配置文件所包含的文件的设定,可以减少主配置文件的复杂度,可以将其他各个模块的具体配置分散在不同的文件夹中。
# default_type
default_type属于HTTP核心模块指令,这里设定默认类型为二进制流,也就是当文件类型未定义时使用这种方式,例如在没有配置PHP环境时,Nginx是不予解析的,此时,用浏览器访问PHP文件就会出现下载窗口。
# log_format
log_format是Nginx的HttpLog模块指令,用于指定Nginx日志的输出格式。main为此日志输出格式的名称,可以在下面的access_log指令中引用。
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $bytes_sent '
'"$http_referer" "$http_user_agent" '
'"$gzip_ratio"';
log_format download '$remote_addr - $remote_user [$time_local] '
'"$request" $status $bytes_sent '
'"$http_referer" "$http_user_agent" '
'"$http_range" "$sent_http_content_range"';
# 7 Nginx路由匹配
# 7.1 准备工作
# 7.1.1 初始化目录
先初始化以下测试目录
mkdir -p /root/www/nginx/{abc,bbs}/
# 创建Index文件
创建abc目录下得Index文件
vi /root/www/nginx/abc/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>abc</title>
<head>
<body>
<H1>
www.abc.com
</H1>
</body>
</html>
创建bbs目录下得Index文件
vi /root/www/nginx/bbs/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>bbs</title>
<head>
<body>
<H1>
www.bbs.com
</H1>
</body>
</html>
# 7.1.2 编译安装echo
又涉及到
echo
操作的地方需要安装echo模块,使用--add-module
重新编译安装
./configure --prefix=/usr/local/nginx --add-module=/usr/local/nginx/modules/echo-nginx-module-master
make && make install
# 7.2 虚拟主机
所谓虚拟主机,在 Web 服务里就是一个独立的网站站点,这个站点对应独立的域名(也可能是IP 或端口),具有独立的程序及资源,可以独立地对外提供服务供用户访问。
在 Nginx 中,使用一个 server{} 标签来标识一个虚拟主机,一个 Web 服务里可以有多个虚拟主机标签对,即可以同时支持多个虚拟主机站点。
虚拟主机有两种类型:基于域名的虚拟主机、基于IP+端口的虚拟主机。
# 7.2.1 完全匹配虚拟主机
在 conf.d文件夹下创建
vhostserver.conf
配置文件内容如下
server {
listen 80;
charset utf-8;
server_name www.abc.com;
location /{
alias '/root/www/nginx/abc/';
index index.html index.htm;
expires 7d;
}
}
server {
listen 80 ;
charset utf-8;
server_name www.bbs.com;
location /{
alias '/root/www/nginx/bbs/';
index index.html index.htm;
expires 7d;
}
}
# 修改host文件
更新配置文件后配置本地host
192.168.64.150 www.abc.com
192.168.64.150 www.bbs.com
# 访问测试
我们发现访问不同的域名访问的地址是不一样的这样就像是有多个物理机一样
# 7.2.2 通配符配置虚拟主机
虚拟主机还支持通配符方式进行配置
server {
listen 80;
charset utf-8;
server_name *.com;
location /{
default_type text/html;
echo "通配符在前";
}
}
server {
listen 80;
charset utf-8;
server_name www.abc.*;
location /{
default_type text/html;
echo "通配符在后";
}
}
# 访问测试
访问 www.abc.com (opens new window) 发现出现如下页面
# 7.2.3 虚拟主机匹配顺序
通过上面的实验可以得出如下结论
- 最高优先级:完全匹配
- 第二优先级:通配符在前
- 第三优先级:通配符在后
# 7.2.4 默认主机匹配
默认虚拟主机的作用就是:如果有多个访问的域名指向这台web服务器,但某个域名未添加到nginx虚拟主机中,就会访问默认虚拟主机(泛解析)
server {
# 将域名bbs.com 设置为默认虚拟主机
listen 80 default;
charset utf-8;
server_name www.bbs.com;
location /{
alias '/root/www/nginx/bbs/';
index index.html index.htm;
expires 7d;
}
}
# 测试
使用未配置的域名或者IP地址访问
我们发现如果访问一个没有配置的虚拟主机就会跳到bbs的页面
# 8 location的作用
location有“定位”的意思,根据请求不同的URL来进行不同的处理,在虚拟主机中(server),location配置是必不可少的,可以把网站不同的部分定位到不同的处理方式上。
# 8.1 location规则
location区段,通过指定模式来与客户端请求的URI相匹配
允许根据用户请求的URI来匹配定义的各location,匹配到时,此请求将被响应的location配置快中的配置所处理,例如做访问控制等功能
# 语法
location [修饰符] pattern {…}
# 常用的修饰符说明
修饰符 | 功能 |
---|---|
空 | 前缀匹配 能够匹配以需要匹配的路径为前缀的uri |
= | 精确匹配 |
~ | 正则表达式模式匹配,区分大小写 |
~* | 正则表达式模式匹配,不区分大小写 |
^~ | 精确前缀匹配,类似于无修饰符的行为,也是以指定模块开始,不同的是,如果模式匹配,那么就停止搜索其他模式了,不支持正则表达式 |
/ | 通用匹配,任何请求都会匹配到。 |
# 8.2 前缀匹配
没有修饰符表示必须以指定模式开始,指定模式前面没有任何修饰符,直接在location后写需要匹配的uri,它的优先级次于正则匹配
server {
server_name www.itcast.com;
charset utf-8;
location /abc {
default_type text/html;
echo "前缀匹配-abc...";
}
}
那么如下内容可以就可以正确匹配:
- www.itcast.com/abc (opens new window)
- www.itcast.com/abc/ (opens new window)
- www.itcast.com/abc?. (opens new window)…
# 8.3 通用匹配
通用匹配使用一个 / 表示,可以匹配所有请求,一般nginx配置文件最后都会有一个通用匹配规则,当其他匹配规则均失效时,请求会被路由给通用匹配规则处理;如果没有配置通用匹配,并且其他所有匹配规则均失效时,nginx会返回 404 错误
server {
server_name www.itcast.com;
charset utf-8;
location /{
default_type text/html;
echo "通用匹配-default";
}
}
那么如下内容可以就可以正确匹配所有请求
# 8.4 精确匹配
精确匹配使用 = 表示,nginx进行路由匹配的时候,精确匹配具有最高的优先级,请求一旦精确匹配成功nginx会停止搜索其他到匹配项
server {
server_name www.itcast.com;
charset utf-8;
location = /abc {
default_type text/html;
echo "精确匹配-abc-accurate";
}
}
那么如下内容可正确匹配:
如下内容则无法匹配:
# 8.5 精确前缀匹配
精确前缀匹配的优先级仅次于精确匹配,nginx对一个请求精确前缀匹配成功后,停止继续搜索其他到匹配项
server {
server_name www.itcast.com;
charset utf-8;
location ^~ /abc {
default_type text/html;
echo "精确前缀匹配-abc-prefix";
}
}
那么如下内容可以就可以正确匹配:
- www.itcast.com/abc (opens new window)
- www.itcast.com/abc/ (opens new window)
- www.itcast.com/abc?. (opens new window)…
# 8.6 正则表达式
正则匹配分为区分大小写和不区分大小写两种,分别用 ~ 和 ~* 表示;一个请求精确匹配和精确前缀匹配都失败后,如果配置有相关的正则匹配location,nginx会尝试对该请求进行正则匹配。需要说明的是正则匹配之间没有优先级一说,而是按照在配置文件中出现的顺序进行匹配,一旦匹配上一个,就会停止向下继续搜索
# 区分大小写
~:表示指定的正则表达式要区分大小写,如:
server {
server_name www.itcast.com;
charset utf-8;
location ~ ^/abc$ {
default_type text/html;
echo "正则区分大小写-abc-regular-x";
}
}
那么如下内容可以正确匹配:
如下内容则无法匹配:
- www.itcast.com/abc/ (opens new window)
- www.itcast.com/ABC (opens new window)
- www.itcast.com/abcde (opens new window)
# 不区分大小写
~*:表示指定的正则表达式不区分大小写,如:
server {
server_name www.itcast.com;
charset utf-8;
location ~* ^/abc$ {
default_type text/html;
echo "正则不区分大小写-abc-regular-Y";
}
}
那么如下内容就可以正确匹配:
- www.itcast.com/abc (opens new window)
- www.itcast.com/abc?. (opens new window)…
- www.itcast.com/ABC (opens new window)
如下内容则无法匹配:
# 8.7 完整例子
server {
server_name www.itcast.com;
default_type text/html;
charset utf-8;
location = / {
echo "规则A";
}
location = /login {
echo "规则B";
}
location ^~ /static/ {
echo "规则C";
}
location ^~ /static/files {
echo "规则X";
}
location ~ \.(gif|jpg|png|js|css)$ {
echo "规则D";
}
location ~* \.js$ {
echo "规则E";
}
location /img {
echo "规则Y";
}
location / {
echo "规则F";
}
}
请求uri | 匹配路由规则 |
---|---|
http://www.itcast.com/ | 规则A |
http://www.itcast.com/login | 规则B |
http://www.itcast.com/register | 规则F |
http://www.itcast.com/static/a.html | 规则C |
http://www.itcast.com/static/files/a.txt | 规则X |
http://www.itcast.com/a.png | 规则D |
http://www.itcast.com/a.PNG | 规则F |
http://www.itcast.com/img/a.gif | 规则D |
http://www.itcast.com/img/a.tiff | 规则Y |
# 8.8 匹配顺序
匹配顺序和优先级,由高到底依次为:
- 带有“=”的精确匹配优先
- 正则表达式
- 没有修饰符的精确匹配
具体匹配规则如下:
- =精准匹配命中时,停止location动作,直接走精准匹配,
- 一般匹配(含精确前缀匹配)命中时,先收集所有的普通匹配,最后对比出最长的那一条
- 如果最长的那一条普通匹配声明为精确前缀匹配,直接此条匹配,停止location
- 如果最长的那一条普通匹配不是精确前缀匹配,继续往下走正则location
- 按代码顺序执行正则匹配,当第一条正则location命中时,停止location
注:有多个正则表达式出现时,按照它们在配置文件中定义的顺序
# 8.9 path匹配过程
假设http请求路径为 http://192.168.0.132:8088/mvc/index?id=2 ,匹配过程如下:
- 将整个url拆解为域名/端口/path/params
- 先由域名/端口,对应到目标server虚拟主机
- path部分参与location匹配,path = path1匹配部分 + path2剩余部分
- 进入location方法体内部流程。
- 若是静态文件处理,则进入目标目录查找文件:root指令时找path1+path2对应的文件;alias指令时找path2对应的文件
- 若是proxy代理,则形如proxy_pass=ip:port时转发path1+path2路径到tomcat;形如proxy_pass=ip:port/xxx时转发path2路径到tomcat。params始终跟随转发。
# 8.10 实际使用建议
所以实际使用中,个人觉得至少有三个匹配规则定义,如下:
#直接匹配网站根,通过域名访问网站首页比较频繁,使用这个会加速处理,官网如是说。
#这里是直接转发给后端应用服务器了,也可以是一个静态首页
# 第一个必选规则
location = / {
proxy_pass http://tomcat:8080/index
}
# 第二个必选规则是处理静态文件请求,这是nginx作为http服务器的强项
# 有两种配置模式,目录匹配或后缀匹配,任选其一或搭配使用
location ^~ /static/ {
alias /webroot/static/;
}
location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ {
root /webroot/res/;
}
# 第三个规则就是通用规则,用来转发动态请求到后端应用服务器
# 非静态文件请求就默认是动态请求,自己根据实际把握
# 毕竟目前的一些框架的流行,带.php,.jsp后缀的情况很少了
location / {
proxy_pass http://tomcat:8080/
}
# 9 Nginx负载均衡
负载均衡用于从“upstream”模块定义的后端服务器列表中选取一台服务器接受用户的请求,一个最基本的upstream模块是这样的,模块内的server是服务器列表:
#动态服务器组
upstream dynamicserver {
server 172.16.44.47:9001; #tomcat 1
server 172.16.44.47:9002; #tomcat 2
server 172.16.44.47:9003; #tomcat 3
server 172.16.44.47:9004; #tomcat 4
}
在upstream模块配置完成后,要让指定的访问反向代理到服务器列表:
#其他页面反向代理到tomcat容器
location ~.*$ {
index index.jsp index.html;
proxy_pass http://dynamicserver;
}
这就是最基本的负载均衡实例,但这不足以满足实际需求;目前Nginx服务器的upstream模块支持6种方式的分配
完整配置文件如下
upstream dynamicserver {
server 192.168.64.1:9001; #tomcat 1
server 192.168.64.1:9002; #tomcat 2
server 192.168.64.1:9003; #tomcat 3
server 192.168.64.1:9004; #tomcat 4
}
server {
server_name www.itcast.com;
default_type text/html;
charset utf-8;
location ~ .*$ {
index index.jsp index.html;
proxy_pass http://dynamicserver;
}
}
# 9.1 常用参数
参数 | 描述 |
---|---|
server | 反向服务地址 加端口 |
weight | 权重 |
fail_timeout | 与max_fails结合使用。 |
max_fails | 设置在fail_timeout参数设置的时间内最大失败次数,如果在这个时间内,所有针对该服务器的请求都失败了,那么认为该服务器会被认为是停机了 |
max_conns | 允许最大连接数 |
fail_time | 服务器会被认为停机的时间长度,默认为10s |
backup | 标记该服务器为备用服务器,当主服务器停止时,请求会被发送到它这里。 |
down | 标记服务器永久停机了 |
slow_start | 当节点恢复,不立即加入 |
# 9.2 负载均衡策略
在这里,只详细说明Nginx自带的负载均衡策略,第三方不多描述。
负载策略 | 描述 |
---|---|
轮询 | 默认方式 |
weight | 权重方式 |
ip_hash | 依据ip分配方式 |
least_conn | 最少连接方式 |
fair(第三方) | 响应时间方式 |
url_hash(第三方) | 依据URL分配方式 |
# 轮询
最基本的配置方法,上面的例子就是轮询的方式,它是upstream模块默认的负载均衡默认策略,每个请求会按时间顺序逐一分配到不同的后端服务器。
#动态服务器组
upstream dynamicserver {
server 192.168.64.1:9001; #tomcat 1
server 192.168.64.1:9002; #tomcat 2
server 192.168.64.1:9003; #tomcat 3
server 192.168.64.1:9004; #tomcat 4
}
# 注意
- 在轮询中,如果服务器down掉了,会自动剔除该服务器。
- 缺省配置就是轮询策略。
- 此策略适合服务器配置相当,无状态且短平快的服务使用。
配置示例
upstream dynamicserver {
server 192.168.64.1:9001; #tomcat 1
server 192.168.64.1:9002; #tomcat 2
server 192.168.64.1:9003; #tomcat 3
server 192.168.64.1:9004; #tomcat 4
}
server {
server_name www.itcast.com;
default_type text/html;
charset utf-8;
location ~ .*$ {
index index.jsp index.html;
proxy_pass http://dynamicserver;
}
}
# weight
权重方式,在轮询策略的基础上指定轮询的几率
#动态服务器组
upstream dynamicserver {
server 192.168.64.1:9001 weight=2; #tomcat 1
server 192.168.64.1:9002; #tomcat 2
server 192.168.64.1:9003; #tomcat 3
server 192.168.64.1:9004; #tomcat 4
}
weight参数用于指定轮询几率,weight的默认值为1,;weight的数值与访问比率成正比,比如Tomcat 7.0被访问的几率为其他服务器的两倍。
# 注意
- 权重越高分配到需要处理的请求越多。
- 此策略可以与least_conn和ip_hash结合使用。
- 此策略比较适合服务器的硬件配置差别比较大的情况。
# ip_hash
指定负载均衡器按照基于客户端IP的分配方式,这个方法确保了相同的客户端的请求一直发送到相同的服务器,以保证session会话,这样每个访客都固定访问一个后端服务器,可以解决session不能跨服务器的问题
upstream dynamicserver {
ip_hash; #保证每个访客固定访问一个后端服务器
server 192.168.64.1:9001 weight=2; #tomcat 1
server 192.168.64.1:9002; #tomcat 2
server 192.168.64.1:9003; #tomcat 3
server 192.168.64.1:9004; #tomcat 4
}
# 注意
- 在nginx版本1.3.1之前,不能在ip_hash中使用权重(weight)。
- ip_hash不能与backup同时使用
- 此策略适合有状态服务,比如session。
- 当有服务器需要剔除,必须手动down掉。
# least_conn
把请求转发给连接数较少的后端服务器,轮询算法是把请求平均的转发给各个后端,使它们的负载大致相同;但是,有些请求占用的时间很长,会导致其所在的后端负载较高,这种情况下,least_conn这种方式就可以达到更好的负载均衡效果。
upstream dynamicserver {
least_conn; #把请求转发给连接数较少的后端服务器
server 192.168.64.1:9001 weight=2; #tomcat 1
server 192.168.64.1:9002; #tomcat 2
server 192.168.64.1:9003; #tomcat 3
server 192.168.64.1:9004; #tomcat 4
}
# 注意
- 此负载均衡策略适合请求处理时间长短不一造成服务器过载的情况。
# 9.3 重试策略
现在对外服务的网站,很少只使用一个服务节点,而是部署多台服务器,上层通过一定机制保证容错和负载均衡。
# 9.3.1 基础失败重试
为了方便理解,使用了以下配置进行分析(
proxy_next_upstream
没有特殊配置)
# 重试配置
upstream dynamicserver {
server 192.168.64.1:9001 fail_timeout=60s max_fails=3; #Server A
server 192.168.64.1:9002 fail_timeout=60s max_fails=3; #Server B
}
# 配置说明
max_fails=3 fail_timeout=60s
代表在60
秒内请求某一应用失败3
次,认为该应用宕机,后等待60
秒,这期间内不会再把新请求发送到宕机应用,而是直接发到正常的那一台,时间到后再有请求进来继续尝试连接宕机应用且仅尝试1
次,如果还是失败,则继续等待60
秒...以此循环,直到恢复
# 模拟异常
模拟后端异常的方式是直接将对应服务关闭,造成 connect refused 的情况,对应
error
错误。
在最初始阶段,所有服务器都正常,请求会按照轮询方式依次转发给 AB 两个 Server 处理。假设这时 A 节点服务崩溃,端口不通,则会出现这种情况:
- 请求 1 转到 A 异常,再重试到 B 正常处理,A fails +1
- 请求 2 转到 B 正常处理
- 请求 3 转到 A 异常,再重试到 B 正常处理,A fails +1 达到 max_fails 将被屏蔽 60s
- 屏蔽 A 的期间请求都只转给 B 处理,直到屏蔽到期后将 A 恢复重新加入存活列表,再按照这个逻辑执行
如果在 A 的屏蔽期还没结束时,B 节点的服务也崩溃,端口不通,则会出现:
- 请求 1 转到 B 异常,此时所有线上节点异常,会出现:
- AB 节点一次性恢复,都重新加入存活列表
- 请求转到 A 处理异常,再转到 B 处理异常
- 触发 no live upstreams 报错,返回 502 错误
- 所有节点再次一次性恢复,加入存活列表
- 请求 2 依次经过 AB 均无法正常处理, 触发
no live upstreams
报错,返回 502 错误
# 9.3.2 错误重试
有时候我们系统出现
500
等异常的情况下,希望nginx能够到其他的服务器进行重试,我们可以配置那些错误码才进行重试
# 配置说明
在nginx的配置文件中,proxy_next_upstream项定义了什么情况下进行重试,官网文档中给出的说明如下
Syntax: proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | off ...;
Default: proxy_next_upstream error timeout;
Context: http, server, location
默认情况下,当请求服务器发生错误或超时时,会尝试到下一台服务器,还有一些其他的配置项如下:
错误状态 | 描述 |
---|---|
error | 与服务器建立连接,向其传递请求或读取响应头时发生错误; |
timeout | 在与服务器建立连接,向其传递请求或读取响应头时发生超时; |
invalid_header | 服务器返回空的或无效的响应; |
http_500 | 服务器返回代码为500的响应; |
http_502 | 服务器返回代码为502的响应; |
http_503 | 服务器返回代码为503的响应; |
http_504 | 服务器返回代码504的响应; |
http_403 | 服务器返回代码为403的响应; |
http_404 | 服务器返回代码为404的响应; |
http_429 | 服务器返回代码为429的响应(1.11.13); |
non_idempotent | 通常,请求与 非幂等 方法(POST,LOCK,PATCH)不传递到请求是否已被发送到上游服务器(1.9.13)的下一个服务器; 启用此选项显式允许重试此类请求; |
off | 禁用将请求传递给下一个服务器。 |
# 配置说明
这里面我们配置了
500
等错误的时候会进行重试
upstream dynamicserver {
server 192.168.64.1:9001 fail_timeout=60s max_fails=3; #tomcat 1
server 192.168.64.1:9002 fail_timeout=60s max_fails=3; #tomcat 2
}
server {
server_name www.itcast.com;
default_type text/html;
charset utf-8;
location ~ .*$ {
index index.jsp index.html;
proxy_pass http://dynamicserver;
#下一节点重试的错误状态
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
}
}
# 模拟异常
在正常的情况下如果
500
错误会直接出现异常页面,现在我们加入了以上500
重试策略,重试错误的流程和上面流程是一样的
# 9.3.3 backup 服务器
Nginx 支持设置备用节点,当所有线上节点都异常时启用备用节点,同时备用节点也会影响到失败重试的逻辑,因此单独列出来介绍。
# backup 处理逻辑
upstream 的配置中,可以通过
backup
指令来定义备用服务器,其含义如下
- 正常情况下,请求不会转到到 backup 服务器,包括失败重试的场景
- 当所有正常节点全部不可用时,backup 服务器生效,开始处理请求
- 一旦有正常节点恢复,就使用已经恢复的正常节点
- backup 服务器生效期间,不会存在所有正常节点一次性恢复的逻辑
- 如果全部 backup 服务器也异常,则会将所有节点一次性恢复,加入存活列表
- 如果全部节点(包括 backup)都异常了,则 Nginx 返回 502 错误
# 配置说明
upstream dynamicserver {
server 192.168.64.1:9001 fail_timeout=60s max_fails=3; #Service A
server 192.168.64.1:9002 fail_timeout=60s max_fails=3; #Server B
server 192.168.64.1:9003 backup; #backup
}
server {
server_name www.itcast.com;
default_type text/html;
charset utf-8;
location ~ .*$ {
index index.jsp index.html;
proxy_pass http://dynamicserver;
#下一节点重试的错误状态
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
}
}
在最初始阶段,所有服务器都正常,请求会按照轮询方式依次转发给 AB 两个节点处理。当只有 A 异常的情况下,与上文没有 backup 服务器场景处理方式一致,这里就不重复介绍了。
假设在 A 的屏蔽期还没结束时,B 节点的服务也崩溃,端口不通,则会出现:
- 请求 1 转到 B 处理,异常,此时所有线上节点异常,会出现:
- AB 节点一次性恢复,都重新加入存活列表
- 请求转到 A 处理异常,再重试到 B 处理异常,两者 fails 都 +1
- 因 AB 都异常,启用 backup 节点正常处理,并且 AB 节点一次性恢复,加入存活列表
- 请求 2 再依次经过 A、B 节点异常,转到 backup 处理,两者 fails 都达到 max_fails:
- AB 节点都将会被屏蔽 60s,并且不会一次性恢复
- backup 节点正式生效,接下来所有请求直接转到 backup 处理
- 直到 AB 节点的屏蔽到期后,重新加入存活列表
假设 AB 的屏蔽期都还没结束时,C 节点的服务也崩溃,端口不通,则会出现
- 请求 1 转到 C 异常,此时所有节点(包括 backup)都异常,会出现:
- ABC 三个节点一次性恢复,加入存活列表
- 请求转到 A 处理异常,重试到 B 处理异常,最后重试到 C 处理异常
- 触发
no live upstreams
报错,返回 502 错误 - 所有节点再次一次性恢复,加入存活列表
- 请求 2 依次经过 AB 节点异常,重试到 C 异常,最终结果如上个步骤,返回 502 错误
# 9.3.4 限制重试方式
默认配置是没有做重试机制进行限制的,也就是会尽可能去重试直至失败,Nginx 提供了以下两个参数来控制重试次数以及重试超时时间
proxy_next_upstream_tries
:设置重试次数,默认0
表示无限制,该参数包含所有请求 upstream server 的次数,包括第一次后之后所有重试之和;proxy_next_upstream_timeout
:设置重试最大超时时间,默认0
表示不限制,该参数指的是第一次连接时间加上后续重试连接时间,不包含连接上节点之后的处理时间
# 配置说明
upstream dynamicserver {
server 192.168.64.1:9001 fail_timeout=60s max_fails=3; #Server A
server 192.168.64.1:9002 fail_timeout=60s max_fails=3; #Server B
}
server {
server_name www.itcast.com;
default_type text/html;
charset utf-8;
location ~ .*$ {
index index.jsp index.html;
proxy_pass http://dynamicserver;
# 表示重试超时时间是3s
proxy_connect_timeout 3s;
#表示在 6 秒内允许重试 3 次,只要超过其中任意一个设置,Nginx 会结束重试并返回客户端响应
proxy_next_upstream_timeout 6s;
proxy_next_upstream_tries 3;
}
}
# 10 Nginx 常用案例
# 10.1 代理静态文件
server {
listen 10086;
server_name www.itcast.com;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_redirect off;
}
location /data/ {
alias '/usr/local/data/';
#这里是重点,就是代理这个文件夹
expires 7d;
}
}
访问 http://localhost:10086/data/下面的资源就是访问/usr/local/data文件夹的资源
# 10.2 反向代理
server {
listen 80;
server_name www.itcast.com;;
location / {
proxy_pass http://127.0.0.1:8080;
index index.html index.htm .jsp;
}
}
# 10.3 跨域配置
server {
listen 80;
server_name www.itcast.com;
if ( $host ~ (.*).itcast.com){
set $domain $1;##记录二级域名值
}
#是否允许请求带有验证信息
add_header Access-Control-Allow-Credentials true;
#允许跨域访问的域名,可以是一个域的列表,也可以是通配符*
add_header Access-Control-Allow-Origin *;
#允许脚本访问的返回头
add_header Access-Control-Allow-Headers 'x-requested-with,content-type,Cache-Control,Pragma,Date,x-timestamp';
#允许使用的请求方法,以逗号隔开
add_header Access-Control-Allow-Methods 'POST,GET,OPTIONS,PUT,DELETE';
#允许自定义的头部,以逗号隔开,大小写不敏感
add_header Access-Control-Expose-Headers 'WWW-Authenticate,Server-Authorization';
#P3P支持跨域cookie操作
add_header P3P 'policyref="/w3c/p3p.xml", CP="NOI DSP PSAa OUR BUS IND ONL UNI COM NAV INT LOC"';
if ($request_method = 'OPTIONS') {##OPTIONS类的请求,是跨域先验请求
return 204;##204代表ok
}
}
# 10.4 防盗链
通过Referer实现防盗链比较基础,仅可以简单实现方式资源被盗用,构造Referer的请求很容易实现
场景:由于图片链接可以跨域访问,所以图片链接往往被其他网站盗用,从而增加服务器负担;
解决方案:nginx可以通过valid_referers配置进行防盗链配置
# valid_referers 指令
指定合法的来源'referer', 他决定了内置变量$invalid_referer的值,如果referer头部包含在这个合法网址里面,这个变量被设置为0,否则设置为1. 需要注意的是:这里并不区分大小写的.
- 语法: valid_referers none | blocked | server_names | string …;
- 配置段: server, location
# 配置说明
none : 允许没有http_refer的请求访问资源;
blocked : 允许不是http://开头的,不带协议的请求访问资源;
192.168.0.1 : 只允许指定ip来的请求访问资源;
*.google.com:允许
*.google.com
的域名请求访问资源
# 配置代码
# 需要防盗的后缀
location ~* \.(jpg|jpeg|png|gif|bmp|swf|rar|zip|doc|xls|pdf|gz|bz2|mp3|mp4|flv)$
#设置过期时间
expires 30d;
# valid_referers 就是白名单的意思
# 支持域名或ip
# 允许ip 192.168.0.1 的请求
# 允许域名 *.google.com 所有子域名
valid_referers none blocked 192.168.0.1 *.google.com;
if ($invalid_referer) {
# return 403;
# 盗链返回的图片,替换盗链网站所有盗链的图片
rewrite ^/ https://site.com/403.jpg;
}
root /usr/share/nginx/img;
}