高性能Web服务器-Nginx的常用模块

文章目录

Nginx安装

脚本安装

bash 复制代码
#!/bin/bash
#
#********************************************************************
#Author:			wangxiaochun
#Date: 				2020-12-01
#FileName:			install_nginx.sh
#URL: 				http://www.wangxiaochun.com
#Description:		The test script
#Copyright (C): 	2021 All rights reserved
#********************************************************************
SRC_DIR=/usr/local/src
NGINX_URL=http://nginx.org/download/
NGINX_FILE=nginx-1.20.2
#NGINX_FILE=nginx-1.18.0
TAR=.tar.gz
NGINX_INSTALL_DIR=/apps/nginx
CPUS=`lscpu |awk '/^CPU\(s\)/{print $2}'`
. /etc/os-release

color () {
    RES_COL=60
    MOVE_TO_COL="echo -en \\033[${RES_COL}G"
    SETCOLOR_SUCCESS="echo -en \\033[1;32m"
    SETCOLOR_FAILURE="echo -en \\033[1;31m"
    SETCOLOR_WARNING="echo -en \\033[1;33m"
    SETCOLOR_NORMAL="echo -en \E[0m"
    echo -n "$1" && $MOVE_TO_COL
    echo -n "["
    if [ $2 = "success" -o $2 = "0" ] ;then
        ${SETCOLOR_SUCCESS}
        echo -n $"  OK  "    
    elif [ $2 = "failure" -o $2 = "1"  ] ;then 
        ${SETCOLOR_FAILURE}
        echo -n $"FAILED"
    else
        ${SETCOLOR_WARNING}
        echo -n $"WARNING"
    fi
    ${SETCOLOR_NORMAL}
    echo -n "]"
    echo 
}

os_type () {
   awk -F'[ "]' '/^NAME/{print $2}' /etc/os-release
}

os_version () {
   awk -F'"' '/^VERSION_ID/{print $2}' /etc/os-release
}

check () {
    [ -e ${NGINX_INSTALL_DIR} ] && { color "nginx 已安装,请卸载后再安装" 1; exit; }
    cd  ${SRC_DIR}
    if [  -e ${NGINX_FILE}${TAR} ];then
        color "相关文件已准备好" 0
    else
        color '开始下载 nginx 源码包' 0
        wget ${NGINX_URL}${NGINX_FILE}${TAR} 
        [ $? -ne 0 ] && { color "下载 ${NGINX_FILE}${TAR}文件失败" 1; exit; } 
    fi
} 

install () {
    color "开始安装 nginx" 0
    if id nginx  &> /dev/null;then
        color "nginx 用户已存在" 1 
    else
        useradd -s /sbin/nologin -r  nginx
        color "创建 nginx 用户" 0 
    fi
    color "开始安装 nginx 依赖包" 0
    if [ $ID == "centos" ] ;then
	    if [[ $VERSION_ID =~ ^7 ]];then
            yum -y -q  install make gcc pcre-devel openssl-devel zlib-devel perl-ExtUtils-Embed
		elif [[ $VERSION_ID =~ ^8 ]];then
            yum -y -q install make gcc-c++ libtool pcre pcre-devel zlib zlib-devel openssl openssl-devel perl-ExtUtils-Embed 
		else 
            color '不支持此系统!'  1
            exit
        fi
    elif [ $ID == "rocky"  ];then
	    yum -y -q install make gcc-c++ libtool pcre pcre-devel zlib zlib-devel openssl openssl-devel perl-ExtUtils-Embed 
	else
        apt update &> /dev/null
        apt -y install make gcc libpcre3 libpcre3-dev openssl libssl-dev zlib1g-dev &> /dev/null
    fi
    cd $SRC_DIR
    tar xf ${NGINX_FILE}${TAR}
    NGINX_DIR=`echo ${NGINX_FILE}${TAR}| sed -nr 's/^(.*[0-9]).*/\1/p'`
    cd ${NGINX_DIR}
    ./configure --prefix=${NGINX_INSTALL_DIR} --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module 
    make -j $CPUS && make install 
    [ $? -eq 0 ] && color "nginx 编译安装成功" 0 ||  { color "nginx 编译安装失败,退出!" 1 ;exit; }
    echo "PATH=${NGINX_INSTALL_DIR}/sbin:${PATH}" > /etc/profile.d/nginx.sh
    cat > /lib/systemd/system/nginx.service <<EOF
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=${NGINX_INSTALL_DIR}/logs/nginx.pid
ExecStartPre=/bin/rm -f ${NGINX_INSTALL_DIR}/logs/nginx.pid
ExecStartPre=${NGINX_INSTALL_DIR}/sbin/nginx -t
ExecStart=${NGINX_INSTALL_DIR}/sbin/nginx
ExecReload=/bin/kill -s HUP \$MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true
LimitNOFILE=100000

[Install]
WantedBy=multi-user.target
EOF
    systemctl daemon-reload
    systemctl enable --now nginx &> /dev/null 
    systemctl is-active nginx &> /dev/null ||  { color "nginx 启动失败,退出!" 1 ; exit; }
    color "nginx 安装完成" 0
}

check
install

Nginx平滑升级与回滚

平滑升级流程

bash 复制代码
1.将旧Nginx二进制文件换成新Nginx程序文件(注意备份);
2.向master进程发送USR2信号;
3.master进程修改pid文件名加上后缀.oldbin,成为nginx.pid.oldbin
4.master进程用新master文件启动新master进程成为旧master的子进程,
  此时系统中将有新旧两个Nginx主进程共同提供Web服务,
  当前新的请求仍然由旧Nginx的woker进程进行处理,将新生成的master进程的PID存放至新生成的pid文件nginx.pid
5.向旧的Nginx服务进程发送WINCH信号,使旧的Nginx worker进程平滑停止;
6.向旧master进程发送QUIT信号,关闭老master,并删除Nginx.pid.oldbin文件;
7.如果升级有问题,可以回滚:向老master发送HUP,向新master发送QUIT;

