笔记---Nginx

摩森特沃 2021年03月07日 525次浏览

Nginx简介

Nginx作用

  • 作为HTTP服务器
  • 负载均衡,Nginx提供了多种负载均衡策略,实现了7层负载均衡
  • 反向代理

Nginx安装和全局配置

Nginx安装的步骤

  • configure:用来生成Makefile文件,为make做准备,一般情况下configure会带一些参数,对编译和安装进行控制,如prefix,用来控制程序的安装位置
  • make:对源文件进行编译,生成可执行文件
  • make install:执行Makefile文件中的install标签内容,用来安装make生成的可执行文件

Nginx安装步骤详解

  • 安装环境说明
  • 安装环境为CentOS系统
  • 下载并解压源码
# 下载源码
wget http://nginx.org/download/nginx-1.16.1.tar.gz
# 解压源码
tar -xzvf nginx-1.16.1.tar.gz
  • configure
  • 通用配置项

  • 第三方模块

第三方模块分为两种,一种是默认自动编译到nginx可执行文件中的模块,一种是没有自动编译到nginx可执行文件中

对于前者,我们可以使用--without-XXX_module的方式来取消自动编译。比如--without-http_gzip_module就是不再将gzip压缩模块编译到nginx中。

对于后者,我们可以使用--with--XX_module的方式将模块编译到可执行程序中。比如我们可以通过--with-http_geoip_module命令将地理位置的geoip模块编译到最终的nginx可执行程序中

# 配置nginx,指定安装位置,在这一步中可能会有报错,主要原因是缺少依赖,可根据错误提示进行依赖安装
./configure --prefix=/usr/local/nginx
# 以下为常用依赖的安装命令,并不一定需要都执行
yum install -y gcc-c++
yum install -y pcre pcre-devel
yum install -y zlib zlib-devel
yum install -y openssl openssl-devel
  • make
# 编译
make
  • make install
# 安装
make install

Nginx常用命令

  • 启动Nginx
  • 启动nginx的常用参数
    • -c:指定nginx启动时使用的配置文件,默认为:/user/local/nginx/confi/nginx.conf
    • -t:测试配置文件的语法是否正确
    • -p:指定nginx服务器使用的文件的路径前缀,默认为:/user/local/nginx
    • -g:通过命令指定一些全局配置选项
    • -s:向nginx进程发送信号
  • 可使用以下命令查看nginx是否启动成功
# 访问nginx
curl http://localhost/
  • 停止nginx
  • 快速关闭
# ngixn强制停止服务,master和worker进程收到信号后,会立即结束运行
${NGINX}/sbin/nginx -s stop
  • 优雅的关闭nginx
# 优雅的关闭nginx时,nginx会按照以下三步停止服务
# 关闭监听端口,停止接收新的连接
# nginx处理完当前的所有请求
# 停止nginx服务
${NGINX}/sbin/nginx -s quit
  • 说明:以上两种方式都是对kill命令的封装,因此也可以直接使用kill命令进行关闭操作
  • 重新加载配置文件:${NGINX}/sbin/nginx -s reload
  • 查看版本号:${NGINX}/sbin/nginx -v
  • 查看编译时的配置参数:${NGINX}/sbin/nginx -V

Nginx配置文件

  • 查看配置文件路径的方法
# 第一种方法:通过查看进程的方式查看,在返回的进程信息中会显示配置文件的路径
ps -ef|grep nginx
# 第二种方法:通过-t参数查看配置文件(-t参数的作用是测试配置文件的语法是否正确)
${NGINX}/sbin/nginx -t
  • 配置指令特征和配置结构

指令特征

配置结构

  • 核心配置
  • 设置worker进程的用户,即为使用ps -ef|grep nginx命令看到的worker process对应的用户,会涉及到nginx操作目录或文件的一些权限,默认为nobody,
# user nobody; 
  • worker进程工作数设置,默认为1,一般来说cpu有几个就设置几个或者设置为n-1个
worker_processes  1;
  • 设置nginx进程pid
pid logs/nginx.pid;
  • 设置工作模式
# events级别配置,nginx事件机制
events {
 # 默认使用 epoll,因此以下配置语句也可以不加
 use epoll;

 # 配置每个 woker 允许客户端的最大链接数量
 worker_connections 1024
}
  • http:是针对http网络传输的指令块配置
http { 
   server { 
	# server级别,一个server代表一个虚拟主机
	# location级别,一个location代表一个url
   }
}
# 说明:
# 1,一个http模块可以包含多个server,一个server也可以包含多个>location
# 2,下一层级如果没有配置指令的话,会继承上一层级的同一个配置的值
  • include:可以引入外部文件,提高可读性,避免单个配置文件过大
include proxy.conf;
  • sendfile:使用高效文件传输,提升传输性能。开启后才能使用tcp_nopush,是指当数据表累积到一定大小后才发送,提高了效率
sendfile on;
tcp_nopush on;
  • keepalive_timeout:设置客户端链接服务端超时的时间,也是保持长链接的空闲时间,这里单位是秒,保证客户端多次请求的时候不会重复建立新的连接,避免资源损耗
# keepalive_timeout 0;
keepalive_timeout 65;
  • gzip:开启文件压缩,详见后文Nginx其他特性章节内容
  • server:虚拟主机
# listen:监听端口
# server_name:可以定义 IP 或域名
# location:路由
# error_page:发生错误的时候,使用这里响应的状态码页面展示
# location中的root:root用于指定资源所在的文件夹路径,root + location + 请求的资源,拼接起来组成一个主机上存在的文件的物理路径
# location中的alias:alias用于指定资源所在文件夹路径的别名,在处理请求时,location的路径会被替换为alias指定的文件夹路径,最终alias + 请求的资源 拼接起来组成一个主机上存在的文件的物理路径
以下两种方式均可以访问到/home/imooc下的资源
需要使用 /imooc/资源名称 来访问
location /imooc {
   root /home;
}

