文章目录
- Nginx安装
- Nginx平滑升级与回滚
- Nginx核心配置
- Nginx高级配置
- Nginx自定义访问日志
- Nginx压缩功能
- Https功能
- Rewrite相关功能
- Nginx防盗链
- Nginx反向代理
- Http反向代理负载均衡
- 负载均衡调度算法
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;
}
}