相关信号

当前nginx版本是1.20.2,我们将它升级到1.22.1

第1步,下载新版本

下载新版本nginx-1.22.1.tar.gz包并解压

bash 复制代码
wget https://nginx.org/download/nginx-1.22.1.tar.gz
bash 复制代码
tar xvf nginx-1.22.1.tar.gz


第2步,编译

查看Nginx当前使用的版本及编译选项

开始编译

bash 复制代码
./configure --prefix=/apps/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module

第3步,执行make

执行make无需make install

make完之后又在objs目录下生成了nginx文件,这个文件存放就是刚编译完的二进制程序,可以查看编译的这个nginx版本

第4步,对比新旧版本

对比下新旧版本的两个文件的大小

第5步,备份旧nginx二进制文件

第6步,模拟用户正在访问nginx

bash 复制代码
dd if=/dev/zero of=/apps/nginx/html/f1.img bs=1M count=100

模拟用户下载f1.img资源

第7步,替换旧的nginx二进制文件

检测语法

第8步,发送USR2信号

此时的进程变化如下

第9步,发送winch信号,

使新的master进程工作

以下截图可以看到旧worker进程正在关闭(shutting down)

此时新版本已经改过来了

回滚老版本

如果此时想回滚老版本,直接杀死掉当前的nginx.pid,在将我们之前备份的旧的nginx二进制文件拷贝回去即可。

第10步,平滑关掉旧版本的master进程

我停止了模拟用户的访问,此时旧master进程才彻底退出

Nginx核心配置

Nginx官方指令文档https://nginx.org/en/docs/dirindex.html

Nginx的配置文件的组成部分