需要使用 /static/资源名称 来访问
location /static {
   alias /home/imooc
}

Nginx中的事件

Nginx的Server和Location重写

正则表达式

  • 元字符,元字符是指在正则语法中具有特殊意义的字符,元字符可分为以下几类
  • 匹配特殊位置/字符的元字符
    • ^:匹配一行的开头
    • $:匹配一行的结尾
    • .:匹配除换行符以外的所有字符
    • [abc]:匹配abc中的任意一个字符
    • -:和[]配合使用,用于区间匹配
  • 数量元字符
    • ?:匹配前面的字符0次或者1次
    • +:匹配前面的字符一次或者多次
    • *:匹配前面的字符0次或者多次
    • :匹配前面的字符n次
    • {n,}:匹配前面的字符n次或者多次,至少匹配n次
    • {m,n}:匹配前面的字符的次数在m和n之间
  • 语法糖
    • \w:匹配所有字符,相当于[0-9a-zA-Z_],即匹配数字,字母和下划线
    • \d:相当于[0-9],匹配所有的数字
    • \s:匹配所有的空白字符,包括空格,制表符,换行符
  • 分组捕获和反向引用
    • 分组的概念:用括号把正则表达式括起来
    • 分组的使用:可使用$n表示分组,n为分组的编号,从1开始

Server重写

  • Server支持的三种虚拟主机配置
  • 基于端口号的主机配置
  • 基于ip的主机配置
  • 基于域名的主机配置
  • 举例说明
# 基于端口号的虚拟主机配置
server {
    listen 8081;
    server_name localhost;
    location / {
	# 保存真实文件的路径,此处写的相对路径,实际路径为:${NGINX}/html/port/8081,此处也可以配置为绝对路径,以下各配置与此同理
	root html/port/8081; 
	index index.html index.htm;
    }
}
server {
    listen 8082;
    server_name localhost;
    location / {
	root html/port/8082; 
	index index.html index.htm;
    }
}
# 基于IP的虚拟主机配置
# 测试时可使用ifconfig命令来增加虚拟IP
server {
    listen 127.0.0.2:8081;
    location / {
	root html/ip/2;
	index index.html index.htm;
    }
}
server {
    listen 127.0.0.3:8081;
    location / {
	root html/ip/3;
	index index.html index.htm;
    }
}
# 基于域名的虚拟主机配置
server {
    listen 127.0.0.1:8080;
    server_name www.baidu.com;
    location / {
	root html/domain/baidu/;
	index index.html index.htm;
    }
}
server {
    listen 127.0.0.1:8080;
    server_name www.google.com;
    location / {
	root html/domain/google/;
	index index.html index.htm;
    }
}

Rewrite语法

  • Rewrite模块常用指令

  • Rewrite模块指令解析
  • rewrite_log指令用来控制是否打开日志开关,打开之后就会把每次的rewrite步骤记录到error_log日志中
  • return指令,用来停止后面的处理直接返回
  • rewrite指令,用来指定正则表达式,语法如下
rewirte regex replacement [flag]
# regex:用来匹配当前请求的URI,如果匹配成功,则使用replacement更改URI
# flag:用来控制进一步处理,如果替换字符串replacement以http://,https://或者$scheme开头,则停止处理后续内容,并直接重定向返回给客户端
# flag释义:
# 1,last:停止处理当前的rewrite指令集,并开始搜索与更改后的URI相匹配的location(相当于持续处理的意思)
# 2,break:停止处理当前的rewrite指令集,不会重新搜索匹配location,也不执行后面的return,而是访问并返回已经匹配到的路径,详见举例说明
# 当前指令集的含义:rewrite模块(ngx_http_rewrite_module)实现了一个脚本引擎,脚本引擎执行的过程需要上下文,所有在同一个server级别的rewrite指令集共享同一个上下文,所有在同一个location级别的rewrite指令集共享同一个上下文,当前即表示的是同一个上下文
# rewrite执行顺序如下:
# 1,按照rewrite指令在server中出现的顺序逐个执行,确定最终的URI(最终URI的含义为:在server级别的rewrite全部按照要求执行之后得到的URI,Nginx会使用这个URI查找location)
# 2,循环执行下面的步骤(上一步确定了URI)
# 2.1,根据URI找到匹配的location
# 2.2,按照rewrite指令在location中出现的顺序逐个执行
# 2.3,如果上一步中对URI进行了重写,name重复执行第2步,最多重复执行10次
  • 举例说明
location /first {
    rewrite /first/(.*) /second/$1 last;
    return 200 "hello first\n";
}
location /second {
    rewrite /second/(.*) /third/$1;
    return 200 "hello second\n";
}
location /third {
    return 200 "hellor third\n";
}
# 当使用curl -i http://localhost/first/2.txt时返回hello second
# 当在第二个rewrite最后加上break后,使用curl -i http://localhost/first/3.txt时返回3.txt这个文件中的内容

Location重写

  • Location语法
location [=|~|~*|^~|@] uri {...}
# 分类说明
# = 进行普通字符精确匹配
# ~ 波浪线表示执行一个正则匹配,区分大小写
# ~* 表示执行一个正则匹配,不区分大小写
# ^~ 表示非正则表达式,即普通字符匹配,如果该选项匹配,只匹配该选项,不匹配别的选项,一般用来匹配目录
# @ 定义一个命名的 location,使用在内部定向时,例如 error_page, try_files
  • Location匹配顺序
  1. 精确匹配(=)的优先级最高,如果找到,停止搜索
  2. 所有剩下的常规字符串,最长的匹配,如果这个匹配项使用^~前缀,停止搜索
  3. 正则表达式,按照配置文件中的定义的顺序依次匹配
  4. 如果3中规则有匹配到结果则返回,否则使用2中规则的匹配结果
  • 举例说明
# 精确匹配
location  = / {
  # 只匹配"/".
  [ configuration A ] 
}
# 默认匹配,普通匹配
location  / {
  # 匹配任何请求,因为所有请求都是以"/"开始
  # 但是更长字符匹配或者正则表达式匹配会优先匹配
  [ configuration B ] 
}
# 以固定路径开头的匹配
location ^~ /images/ {
  # 匹配任何以 /images/ 开始的请求,并停止匹配其它location
  [ configuration C ] 
}
# 匹配正则表达式,不区分大小写,只有~时,表示区分大小写
location ~* \.(gif|jpg|jpeg)$ {
  # 匹配以 gif, jpg, or jpeg结尾的请求. 
  # 但是所有 /images/ 目录的请求将由 [Configuration C]处理.   
  [ configuration D ] 
}

Nginx日志

  • 日志格式参数
参数名参数描述
$remote_addr客户端ip地址
$remote_user客户端用户名,一般为“-”,表示无法获取
$time_local访问服务器的时间
$request请求的url、method和协议,非常重要
$status返回的http状态码
$body_bytes_send响应客户端内容字节数,不包含头部信息长度
$upstream_addr提供upstream服务的地址
$http_referer记录用户从哪个链接跳转过来的
$http_user_agent用户所使用的代理,一般都是浏览器
$http_x_forword_for客户端 IP,通过代理转发后的 IP
# 常用日志的配置格式
log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"'
                       '$upstream_addr $upstream_response_time $request_time ';
# 日志格式的名称为main
  • 访问日志配置日志配置
# 访问日志配置格式
access_log path [format [buffer=size]
# 说明:
# path:的访问日志要保存的路径
# format:每条日志记录的格式,也就是上面的log_format定义的格式
# buffer:定义了一个缓冲区的大小,只有当内存中的日志体积大于buffer的时候才会持久化到磁盘,这样可以减少磁盘读写次数,提高效率
# 配置示例如下
access_log  "/usr/local/nginx/logs/access_log"  main;
  • 错误日志配置
# 错误日志的配置格式
error_log file [level];
# 说明:
# level为日志等级,nginx的日志等级
# debug < info < notice < warn < error < crit < alert < emerg
# 配置示例如下:
error_log  "/usr/local/nginx/logs/error_log"  error;
  • 日志分割

现有的日志都是保存到一个文件中的,并没有根据时间等因素做拆分,这样不利于日志的查看,因此往往需要对日志信息进行分割,日志分割的步骤如下

  • 在nginx的sbin目录下创建一个可执行文件shell脚本cut_my_log.sh,内容如下
# 配置示例
#!/bin/bash
LOG_PATH="/var/log/nginx/" 
# 此处为按照分为单位进行分割,可以将其修改为按天、小时之类的单位进行分割
RECORD_TIME=$(date -d "yesterday" +%Y-%m-%d+%H:%M)
PID=/var/run/nginx/nginx.pid 
# 切割访问日志
mv ${LOG_PATH}/access.log ${LOG_PATH}/access.${RECORD_TIME}.log
# 切割错误日志
mv ${LOG_PATH}/error.log ${LOG_PATH}/error.${RECORD_TIME}.log
# 向Nginx主进程发送信号,用于重新打开日志文件
kill -USR1 `cat $PID`
  • 添加可执行权限:chmod u+x cut_my_log.sh
  • 执行脚本,查看结果:./cut_my_log.sh
  • 日志定时分割

日志定时分割需要使用 crontab 来定时执行脚本

# 安装
yum instal -y crontabs

# 使用 crontab -e 命令增加下面一行配置,文件使用绝对路径指向刚刚的脚本文件
*/1 * * * * /usr/local/nginx/sbin/cut_my_log.sh

# 重启定时任务
service crond restart

Nginx进程模型

nginx进程模型分为以下两种

  • master 进程:主进程
  • worker 进程:工作进程,默认为1,可在nginx.conf中通过worker_processes进行配置

Nginx工作方式

在上述工作方式中,master 是用来管理 worker 的,而 work 是用来处理 web 请求的,与浏览器之间保持连接,处理响应请求

这种多进程的模型与多线程的模型相比还是有一些优势的,比如:

  • 进程之间的相互独立的
  • 某个 work 异常了,可以直接干掉,而不会影响其他的 work

请求连接和处理

Nginx 的并发性能很棒,可达到几万甚至几十万,这种高性能主要得益于它的worker 的抢占机制和事件处理机制

worker抢占机制

worker抢占机制

如上图所示:master 监听了 80 端口,并且 fork 出 3 个 wokrer 进程,当 client 一个请求过来时,work 是需要去 抢占一个互斥锁(上图中的 accept_mutex) ,抢占成功的 worker 进行处理

事件处理机制

传统服务器事件处理

传统服务器事件处理

在传统服务器事件处理机制中,最大的问题是同步阻塞。一个 worker 同时只能处理一个 client 请求,当一个请求被阻塞时,就不能处理其他的请求了,只能 fork 出新的 worker 出来,接受新的请求

Nginx事件处理

Nginx事件处理解决问题的关键就是异步非阻塞

Nginx事件处理

当 3 个请求落在一个 work 上时,当一个 client 阻塞时,它会去处理另外一个请求,而不是像传统服务器那样就卡住了。nginx 只需要少量的 work 就可以处理大量的请求

对于默认的并发处理能力,也可以通过 worker_connections 属性配置

events {
  # 默认使用 epoll,因此以下配置语句也可以不加
  use epoll;
  
  # 配置每个 woker 允许客户端的最大链接数量
  worker_connections 1024
}

HTTPS和代理服务器

正向代理和反向代理

  • 正向代理
  • 正向代理的代理对象是客户端,例如当客户端不能直接访问
  • 代理服务器站在客户端那边就是正向代理
  • 举例说明
# 客户端无法直接访问到服务器,通过访问代理服务器,由代理服务器去访问目标服务器,如科学上网
# 在此种情况下,客户端是明确知道访问的目标服务器的,只是自身无法直接访问
  • 反向代理
  • 反向代理的代理对象是服务端
  • 代理服务器站在原始服务器那边就就是反向代理
  • 举例说明
# 常见的情形是负载均衡,客户端不知道访问的真实的目标服务器,只知道访问代理服务器可以获得目标服务器上的内容

Nginx配置https

  • 编译Nginx并安装
  • 可通过-V参数查看Nginx编译时候使用的参数,如果不包含SSL功能,则需要重新编译Nginx
# 执行以下命令进行编译
${NGINX}/configure --prefix=/user/local/nginx/ --with-http_ssl_module
# 执行以下命令进行安装
make
make install
  • 生成证书
  • 使用openssl生成一对证书,具体过程可参照CentOS创建证书
  • 配置conf
# 配置方式如下
# 配置证书
server {
    listen 443 ssl;
    server_name www.test.com;
    
    # 配置ssl证书
    ssl_certificate /key/server.crt;
    # 配置证书秘钥
    ssl_certificate /key/server.key;

    # ssl会话cache
    ssl_session_cache shared:SSL:1m;
    # ssl会话超时时间
    ssl_session_timeout 5m;

    # 配置加密套件,写法遵循openssl标准
    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 html;
	index index.html index.htm;
    }
}