bash 复制代码
1.主配置文件 nginx.conf
2.子配置文件:include conf.d/*.conf

主配置文件结构

nginx配置文件格式说明

bash 复制代码
1.配置文件由指令与指令块构成;
2.每条指令以;号结尾,指令与值之间以空格符号分隔;
3.指令块以{}大括号将多条指令组织在一起,且可以嵌套指令块;
4.include语句允许组合多个配置文件以提升可维护性;
5.部分指令参数支持正则表达式。

可以查看nginx.conf默认的文件结构配置有哪些

bash 复制代码
grep -E '{|}' /apps/nginx/conf/nginx.conf
bash 复制代码
#事件驱动相关配置
event{
    ...
}

#http/https 协议相关配置
http{
.....
}

#mail协议相关配置项
mail{
 ......
}

#stream 服务器相关配置
stream{
      ......
}

全局配置

bash 复制代码
Main全局配置段常见的配置指令分类
  1.正常运行必备的配置
  2.优化性能相关的配置
  3.用于调试及定位问题相关的配置
  4.时间驱动相关配置
bash 复制代码
user nginx nginx;  #启动Nginx工作进程的用户和组
worker_processes  [number |auto]; # 启动Nginx工作进程的数量,一般设为和CPU核数相同
worker_cpu_affinity 0001 0010 0100 1000; # 将Nginx工作进程绑定到指定的CPU核心,减少了nginx工作进程在不同cpu核心上来回跳转,减少了cpu对进程资源分配与回收以及内存管理,提高nginx性能。
worker_priority 0; # 工作进程优先级,-20~20(19)
worker_rlimit_nofile 65535; # 所有nginx进程能打开的文件数量上限,包括nginx的所有连接(例如与代理服务器的连接等)
daemon off; #前台运行Nginx服务用于测试,docker等环境
master_process off|on; 是否开启nginx的master-worker工作模式,

events{
   worker_connections 65535; # 设置单个工作进程的最大并发连接数
   use epoll; #使用epoll事件驱动,nginx支持总舵事件驱动,如:select、poll、 epoll,智能设置在events模块中。
   accept_mutex on; # on为同一时刻一个请求轮流由worker进程处理,而防止唤醒所有的worker;默认为off,新请求会唤醒所有worker进程,此过程也称为'惊群',因此nginx刚安装完以后要进行适当的优化,建议设置为on
}

配置自动匹配机器的CPU核数,我的CPU是4核

bash 复制代码
worker_cpu_affinity  
worker_cpu_affinity 0001 0010 0100 1000 | auto;
#将nginx工作进程绑定到指定的CPU核心,默认Nginx是不进行绑定的,绑定不是意味着当前nginx进程独占一个核心CPU,
#但是可以保证此进程不会运行在其他核心上,这就极大减少了nginx的工作进程在不同的cpu核心上的来回跳转,
#减少了cpu对进程的资源分配与回收以及内存管理等,因此可以有效的提升nginx服务器的性能。

指定响应报文server首部

bash 复制代码
charset charset | off;  # 是否在响应报文中的Content-Type显示指定的字符集,默认off不显示
# charset uft-8

server_tokens on | off | build | string; # 是否在相应报文的server首部显示nginx版本

修改server字段

bash 复制代码
如果想自定义响应报文的nginx版本信息,需要修改源码文件,重新编译
如果server_token on, 修改src/core/nginx.h 修改如下
#define NGINX_VERSION  "1.110.1"
#define NGINX_VER      "Leiginx" NGINX_VERSION

实现多虚拟主机

马哥教育有两个域名网站,一个是"马哥官网",一个是"运维派",但是这两个网站用的是同一个IP地址。这就是通过Nginx实现的多虚拟主机的案例。该功能通过"ngx_http_core_module"模块实现

多虚拟主机实验步骤

1.实现目标:

创建两个网站,一个PC站点,一个Mobile站点;

2.创建子配置文件

创建conf.d的目录分别存放pc和mobile的配置文件

pc.conf

bash 复制代码
server{
  listen 80;
  charset utf-8;
  server_name www.leiedu.org;
  location / {
    root /data/nginx/html/pc;
  }
}

mobile.conf

bash 复制代码
server{
  listen 80;
  charset utf-8;
  server_name mobile.leiedu.org;
  location / {
    root /data/nginx/html/mobile;
  }
}


3.创建两个站点网页

pc站点和mobile站点的网页内容

通过浏览器访问这两个站点。

可以看出页面有中文乱码,所以需要配置"charset utf-8"

如此就实现了多虚拟主机的实验,

注意DNS解析我配置的是hosts文件。

思考,两个站点的访问时是怎么解析的,

bash 复制代码
两个站点的解析主要是我们的http请求携带了host的主机头,nginx根据不同的host主机头去解析不同的配置文件。

当curl 主机的ip地址时,则访问的是nginx的默认页面

如果我们将include包含的子配置文件从nginx的主配置文件nginx.conf末尾处提前到sever模块前,则nginx会根据子配置文件的字母顺序去访问相应的站点网页。


curl命令可以携带host主机到去访问具体的域名

bash 复制代码
-H, --header <header/@file> Pass custom header(s) to server

root和alias

root:

指定web的家目录,在定义location的时候,文件的绝对路径等于root+location

bash 复制代码
server{
  listen 80;
  charset utf-8;
  server_name www.leiedu.org;
  location / {
    root /data/nginx/html/pc;
  }

  location /about {
    root /opt/html; # 必须在html目录中创建一个名为about的目录才可以访问,否则报错。
  }
}


由此可见,root的配置访问规则是当我们访问"/about"时,就去找/apt/html的about目录下的文件。"/opt/html"是"/about"的根。当"/opt/html"这个根下没有about这个子目录时就会报错。

alias:

定义路径别名,会把访问的路径重新定义到其指定的路径,文档映射的另一中机制,仅能用于location上限文中。

bash 复制代码
server{
  listen 80;
  charset utf-8;
  server_name www.leiedu.org;
  location / {
    root /data/nginx/html/pc;
  }

  location /about {
    alias /opt/pc/aboutdir;
  }
}

浏览器访问

由此可见 alias连接过去的目录"/opt/pc/aboutdir"下并没有about这个子目录,也就是说alias的访问规则是当我们访问/about时就去找"/opt/pc/aboutdir"这个目录下的文件,"/about"就是"/opt/pc/aboutdir"的别名。

location配置

参加我的另一篇博客 location匹配规则

Nginx账户认证

bash 复制代码
Syntax:	auth_basic string | off;
Default:	
auth_basic off;
Context:	http, server, location, limit_except

由"ngx_http_auth_basic_module"模块提供功能

官方帮助文档:https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html#auth_basic

Nginx自定义错误页面

bash 复制代码
Syntax:	error_page code ... [=[response]] uri;
Default:	---
Context:	http, server, location, if in location

example

bash 复制代码
error_page 404             /404.html;
error_page 500 502 503 504 /50x.html;

自定义错误页,同时也可以用指定的响应状态码进行相应,可配置在http, server, location, if in location等模块中。

官方帮助文档:https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page

自定义错误日志

bash 复制代码
Syntax:	    error_log file [level];
Default:    error_log logs/error.log error;
Context:    main, http, mail, stream, server, location

#The second parameter determines the level of logging, and can be one of the following: debug, info, notice, warn, error, crit, alert, or emerg.

官方帮助文档:https://nginx.org/en/docs/ngx_core_module.html#error_log

检测文件是否存在

bash 复制代码
Syntax:	try_files file ... uri;
try_files file ... =code;
Default:	---
Context:	server, location

For example

bash 复制代码
location /images/ {
    try_files $uri /images/default.gif;
}

location = /images/default.gif {
    expires 30s;
}

try_files会按照顺序检查文件是否存在,返回第一个找到的文件和文件夹(结尾加斜线表示为文件夹),如果所有文件和文件夹都找不到,会进行一个内部重定向到最后一个参数,只有最后一个参数可以引起一个内部重定向,之前的参数值设置内部URI的指向,最后一个参数是回退URI且必须存在,否则会出现内部500错误。

官方帮助文档:https://nginx.org/en/docs/http/ngx_http_core_module.html#try_files

Nginx高级配置

Nginx状态页

由nginx的"ngx_http_stub_status_module"模块提供

官方帮助文档:https://nginx.org/en/docs/http/ngx_http_stub_status_module.html#stub_status

bash 复制代码
Syntax:	stub_status;
Default:	---
Context:	server, location
bash 复制代码
location = /basic_status {
    stub_status;
}
bash 复制代码
Active connections:#当前处于活动状态的客户端连接数,包括连接等待空闲连接数=reading+writing+waiting

accepts:#统计总值,Nginx自启动后已经接受的客户端请求连接的总数。

hand1ed:#统计总值,Nginx自启动后已经处理完成的客户端请求连接总数,通常等于accepts,除非有因worker_connections限制等被拒绝的连接

requests:#统计总值,Nginx自启动后客户端发来的总的请求数。

Reading:#当前状态,正在读取客户端请求报文首部的连接的连接数,数值越大,说明排队现象严重,性能不足

writing:#当前状态,正在向客户端发送响应报文过程中的连接数,数值越大,说明访问量很大

waiting:#当前状态,正在等待客户端发出请求的空闲连接数,开启 keep-alive的情况下,这个值等于active - (reading+writing)

范例:分析网站当前访问量

bash 复制代码
[root@centos7 ~]#cur1 http: / /wang:123456@ww.wangxiaochun. com/nginx_status 2>/dev/nu11 |awk '/Reading/{print $2,$4,$6}'
0 1 15

Nginx第三方模块

第三方模块是对Nginx的功能扩展。第三方模块需要在编译安装Nginx的时候使用参数--add-module=PATH指定路径添加,有的模块是由公司的开发人员针对业务需求定制开发的,有的模块是开源爱好者开发好之后上传到github进行开源模块,nginx的第三方模块需要从源码重新编译进行支持。

实现流量监控
https://github.com/vozlt/nginx-module-vts

实现信息显示
https://github.com/openresty/echo-nginx-module

Nginx变量使用

nginx的变量可以在配置文件中引用,作为功能判断或者日志等场景使用

变量可以分为内置变量和自定义变量

内置变量是由nginx模块自带,通过变量可以获取到众多的与客户端访问相关的值。

官方帮助文档:https://nginx.org/en/docs/varindex.html

bash 复制代码
$remote_addr; #存放了客户端的地址,注意是客户端的公网IP

$proxy_add_x_forwarded_for 
# 此变量表示将客户端IP追加请求报文中x-Forwarded-For首部字段,多个ip之间用逗号隔开,如果请求中灭有X-Forwarded-For,就使用$remote_addr

$args; #存放了URL中的所有参数
$is_args; #如果有参数为?否则为空
 
$document_root; #保存了针对当前资源的请求的系统根目录,例如:/apps/nginx/html.
$document_uri; #保存了当前不包含参数的URI

$host; #存放了请求的host名称

limit_rate 10240;
echo $1imit_rate;
#如果nginx服务器使用limit_rate配置了显示网络速率,则会显示,如果没有设置,则显示0

$remote_port; #客户端请求Nginx服务器时随机打开的端口,这是每个客户端自己的端口
$remote_user; #已经经过Auth Basic Module验证的用户名

$request_body_file; #做反向代理时发给后端服务器的本地资源的名称
$request_method; #请求资源的方式,GET/PUT/DELETE等
$request_filename; #当前请求的资源文件的磁盘路径,由root或a7ias指令与URI请求生成的文件绝对路径,如: /apps/nginx/htm1/ main/index.html
$request_uri;
#包含请求参数的原始URI,不包含主机名,相当于:$document_uri?$args,例如:/main/index.do?id=20190221&partner=search

$scheme; #请求的协议,例如:http,https,ftp等

$server_protoco1; #保存了客户端请求资源使用的协议的版本,例如:HTTP/1.0,HTTP/1.1,HTTP/2.0等
$server_addr; #保存了服务器的IP地址
$server_name; #请求的服务器的主机名
$server_port; #请求的服务器的端口号

$http_user_agent; #客户端浏览器的详细信息
$http_cookie; #客户端的所有cookie信息

$cookie_<name> #name为任意请求报文首部字部cookie的key名
$http_<name>
#name为任意请求报文首部字段,表示记录请求报文的首部字段,ame的对应的首部字段名需要为小写,如果有横线需要替换为下划线
arbitrary request header field; the last part of a variable name is the fieldname converted to lower case with dashes replaced by underscores #用下划线代替横线
#示例:
echo $http_user_agent;echo $http_host;

$sent_http_<name>
#name为响应报文的首部字段,name的对应的首部字段名需要为小写,如果有横线需要替换为下划线,此变量有问题
echo $sent_http_server;

$arg_<name>#此变量存放了URL中的指定参数,name为请求ur1中指定的参数echo $arg_id;

Nginx自定义访问日志

访问日志是记录客户端即用户的具体请求内容的信息,而在全局配置模块中的error_log是记录nginx服务器运行时的日志保存路径和记录日志的level,因此两者是不同的,而且Nginx的错误日志一般只有一个,但是访问日志可以在不同server中定义多个,定义一个日志需要使用access_log指定日志的保存路径,使用log_format指定日志的格式,格式中定义要保存的具体日志内容。

访问日志由ngx_http_log_module模块实现

官方帮助文档:https://nginx.org/en/docs/http/ngx_http_log_module.html

bash 复制代码
Syntax:	access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
        access_log off;
Default: access_log logs/access.log combined;
Context: http, server, location, if in location, limit_except

自定义默认格式日志

Nginx的默认访问日志记录内容相对比较单一,默认的格式也不方便后期做日志统计分析,生产环境中通常将nginx日志转换为json日志,然后配合使用ELK做日志收集,统计和分析。

bash 复制代码
log_format access_json '{"@timestamp":"$time_iso8601", '
   '"host":"$server_addr",'
   '"clientip":"$remote_addr",'
   '"size":"$body_bytes_sent",'
   '"responsetime":"$request_time",'  #总的处理时间
   '"upstreamtime":"$upstream_response_time",'
   '"upstreamhost":"$upstream_addr",' #后端应用服务器处理时间
   '"http_host":"$host",'
   '"uri":"$uri",'
   '"xff":"$http_x_forwarded_for",'
   '"referer":"$http_referer",'
   '"tcp_xff":"$proxy_protoco1_addr",'
   '"http_user_agent":"$http_user_agent",'
   '"status":"$status"}';
access_log/apps/nginx/logs/access_json.logaccess_json;

Nginx压缩功能

Nginx支持对指定类型的文件进行压缩然后再传输给客户端,而且还可以设置压缩比例,压缩后的文件大小将比源文件显著变小,这样有助于降低出口带宽的利用率,降低企业的IT支出,不过会占用相应的CPU资源。

Nginx对文件的压缩功能是依赖于模块 ngx_http_gzip_module,默认是内置模块

官方帮助文档:https://nginx.org/en/docs/http/ngx_http_gzip_module.html

bash 复制代码
Syntax:	gzip on | off;
Default: gzip off;
Context: http, server, location, if in location
bash 复制代码
gzip on |off;  #启用或禁用gzip压缩,默认关闭

gzip_comp_leve1 level;  #压缩比由低到高从1到9,默认为1
gzip_disab1e "MSIE [1-6]\."; #禁用IE6 gzip功能

gzip_min_length 1k ; #gzip压缩的最小文件,小于设置值的文件将不会压缩
gzip_http_version 1.0 |1.1; #启用压缩功能时,协议的最小版本,默认HTTP/1.1

gzip_buffers number size; #指定Nginx服务需要向服务器申请的缓存空间的个数和大小,平台不同,默认:324k或者16 8k;
gzip_types mime-type ...;#指明仅对哪些类型的资源执行压缩操作;默认为gzip_types text/htm1,不用显示指定,否则出错

gzip_vary on | off;#如果启用压缩,是否在响应报文首部插入"vary: Accept-Encoding",一般建议打开

gzip_static on | off;
#预压缩,即直接从磁盘找到对应文件的gz后缀的式的压缩文件返回给用户,无需消耗服务器CPU#注意:来自于ngx_http_gzip_static_modu7e模块

Https功能

Web网站的登录页面通常都会使用https加密传输的,加密数据以保障数据的安全,HTTPS能够加密信息,以免敏感信息被第三方获取,所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用HTTPS协议,HTTPS其实是有两部分组成:HTTP + SSL/TLS,也就是在HTTP上又加了一层处理加密信息的模块。服务端和客户端的信息传输都会通过TLS进行加密,所以传输的数据都是加密后的数据。

Https实现过程

bash 复制代码
https实现过程如下:
1.客户端发起HTTPS请求:
  客户端访问某个web端的https地址,一般都是443端口

2.服务端的配置:
  采用https协议的服务器必须要有一套证书,可以通过一些组织申请,也可以自己制作,目前国内很多网站都自己做的,
  当你访问一个网站的时候提示证书不可信任就表示证书是自己做的,
  证书就是一个公钥和私钥匙,就像一把锁和钥匙,正常情况下只有你的钥匙可以打开你的锁,
  你可以把这个送给别人让他锁住一个箱子,里面放满了钱或秘密,别人不知道里面放了什么而且别人也打不开,
  只有你的钥匙是可以打开的。

3.传送证书:
  服务端给客户端传递证书,其实就是公钥,里面包含了很多信息,例如证书得到的颁发机构、过期时间等等。

4.客户端解析证书:
  这部分工作是有客户端完成的,首先会验证公钥的有效性,比如颁发机构、过期时间等等,
  如果发现异常则会弹出一个警告框提示证书可能存在问题,
  如果证书没有问题就生成一个随机值,然后用证书对该随机值进行加密,就像2步骤所说把随机值锁起来,不让别人看到。

5.传送4步骤的加密数据:
  就是将用证书加密后的随机值传递给服务器,目的就是为了让服务器得到这个随机值,
  以后客户端和服务端的通信就可以通过这个随机值进行加密解密了。
  
6.服务端解密信息:
  服务端用私钥解密5步骤加密后的随机值之后,得到了客户端传过来的随机值(私钥),然后把内容通过该值进行对称加密,
  对称加密就是将信息和私钥通过算法混合在一起,这样除非你知道私钥,不然是无法获取其内部的内容,
  而正好客户端和服务端都知道这个私钥,所以只要机密算法够复杂就可以保证数据的安全性。

7.传输加密后的信息:
  服务端将用私钥加密后的数据传递给客户端,在客户端可以被还原出原数据内容。

8.客户端解密信息:
  客户端用之前生成的私钥获解密服务端传递过来的数据,由于数据一直是加密的,因此即使第三方获取到数据也无法知道其详细内容。

配置参数如下

nginx的https功能基于模块ngx_http_ssl_module实现,因此如果是编译安装的nginx要使用参数ngx_http_ssl_module开启ssl功能,但是作为nginx的核心功能,yum安装的nginx默认就是开启的,编译安装的nginx需要指定编译参数--with-http_ssl_module开启

官方帮助文档: https://nginx.org/en/docs/http/ngx_http_ssl_module.html

bash 复制代码
ss1 on | off;
#为指定的虚拟主机配置是否启用ss1功能,此功能在1.15.0废弃,使用1isten [ssl]替代7isten 443 ssl;

ss1_certificate /path/to/file;
#指向包含当前虚拟主机和CA的两个证书信息的文件,一般是crt文件

ss1_certificate_key /path/to/file;
#当前虚拟主机使用的私钥文件,一般是key文件

ss1_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2];
#支持ss1协议版本,早期为ss1现在是TLs,默认为后三个

ss1_session_cache off | none | [builtin[:size]] [shared:name:size];
#配置ss1缓存
  off: #关闭缓存
  none: #通知客户端支持ss1 session cache,但实际不支持
  builtin[:size]: #使用opensSL内建缓存,为每worker进程私有
  [shared : name:size]: #在各worker之间使用一个共享的缓存,需要定义一个缓存名称和缓存空间大小,一兆可以存储4000个会话信息,多个虚拟主机可以使用相同的缓存名称
  
ss1_session_timeout time;
#客户端连接可以复用ss1 session cache中缓存的有效时长,默认5m

https配置

bash 复制代码
server {
    1isten 80;
    listen 443 ss1;
    ssl_certificate /apps/nginx/certs/ww.magedu.org. pem;
    ssl_certificate_key /apps/nginx/certs/www.magedu.org.key;
    ssl_session_cache shared:sslcache:20m;
    ssl_session_timeout 10m;
    root /data/nginx/htm1 ;
}

Rewrite相关功能

Rewrite应用背景

网站的老域名不使用了又不能马上就下线,于是当用户访问老域名时就将其跳转到新域名上。

bash 复制代码
Rewrite应用背景
  1.将用户输入的URL地址进行重写,也就是Rewrite将我们输入的地址修改成了别的地址;
  2.Rewrite重写之后,我们可能会看到一个全新的地址路径;
  3.Rewrite重写过程支持正则表达式,这个正则表达式和之前的正则表达式有一些区别,它依靠PCRE;

Nginx服务器利用ngx_http_rewrite_module模块解析和处理rewrite请求,此功能依靠PCRE(perl compatible regular expression),因此编译之前要安装PCRE库,rewrite是nginx服务器的重要功能之一,用于实现URL重写,URL重写是非常有用的功能,比如它可以在我们改变网站结构之后,不需要客户端修改原来的书签,也无需其他网站修改我们的链接就可以设置为访问,另外还可以在一定程度上提高网站的安全性。

官方帮助文档:https://nginx.org/en/docs/http/ngx_http_rewrite_module.html

rewrite模块指令

return

return用于完成对请求的处理,并直接向客户端返回响应状态码,比如:可以指定重定向URL(对于特殊重定向状态码,301/302等)或者是指定提示文本内容(对于特殊状态码403/500等),处于此指令后的所有配置都将不被执行,return可以在server、if 和location块进行配置

bash 复制代码
return code ;#返回给客户端指定的HTTP状态码
return code [text];#返回给客户端的状态码及响应报文的实体内容,可以调用变量,其中text如果有空格,需要用单或双引号
return code URL;#返回给客户端的URL地址
bash 复制代码
301  永久跳转 (浏览器会有缓存,下次再访问时可从磁盘直接读取)
302  临时跳转 (临时的,没有缓存,下次可能不会跳转)


301配置

bash 复制代码
server{
  listen 80;
  charset utf-8;
  server_name www.leiedu.org;
  location / {
    root /data/nginx/html/pc;
  }

  location /main {
     default_type text/html;
     return 301 /about;
  }

  #基于rewrite的配置实现return 301的效果,使用permanent
  location /exam {
     default_type text/html;
     rewrite .* /about permanent; # 配置301这个数字会报语法错误
  }
}

浏览器则会帮你从www.leiedu.org/main跳转到www.leiedu.org/about


302配置

bash 复制代码
server{
  listen 80;
  charset utf-8;
  server_name www.leiedu.org;
  location / {
    root /data/nginx/html/pc;
  }

  location /main {
     default_type text/html;
     return 302 /about;
  }
  
  # 基于rewrite的配置实现return 302的效果,使用redirect
  location /test {
     default_type text/html;
     rewrite .* /about redirect;  # 这里写302数字的话会报语法错误;
  }
}


我们看下淘宝和京东两个大厂的重定向策略,

rewrite案例:自动跳转https

案例: 基于通信安全考虑公司网站要求全站https,因此要求将在不影响用户请求的情况下将http请求全部自动跳转至https,另外也可以实现部分location 跳转

bash 复制代码
server {
  listen 443 ssl;
  listen 80;
  ssl_certificate /apps/nginx/certs/www.magedu.org.crt;
  ssl_certificate_key /apps/nginx /certs/www.magedu.org.key;
  ssl_session_cache shared:ss1cache:20m;
  ssl_session_timeout 10m;
  server_name www.magedu.org;
  
  location / {  #针对全站跳转
    root /data/nginx/htm1/pc;
    index index.html ;
    if($scheme = http){  #如果没有加条件判断,会导致死循环
       rewrite / https://$host redirect;
     }
  }
   
  location /login {  #针对特定的URL进行跳转https
    if($scheme = http){  #如果没有加条件判断,会导致死循环
       rewrite / https://$host/login redirect;
    }
  }
}

动态匹配跳转

背景需求,现在sz这个子域名换成shenzhen了,但是sz下的子目录仍然保留,

www.leiedu.org/sz/时跳转到www.leiedu.org/shenzhen/

bash 复制代码
server{
  listen 80;
  charset utf-8;
  server_name www.leiedu.org;
  location / {
    root /data/nginx/html/pc;
    #return https://www.baidu.com/;
  }

  location /main {
     default_type text/html;
     return 302 /about;
  }

  location /sz {
     default_type text/html;
     rewrite ^/sz/(.*)$ /shenzhen/$1 redirect;
  }
}

break和last

bash 复制代码
1.break只会匹配一次,相当于java中的break一样匹配一次就跳出循环;
2.last会多次匹配符合条件的表达式;
bash 复制代码
server{
  listen 80;
  charset utf-8;
  server_name www.leiedu.org;
  root /data/nginx/html/pc;
  
  location /break {
     rewrite ^/break/(.*) /dose/$1 break;
  }

  location /last {
     rewrite ^/last/(.*) /dose/$1 last;
  }

  location /dose {
     default_type text/plain;
     return 999 "new last test";
  }
}





Nginx防盗链

什么是盗链

如果别人只链接了你网站的图片视频或某个单独的资源,而不是打开了你网站的整个页面,且挂着你网站的资源,还不让用户看到你的网站,而他还节省了这些图片视频的存储空间和网络带宽这就是盗链。

防盗链的目的是防止其他网站未经许可直接引用你网站的资源(视频,图片等)。防盗链基于客户端携带的referer实现,referer是记录打开一个页面之前是从哪个页面跳转过来的标记信息,

referer就是之前的那个网站域名,正常的referer信息有以下几种

bash 复制代码
none:#请求报文首部没有referer首部,比如用户直接在浏览器输入域名访问web网站,就没有referer信息。
b1ocked:#请求报文有referer首部,但无有效值,比如为空。
server_names:#referer首部中包含本主机名及即nginx监听的server_name。

arbitrary_string: #自定义指定字符串,但可使用*作通配符。
                  #示例:*.magedu.org www.magedu.*regular expression:
                  #被指定的正则表达式模式匹配到的字符串,要使用~开头,例如:~。* \.magedu\.com

Referer是什么

在HTTP协议中,Referer(有时也拼写为Referrer)是一个HTTP请求头字段,用于标识从哪里来的请求。具体来说,当一个网页上的链接被点击时,浏览器会在发送HTTP请求时包含Referer头,表明请求是从哪个URL发起的。

bash 复制代码
Referer作用
  1.来源追踪:网站可以使用Referer来追踪访问者是从哪个页面或网站跳转过来的。这对于分析网站流量来源、了解用户行为路径非常有用。
  2.安全性:某些网站通过检查Referer头来确保请求来自于预期的来源,从而防止跨站请求伪造(CSRF)等安全攻击。
  3.广告和联盟营销:广告商和联盟营销平台使用Referer头来追踪点击广告或联盟链接的来源,从而计算点击率和进行佣金结算。

相关博客参考 HTTP Referer 教程

实现盗链

我目前有两个网站www.leiedu.org和mobile.leiedu.org 在mobile.leiedu.org下有一张nginx的png图片,现在我在www.leiedu.org下创建一个referer.html页面,通过链接地址去显示mobile.leiedu.org下的图片


referer.html

html 复制代码
<html>
   <head>
    <meta http-equiv=Content-Type content="text/html;charset=utf-8">
    <title>盗链</title>
  </head>

  <body>
    <img src="http://mobile.leiedu.org/nginx.png" >
    <h1 style="color:red">嘿嘿,我在盗链哦,欢迎大家!</h1>
    <p><a href=http://www.magedu.org>给咱马哥教育打call</a>欢迎你</p>
  </body>

</html>

查看mobile.leiedu.org的日志可以看出有一个nginx.png的get请求来自于www.leiedu.org

查看referer日志需要打开nginx的主配置文件nginx.conf中access_log main的注释,其中$http_referer的变量就是日志中的referer信息

实现防盗链

定义有效的referer,如果是其他的referer就返回状态码403

官方帮助文档:https://nginx.org/en/docs/http/ngx_http_referer_module.html

bash 复制代码
server{
  listen 80;
  charset utf-8;
  server_name mobile.leiedu.org;
  access_log /apps/nginx/logs/m.leiedu.org-access.log main;

  valid_referers none blocked server_names   *.leiedu.com  ~\.google\. ~\.baidu\. ;
  if ($invalid_referer) {
    return 403 "Forbidden Access";
  }

  location / {
    root /data/nginx/html/mobile;
  }

}

谷歌、百度这样的搜索引擎可以是有效的referer,能从谷歌百度上搜索到你,这是你的流量,珍惜。

配置valid_referers的时候防止误杀,别连你自家的referer也被禁止了。

Nginx反向代理

正向代理代理的是客户端

反向代理代理的是服务器

实现Http反向代理

官方帮助文档:https://nginx.org/en/docs/http/ngx_http_proxy_module.html

我有三台服务器:192.168.10.149,192.168.10.150,192.168.10.151

在150和151的主页分别如下,151的主机域名为www.leiedu.org,现在将151的机器配置为代理服务器,让149通过151去访问150;

没配置151为代理之前,149访问www.leiedu.org时,返回结果如下

配置151为150的代理服务器

bash 复制代码
server{
  listen 80;
  charset utf-8;
  server_name www.leiedu.org;
  root /data/nginx/html/pc;
  access_log /apps/nginx/logs/pc.leiedu.org-access.log main;

  location / {
    proxy_pass http://192.168.10.150;
  }
}

配置完代理后,149使用curl命令访问www.leiedu.com时151的主页就出现了,此时浏览器也是的。

如此丝滑的Nginx代理!

此时查看150的日志可以看出,150根本不知道是使用curl和windos浏览器访问它的ip地址,它只知道代理服务器151是ip

而代理服务器151知道是谁通过它去访问150的。

Nginx和LVS的区别

LVS:客户端感知不到LVS的存在,LVS只是一个转发器。

Nginx:作为中间代理,分别与客户端和服务端建立三次握手连接。

我在150的机器上创建了一个1G的大图片,限速149去下载这个大图片,在下载的过程中使用ss命令查看151的TCP是怎么样的一个过程

bash 复制代码
dd if=/dev/zero of=/data/nginx/html/test.img bs=1M count=1000

如下图所示,在151上执行ss -nt可以看出,151先和149建立了TCP连接,之后151又和150建立了TCP连接。

502和504的区别

bash 复制代码
502: 服务器有响应,并明确告诉客户端你就是连不上;
504: 服务器没有反应,啥也不告诉客户端,客户端一直傻傻等待,等到最后超时了;

比如我修改了150的原来的监听端口80为8080,而此时代理服务器151并不知道修改了端口为8080了,此时访问150就报错502了,那么解决此问题就需要将151的proxy_pass也改为8080

如果后端服务器无法连接(比如:iptables -AINPUT -s nginx_ip -j REJECT或者systemctl stop httpd),关机(无后端服务器arp缓存),会出现下面502提示

151代理服务器上的缓存

再访问就不报错了

当我在150的机器上设置iptables拒绝代理服务器151的连接之后,149再通过代理访问时就会报错,nginx默认配置是60S的超时时间

bash 复制代码
iptables -A INPUT -s 192.168.10.151 -j DROP

默认在1分钟内后端服务器无法响应(比如:iptables -AINPUT -s nginx_ip -j DROP或关机(有后端服务器arp缓存)),会显示下面的504超时提示

动静分离请求

我的静态资源放在192.168.10.150上;动态资源放在192.168.10.149上,现在实现动静分离请求。

bash 复制代码
server{
  listen 80;
  charset utf-8;
  server_name www.leiedu.org;
  #root /data/nginx/html/pc;
  access_log /apps/nginx/logs/pc.leiedu.org-access.log main;

  location ~ \.(jpg|png|gif)$ {
    proxy_pass http://192.168.10.150:8080;
    proxy_connect_timeout 10s;
    #root /data/nginx/html/pc;
  }

  location /api {
    proxy_pass http://192.168.10.149;
  }
}


api服务器149上的配置如下

proxy_pass有无"/"的区别

bash 复制代码
location /url {
   proxy_pass http://hostname;         无"/",会追加url到后端服务器后面 http://hostname/url
   proxy_pass http://hostname/url2;    有"/",/url2会置换/url http://hostname/url2
}

示例:

bash 复制代码
location  /web {
   index index.htm1;
   proxy_pass http://10.0.0.18:8080;
   # 8080后面无uri,即无"/"符号,需要将1ocation后面url附加到proxy_pass指定的url后面,此行为类似于root
   # proxy_pass指定的uri不带斜线将访问的/web,等于访问后端服务器http://10.0.0.18:8080/web/index.htm1,即后端服务器配置的站点根目录要有web目录才可以被访问
   # http://nginx/web/index.htm1 ==> http://10.0.0.18:8080/web/index.htm1

   proxy_pass http://10.0.0.18:8080/; 
   # 8080后面有uri,即有"/"符号,相当于置换,即访问/web时实际返回proxy_pass后面uri内容.此行为类似于alias
   # proxy_pass指定的uri带斜线,等于访问后端服务器的http://10.0.0.18:8080/index.htm1内容返回给客户端
   # http://nginx/web/index.htm1 ==> http://10.0.0.18:8080
}


#如果location定义其uri时使用了正则表达式模式(包括~,~*,但不包括A~),则proxy_pass之后必须不使用uri;即不能有/ ,用户请求时传递的uri将直接附加至后端服务器之后
server {
   server_name HOSTNAME;
   1ocation ~|~* /uri/ {
      proxy_pass  http://host:port; #proxy_pass后面的url不能加/
   }
}

Http反向代理负载均衡

上面我们通过proxy_pass的配置将客户端的请求转发至单台后端服务器,做了简单的代理转发但是却无法转发至特定的一组服务器,而且不能对后端服务器提供相应的服务器状态监控。那么此时Nginx的ngx_http_upstream_module模块就出现了,该模块提供服务器的分组转发、权重分配、状态检测、调度算法等高级功能。

官方帮助文档:https://nginx.org/en/docs/http/ngx_http_upstream_module.html

bash 复制代码
upstream webserver {
  server 192.168.10.149;
  server 192.168.10.150;
}

server{
  listen 80;
  charset utf-8;
  server_name www.leiedu.org;
  root /data/nginx/html/pc;
  access_log /apps/nginx/logs/pc.leiedu.org-access.log main;

  location /{
    proxy_pass http://webserver;
  }
}

当我使用curl命令访问www.leiedu.org时,已经在轮询显示149和150的首页

upstream配置参数

bash 复制代码
server address [parameters] ;
#配置一个后端web服务器,配置在upstream内,至少要有一个server服务器配置。

#server支持的parameters如下:
weight=number     #设置权重,默认为1,实现类似于LVS中的WRR, WLC等
max_conns=number  #给当前后端server设置最大活动链接数,默认为0表示没有限制
max_fai1s=number  #后端服务器的下线条件,当客户端访问时,对本次调度选中的后端服务器连续进行检测多少次,如果都失败就标记为不可用,
                  #默认为1次,当客户端访问时,才会利用TCP触发对探测后端服务器健康性检查,而非周期性的探测
fail_timeout=time #后端服务器的上线条件,对已经检测到处于不可用的后端服务器,每隔此时间间隔再次进行检测是否恢复可用,
                  #如果发现可用,则将后端服务器参与调度,默认为10秒
backup  #设置为备份服务器,当所有后端服务器不可用时,才会启用此备用服务器
down    #标记为down状态,可以平滑下线后端服务器,新用户不再调度到此主机,旧用户不受影响

hash调度算法

bash 复制代码
将用户的请求调度到哪一台服务器上,为了保持session会话的一致性,当然希望将相同的用户请求调度到同一台服务器上。
nginx可以通过$remote_addr、$http_cookie这些变量将会话保持一致也就是将相同的原始ip和cookie调度到同一台服务器上。
其实这只是解决seeion会话的一种方案,也可以通过java程序来保存session一致。
bash 复制代码
upstream webserver {
  #hash $remote_addr;
  #hash $http_cookie;
  #hash $request_uri;
  server 192.168.10.149;
  server 192.168.10.150;
}
bash 复制代码
hash KEY [consistent] ;
#基于指定请求报文中首部字段或者URI等key做hash计算,使用consistent参数,将使用ketama一致性hash算法,
#适用于后端是cache服务器(如varnish)时使用,consistent定义使用一致性hash运算,一致性hash基于取模运算

#示例
hash $request_uri consistent;#基于用户请求的uri做hash
hash $cookie_sessionid#基于cookie中的sessionid这个key进行hash调度,实现会话绑定
ip_hash;
#源地址hash调度方法,基于的客户端的remote_addr(源地址IPv4的前24位或整个IPv6地址)做hashit算,以实现会话保持
#hash $remote_addr则是对全部32bit的IPv4进行hash计算

负载均衡调度算法

tcp负载均衡配置由stream提供,需要配置在主配置文件main里

bash 复制代码
stream { #定义stream相关的服务;context:main
   upstream redis-server{ #定义后端redis服务器
       server 192.168.10.150:6379;
       server 192.168.10.149:6379;
   }

   upstream mysql-server{ #定义后端mysql服务器
       server 192.168.10.150:3306;
       server 192.168.10.149:3306;
   }

   server {
      listen 6379;
      proxy_pass redis-server;
   }
   
   server {
     listen 3306;
     proxy_pass mysql-server; 
   }
}
相关推荐
diemeng11191 小时前
AI前端开发技能变革时代:效率与创新的新范式
前端·人工智能
bin91533 小时前
DeepSeek 助力 Vue 开发:打造丝滑的复制到剪贴板(Copy to Clipboard)
前端·javascript·vue.js·ecmascript·deepseek
勤奋的凯尔森同学3 小时前
webmin配置终端显示样式,模仿UbuntuDesktop终端
linux·运维·服务器·ubuntu·webmin
丁卯4044 小时前
Go语言中使用viper绑定结构体和yaml文件信息时,标签的使用
服务器·后端·golang
chengooooooo4 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
晴空万里藏片云4 小时前
elment Table多级表头固定列后,合计行错位显示问题解决
前端·javascript·vue.js
曦月合一4 小时前
html中iframe标签 隐藏滚动条
前端·html·iframe
奶球不是球4 小时前
el-button按钮的loading状态设置
前端·javascript
kidding7234 小时前
前端VUE3的面试题
前端·typescript·compositionapi·fragment·teleport·suspense
人间打气筒(Ada)5 小时前
MySQL主从架构
服务器·数据库·mysql