# 配置全站https
server {
    listen 80;
    server_name www.text.com;
    rewrite ^/(.*)$ https://www.localhost.com/$1 permanent;
}
  • 启动Nginx
  • 常规启动Nginx就可以实现使用https进行访问了

Nginx配置反向代理和负载均衡

配置反向代理常用指令解析

proxy_pass # 把特定的请求反向代理到一个服务器组,也可以代理到一个ip,一个url等
proxy_connect_timeout 15s; # nginx跟后端服务器连接超时时间(代理连接超时,单位秒)
proxy_read_timeout 24s; # 连接成功后,后端服务器响应时间(代理接收超时,单位秒)
proxy_send_timeout 600; #后端服务器数据回传时间(代理发送超时,单位秒)
proxy_buffer_size 64k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小
proxy_buffers 4 32k; #proxy_buffers缓冲区,此设置为网页平均在32k以下的设置
proxy_busy_buffers_size 64k; #高负荷下缓冲大小(proxy_buffers*2)
proxy_temp_file_write_size 64k; #设定缓存文件夹大小,大于这个值,将从upstream服务器传

反向代理配置实例

# 创建nginx_proxy.conf,路径为:${NGINX}/conf/nginx_proxy.conf,作为反向代理机的配置文件
worker_processes 1;
events {
    worker_connections 1024;
}
http {
    include mime.types;
    default_type application/octet-stream;
    server {
	listen 8088;
	server_name www.proxy.com;
	access_log /user/local/nginx/logs/proxy_access.log;
	location / {
	    root proxy;
	    index index.html index.htm;
	}
    }
}
# 在nginx.conf中配置前端机,路径为:${NGINX}/conf/nginx.conf
worker_processes 1;
events {
    worker_connections 1024;
}
http {
    include mime.types;
    default_type application/octet-stream;
    server {
	listen 80;
	server_name localhost;
	access_log /user/local/nginx/logs/access.log;
	error_log /user/local/nginx/logs/error.log;
	location / {
	    # 注意 proxy_pass后面的地址必须加上http://,否则会报错
	    proxy_pass http://www.proxy.com:8088/;
	}
    }
}

配置完后,启动两个nginx进程,访问http://localhost/hello.html进行验证(hello.html可替换为任意地址)

上游服务器(upstream)指令参数

  • max_conns:限制链接到 server 的最大连接数,当达到最大链接数量时,新增的链接会被拒绝掉,响应 502 Bad Gateway 信息
  • 注意:如果配置了多个 work 进程,则需要共享配置,链接总数就有可能会超过 max_conns 的限制,这里说需要定义在共享内存中,这个也是一个语法,文档中有描述;但是文档中大概意思是说:如果没有定义共享内存,那么这个限制会在每个 worker 进程中独享配置
  • 示例
upstream tomcats {
 server 192.168.56.106:8080 max_conns=1;
 server 192.168.56.107:8080 max_conns=2;
}
  • slow_start:缓慢的启动,只能用在 集群 和 均衡负载为权重 的场景中,含义是:当该节点不正常或新加入的集群在 time 时间内,将它的权重从 0 逐渐恢复到正常设置的权重值
  • 注意:此指令参数只能用于商业版
  • 示例
upstream tomcats {
 server 192.168.56.106:8080 weight=2 slow_start=60s;
 server 192.168.56.107:8080 weight=1;
}
  • down:将服务器标记为永久不可用。也就是不会被路由到
  • 示例
upstream tomcats {
 server 192.168.56.106:8080 weight=2 down;
 server 192.168.56.107:8080 weight=1;
}
  • backup:标识 server 是一个备用节点,只有当集群内所有非 backup 标识的节点都挂掉之后,才会被路由到
  • 示例
upstream tomcats {
 server 192.168.56.106:8080 weight=2 backup;
 server 192.168.56.107:8080 weight=1;
}
  • max_fails:当与服务器建立连接时出现错误或超时,最大的重试次数,到达最大重试次数后,将认为服务器不可用,默认为1
  • fail_timeout:当服务器不可用时,在指定的时间内将不会再次尝试与该服务器连接,默认时间是10s
  • fail_timeout和max_fail两个参数是配合使用的,当服务器不可用时,会被关到小黑屋 fail_timeout 时间,之后会继续尝试连接该服务器
  • 它们组合出来的效果是:当该节点不可用时(连接不上或连接超时)请求会被转发给其他节点,并不会报错,然后会在 fail_timeout 指定的时间之后,再次尝试连接是否可用
  • 示例
upstream tomcats {
 server 192.168.56.106:8080 weight=2 max_fails=1  fail_timeout=2 ;
 server 192.168.56.107:8080 weight=1;
}
  • Keepalived:设置每个工作进程的高速缓存中保留的到上游服务器的空闲保持连接的最大数量。超过此数量时,将关闭最近最少使用的连接,此配置可用于提高服务器的吞吐量
  • 注意1:要使配置生效,需要配置keepalive 、proxy_http_version和proxy_set_header三项内容,对于非http协议,可不用设置头信息
  • 注意2:该指令设置的数量,并不是限制 nginx 能创建连接的总大小,只是保持长连接的大小,因此不应该设置得特别大
  • 示例
upstream tomcats {
  server 192.168.56.106:8080;
  keepalive 16;
}
server {
  listen       80;
  server_name  www.tomcats.com;

  location / {
     proxy_pass    http://tomcats;
     proxy_http_version 1.1;
     proxy_set_header Connection "";
  }
}

负载均衡模型

Nginx负载均衡

  • 四层负载均衡

四层负载均衡基于 IP + 端口,其原理是通过转发请求实现的,当客户端与后端服务器建立链接之后会将处理连接的服务器记录下来,后续连接的请求会继续由同一台服务器进行处理

四层负载均衡是传输层的,基于 TCP/UDP 协议,性能非常高,目前常用的包括以下四种负载均衡处理方式:

  • F5 硬负载均衡:基于硬件,商业级别的负载均衡,很贵
  • LVS 四层负载均衡:linux 内核的均衡负载,与协议无关,使用最多
  • Haproxy 四层负载均衡:也可以在 7 层做负载均衡
  • Nginx 四层负载均衡:1.9之后的版本支持才支持四层负载均衡,但是一般还是习惯使用 Nginx 作为七层
  • 七层负载均衡

基于 URL、IP 的应用层负载均衡,针对 HTTP 协议的负载均衡,目前常用的包括以下三种负载均衡处理方式

  • Nginx 七层负载均衡
  • Haproxy 七层负载均衡,灵活性很高,当它作为四层均衡负载时,是不处理请求的,只是转发。而在七层时,它会处理请求的
  • apache 七层负载均衡,性能不如 Nginx,达到百万级别后,性能不太行

负载均衡配置

负载均衡配置方式
  • 负载均衡配置需要使用upstream和server指令配合使用
  • upstream指令负责负载均衡策略配置
  • server指令用于指定集群中的后端服务器实例,可以使用域名,IP及端口的方式进行配置
Nginx内置负载均衡策略
  • 轮询(默认策略):就是upstream中的上游服务器中的每个 server 依次处理请求,这种情况下适合每个 server 的硬件配置是一样的(处理能力是一样的)
  • 加权轮询:根据每个server的硬件配置的不同,使用weight配置不同的权重
  • ip_hash

ip_hash

  • 配置示例
upstream backend {
  ip_hash;

  server backend1.example.com;
  server backend2.example.com;
  • ip_hash是根据ip地址的前3段进行hash算法,再对结果进行求模得到对应的应该访问的节点的
  • 注意1:当节点需要临时移除时,可以使用down标记,用这种方法可以让已有的ip的hash值不会发生变化
  • 注意2:当提供服务的节点数最终发生改变时,hash 值计算后与原来的不一致,当大量用户和大量机器的时候,依赖该算法相关的业务都会受到一定的影响,比如缓存,此时需要使用到一致性hash算法来解决

一致性hash

一致性hash算法是将一条上面分布着从 0 开始到 232-1 的值的线变成一个圆圈,然后将服务器节点按照 hash 算法,分布在这个圆环上,当用户访问时,用户的 ip 经过 hash 算法后,也会落在这个圆环上的某一个点上,如果落在的不是在服务器节点上,就顺时针找到一个最近的节点,通过这种方式来确定最终要访问的服务器节点。使用一致性hash算法可以保证当节点增加或者减少时,只会影响到一部分用户的访问

  • url_hash

GlRDYg

  • 配置示例
upstream backend {
  hash $request_uri;

  server backend1.example.com;
  server backend2.example.com;
  • 注意1:官方其实提供的是一个 hash 函数,用来计算 hash 的参数可以是任意值,这里我们使用内置变量获得 url 来计算
  • 注意2:有可能某一个 URL 访问量很高,那么就会导致部分节点过热,部分节点过冷,这种情况下,可以让过热的节点上再做一个集群来分担压力
  • least_conn(最少连接)

8B6HgT

  • 配置示例
upstream backend {
  least_conn;

  server backend1.example.com;
  server backend2.example.com;
  • 其他策略
  • 最少耗时
  • fair
配置实例
  • 负载均衡配置实例
upstream backend {
    # 下面定义了四个后端服务器,形成了一个后端集群,weight为权重,权重默认为1
    server backend1.example.com weight=5;
    # 在单位周期fail_timeout内,异常次数达到3次,则在此周期内,此节点会被标记为不可用,并等待下一个周期再一次取请求,默认fail_timeout为10s,max_fail为1次
    server backend2.example.com max_fails=3;
    server backend3.example.com;
    server 127.0.0.1:8080; #ip+端口的格式
}
  • 负载均衡结合反向代理的配置实例
# 配置虚拟主机8081
http {
    include mime.types;
    default_type application/octet-stream;
    access_log /user/local/nginx/logs/access.log.8801;
    server {
	listen 8081;
	server_name www.backend1.com;
	location /proxy {
	    return 200 "server www.backend1.com:8081 \n";
	}
    }
}
# 配置虚拟主机8082
http {
    include mime.types;
    default_type application/octet-stream;
    access_log /user/local/nginx/logs/access.log.8802;
    server {
	listen 8082;
	server_name www.backend2.com;
	location /proxy {
	    return 200 "server www.backend2.com:8082 \n";
	}
    }
}
# 负载均衡配置
http {
    include mime.types;
    default_type application/octet-stream;
    access_log /user/local/nginx/logs/access.log;
    upstream backend {
	# 后端集群
	server www.backend1.com:8081 weight=3;
	server www.backend2.com:8082;
    }
    server {
	listen 80;
	server_name localhost;
	location /proxy {
	    # 反向代理到后端集群
	    proxy_pass http://backend;
	}
    }
}

Nginx的其他特性

gzip

  • gzip常用指令
gzip # 表示是否使用gzip压缩内容,压缩过后可以提高传输效率,节约带宽
gzip_comp_level # 压缩率,参数是一个1-9的数值,数值越高,压缩比率越大,压缩后的体积越小,则传输速率越快,常用的值为:4-6
gzip_min_length # 限制最小压缩,当传输的内容小于配置的值时不会压缩,单位为字节
gzip_types # 压缩文件类型
# 设置建议:仅对html,js,css文件进行压缩,不对视频,大文件和图片压缩,原因是视频,图片等往往是已经压缩过的,如果再次压缩,效果不好且耗时
# 常用配置如下
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript
  • 配置实例
server {
    listen 80;
    server_name localhost;
    gzip on;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_min_length 256;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml+rss text/javascript
		
    location / {
	root html;
	index index.html index.htm;
	# 以下设置为禁止浏览器端使用缓存,非必须设置,只是禁用缓存后更容易看到效果
	chunked_transfer_encoding off;
	add_header Cache-Control no-cache;
	add_header Cache-Control private;
	expires -1s;
    }
}

Nginx解决跨域

  • 在server虚拟机中增加以下配置
# 允许跨域请求的域, * 代表所有
add_header 'Access-Control-Allow-Origin' *;
# 允许带上 cookie 请求
add_header 'Access-Control-Allow-Credentials' 'true';
# 允许请求的方法,如 GET/POST
add_header 'Access-Control-Allow-Methods' *;
# 允许请求的 header
add_header 'Access-Control-Allow-Headers' *;

示例

server {
   listen       92;
   server_name  localhost;

   # 允许跨域请求的域, * 代表所有
   add_header 'Access-Control-Allow-Origin' *;
   # 允许带上 cookie 请求
   add_header 'Access-Control-Allow-Credentials' 'true';
   # 允许请求的方法,如 GET/POST
   add_header 'Access-Control-Allow-Methods' *;
   # 允许请求的 header
   add_header 'Access-Control-Allow-Headers' *;
   
   location ^~ /images {
       root   /home/foodie-shop/;
   }
}

Nginx缓存

缓存

配置浏览器的缓存策略

通过nginx的配置可控制资源在浏览器端的缓存策略,这种方式缓存是保存在浏览器上的,有如下几种配置方式

  • 配置过期时间:expires [time],过期时间为time后
  • 配置过期时间点:expires @[time],过期时间为time表示的时间点
  • 配置不使用缓存:expires -[time],过期时间为time之前
  • 不设置缓存:expires epoch
  • 不开启缓存,默认设置:expires off,此时浏览器会使用自己的缓存策略
  • 永不过期:expires max

配置示例

location /static {
    alias /home/imooc;
    # 10s后过期
    expires 10s;
    # 晚上10点30分过期
    expires @22h30m;
    # 缓存1个小时前已经失效了,即不会用到缓存
    expires -1h;
    # 不设置缓存
    expires epoch;
    # 不开启缓存,此设置项为默认设置,此时浏览器会使用自己的缓存策略
    expires off;
    # 缓存永不过期
    expires max;
}

配置上游服务器的缓存策略

通过nginx的配置可将上游服务器的资源保存到nginx服务器中,配置方式如下

  • proxy_cache_path:设置缓存保存的目录,需要配置在http配置块中
  • keys_zone:设置共享内存以及占用空间大小
  • max_size:设置缓存大小
  • inactive:超过此时间将被自动清理
  • use_temp_path:临时目录,使用后会影响nginx性能,一般需要关闭,即设置为off
  • proxy_cache:设置启用的缓存,可配置在server和location配置块中
  • proxy_cache_valid:设置缓存的条件和时间,可配置在server和location配置块中

配置示例

upstream tomcats {
    server 192.168.1.173:8080;
    server 192.168.1.174:8080;
}
# 上游服务器缓存配置
proxy_cache_path /usr/local/nginx/upstream_cache keys_zone=mycache:5m max_size=1g inactive=1m use_temp_path=off;

server {
    listen 80;
    server_name www.tomcats.com;
    
    location / {
	proxy_path http://tomcats;
 	# 启用缓存,和keys_zone一致
	proxy_cache mycache;
    	# 针对200和304状态码缓存时间为8小时
	proxy_cache_valid 200 304 8h;
    }
}

Nginx图片防盗链

  • 相关指令
valid_referers # 用于设置一些符合要求的 Referer
$invalid_referer # 是一个变量,如果遇到符合我们设置的 Referer,那么 $invalid_referer 变量的值就是 0,否则就是 1
  • 配置实例
location ~* \.(jpeg|png|gif|jpg)$ {
    # 如果请求的 Referer 为空,或者为 www.test.com,那么 $valud_referer 的值是 0,否则是 1
    valid_referers none www.test.com;
    if ($invalid_referer){
    	# 当 $valud_referer 的值是 1 的时候,会返回一个固定的字符串 don't steal my pic,在真实的线上环境中,通常返回 404
        return 200 "don't steal my pic";
    }
}
# 测试方式,可使用以下命令进行测试
# curl -I -H 'Referer: http://www.test.com' http://www.test.com/1.jpg(图片的地址)

Nginx性能优化

图片描述

  • 具有优化措施
  • CPU优化
    • worker_processes Nginx是一种Master-Worker机制,真正干活的是worker进程,所以适当的提高worker进程的数量,可以提高服务器处理请求的速度,一般建议将值设置为和当前服务器核心数量相同
  • 缓存控制
# 常用指令
client_max_body_size # 客户端请求服务器最大的body大小
client_body_buffer_size # Nginx分配给请求数据的Buffer大小,如果请求的数据小于client_body_buffer_size直接将数据先在内存中存储。如果请求的值大于client_body_buffer_size小于client_max_body_size,就会将数据先存储到临时文件中
client_header_buffer_size # 指定用于请求header的缓存区大小
  • 超时时间
client_body_timeout # 客户端和服务器建立连接之后接收一个完整body体的时间
client_header_timeout # 服务器接收一个完整的请求header的时间

扩展内容

Web服务器三种处理连接的方式

  • 单进程,单进程可以理解为普通的bio模型,且不采用多线程的方式,一次只处理一个请求,实际使用中,基本没有使用单进程的web服务器
  • 多进程,多进程同样可以理解为普通的bio模型,但是采用多线程的机制,当有新的请求时,启动新的线程进行处理,Apache服务器即使用此种方式
  • I/O多路复用,nio的模型,简单说就是一次监听多个I/O操作,当某个I/O准备好之后,就进行相应的操作,nginx即采用此种方式进行连接处理,I/O多路复用实现的函数依赖
  • select:监听到I/O数据准备完成时,需要遍历所有的I/O
  • epoll:监听到I/O数据准备完成时,值需要遍历准备好的I/O,nginx使用此机制
epoll高效的原因:
1,epoll在linux内核中开辟了一个cache缓冲区,使用红黑树结构,并且使用共享内核和应用层进行数据传递,非常高效
2,epoll只对活跃的连接感兴趣,及时同时有几百万的客户端和Nginx连接,同时活跃的连接数也不会很高(客户端的特点),所以epoll可以把所有事件处于ready状态的连接全部返回给用户,避免了用户自己轮询所有连接的过程

网络协议

  • 网络架构

网络模型

  • 七层网络模型中每一层的作用
  • 应用层:这是面向用户的,为了让用户和计算机交互,在计算机里会有很多软件,用户可以通过这些应用软件和计算机交互,交互的过程其实就是接口的调用,应用层为用户提供了交互的接口,以此为用户提供服务,那么在这一层最常见的协议有:HTTP,HTTPS,FTP,SMTP,POP3等。Nginx在本层,为七层负载均衡
    • 举例:我要寄一封信给远在天边的老外LiLei,我会打开快递软件下单,这个时候我是用户,快递软件就是应用服务,是建立在计算上提供给用户交互的一种服务或称之为手段
  • 表示层:该层提供数据格式编码以及加密功能,确保请求段的数据能被响应端的应用层识别
    • 举例:我写中文给LiLei,他看不懂,这个时候我就会使用翻译软件把中文翻译成英文,随后信中涉及到一些比较隐私的信息我会加密,这候翻译软件和加密器就充当了表示层的作用,它用于显示用户能够识别的内容
  • 会话层:会话可以理解为session,请求发送到接受响应的这个过程之间存在会话,会话层就充当了这一过程的管理者,从创建会话到最后销毁会话
    • 举例:我每次写信给LiLei都会记录在一个小本本上,寄信时间日期,收信时间日期,这本小本本上存有每次通信记录,这个小本本就相当于会话的管理者。又或者说,我们平时在打电话,首先需要拨打电话,这是建立会话,对方接听电话,此时正在通话(维持并管理会话),通话结束后销毁会话,那么这也是一次会话的生命周期
  • 传输层:该层建立端到端的连接,它提供了数据传输服务,在传输层通信会涉及到端口号,本层常见的协议为TCP、UDP,LVS就是在这一层,也就是四层负载均衡
    • 举例:我和LiLei通信过程中会借助快递公司,快递公司会分配快递员取件和寄件,那么这个快递员则充当传输层的作用
  • 网络层:网络通信的时候必须要有本机IP和对方的IP,请求端和响应端都会有自己的IP的,IP就相当于你家地址门牌号,在网络上云服务器的公网IP,普通计算机也有,只不过是动态IP,运营商每天会分配不同的IP给你的计算机。所以网络层也能称之为IP层,IP是互联网的通信基础,能提供IP分配的设备则为路由器或交换机
    • 举例:快递员由物流公司分配和管理,那么物流公司就是网络层
  • 数据链路层:这一层会提供计算机MAC地址,通信的时候会携带,为了确保请求投递正确,所以他会验证检测MAC地址,以确保请求准确性
    • 举例:快递员在投递派送的时候,他(或客服)会预先提前打电话给你,确认你家地址对不对、有没有人、货到付款有没有准备好钱 候快递员(或客服)就充当了 的职责数据链路层
  • 物理层:负责数据的发送,这是最底层的网卡负责的。网卡把数据转换成高/低电平,然后通过网络发送出去
    • 举例:快递或者新建需要通过运输才能送达,物理层就是运输的方式或者工具
  • 四层网络模型中每一层的作用
  • 应用层:这一层就是上层应用使用的协议,如http,ftp,smtp协议等
  • 传输层:从物理层到传输层这三层都是负责建立网络连接,发送数据的,他们并不关心应用层使用什么协议,传输层使用的协议为:TCP和UDP协议
  • 网络层:这一层负责选择路由路径,从相同的出发地到达相同的目的地会有很多种路径,网络层就负责选择一条数据通行的路径
  • 物理层:负责数据的发送,这是最底层的网卡负责的。网卡把数据转换成高/低电平,然后通过网络发送出去
  • 数据传输的过程

image-20200809230748715

HTTP协议

  • HTTP协议思维导图

  • GET方法和POST方法的区别

HTTP是一个应用层的协议,它的底层使用的是TCP/IP协议,所以GET和POST的底层是相同的,两者能做的事也一模一样

  • GET请求也可以有Request Body,POST请求也可以在URL中加入请求的参数
  • GET和POST参数长度不是HTTP协议所规定的,时服务器和浏览器自行的规定
浏览器和服务器为了节省内存,所以会限制参数的大小,在Nginx中可以通过large_client_header_buffers来限制请求头的长度
  • HTTP报文头的种类
  • 通用首部字段
    • Date:报文的日期相关
    • Cache-Control:控制缓存
    • Connection:管理连接
  • 请求首部字段,
    • Host:请求资源所在的服务器
    • If-Match:标志位,用于判断资源是否符合要求
    • If-Modified-Since:当资源比该字段内容更新时,发送资源
    • If-Unmodified-Since:和If-Modified-Since相反
    • Referer:当前请求来源的地址
    • User-Agent:客户端类型,比如Chrome浏览器,IE浏览器,CRUL程序等
    • Cookie:发送请求时给服务端的一些信息
  • 响应首部字段
    • Accept-Ranges:服务器自身支持的请求范围
    • Location:配合301和302状态码使用,表示资源位置发生了改变
    • Etag:资源标识
  • 实体首部字段
    • Content-Length:表示实体的大小,单位是字节
    • Content-Range:用于范围请求
    • Content-Type:实体类型
    • Last-Modified:最后修改时间
  • 报文头重要字段说明
  • Connection:通用首部字段,取值:keep-alive,close
HTTP 是基于 `Resquest-Response` 形式的,客户端每次发送完一个请求之后,等待服务器响应,最后和服务器断开连接,本次请求结束。

由于 HTTP 是基于 `TCP/IP` 传输的,当 `HTTP` 发送请求的时候要和服务器经过**三次握手**建立连接,断开的时候要经过**四次挥手**进行断开。如果有大量的 HTTP 请求的话,每次都要进过`三次握手`和`四次挥手`,就会非常的耗时,为了解决这个问题,HTTP引入了 `keep-alive` 机制。所谓的`keep-alive`就是客户端和服务器三次握手之后,可以发送多个请求,最后再进行四次挥手

当我们设置 `Connection: keep-alive` 的时候,就会保持该链接,直到某个请求的 `Connection: close` 为止。这样可以在很大程度上提高 HTTP 的性能
  • Accept-Ranges:响应首部字段,取值:none,bytes
    • none:不支持任何范围请求单位,由于其等同于没有返回此头部,因此很少使用,不过一些浏览器,如IE9会依据此头部取禁用或者移除下载管理器的暂停按钮
    • bytes:范围请求的单位是bytes(字节)
  • Content-Type:实体类型,常用类型如下
    • application/x-www-form-urlencoded:默认的GET和POST编码方式,所有的数据都会变成键值对的形式
    • multipart/form-data:如果上传资源的时候,必须使用这种格式

Http状态码

  • 状态码的分类及含义

常见问题解决

pid 打开失败

[root@study nginx]# ./sbin/nginx -s reload
nginx: [error] open() "/var/run/nginx/nginx.pid" failed (2: No such file or directory)

解决方案

  • 先去看看这个路径的文件是否存在
  • 如果是/var/run/nginx/不存在,则创建这个目录
mkdir /var/run/nginx/
  • 目录存在之后,再次尝试重启,报错 pid 无效,此处是需要手动选择配置文件执行命令
./sbin/nginx -s reload
nginx: [error] invalid PID number "" in "/var/run/nginx/nginx.pid"
  • 手动选择配置文件执行命令
./sbin/nginx -c /usr/local/nginx/conf/nginx.conf

# 再次重启就可以了
./sbin/nginx -s reload

pid 文件只有在运行时才会产生。nginx 在运行期间,这个 pid 文件丢失的话,就会出现上面的情况,执行信号指令就会报错,这个时候就只能先 kill 掉 master 进程,再手动指定下配置文件运行后,就可以了。