第八周

内容纲要

一、redis搭建哨兵原理和集群实现

1、哨兵原理
Sentinel是一个分布式系统,即需要在多个节点上各自同时运行一个sentinel进程,Sentienl 进程通过流言协议(gossip protocols)来接收关于Master是否下线状态,并使用投票协议(Agreement Protocols)来决定是否执行自动故障转移,并选择合适的Slave作为新的Master,从而实现系统的高可用性

每个Sentinel进程会向其它Sentinel、Master、Slave定时发送消息,来确认对方是否存活,如果发现某个节点在指定配置时间内未得到响应,则会认为此节点已离线,即为主观宕机Subjective Down,简称为 SDOWN

如果哨兵集群中的多数Sentinel进程认为Master存在SDOWN,共同利用 is-master-down-by-addr 命令互相通知后,则认为客观宕机Objectively Down, 简称 ODOWN

接下来利用投票算法,从所有slave节点中,选一台合适的slave将之提升为新Master节点,然后自动修改其它slave相关配置,指向新的master节点,最终实现故障转移failover

Redis Sentinel中的Sentinel节点个数应该为大于等于3且最好为奇数客户端初始化时连接的是Sentinel节点集合,不再是具体的Redis节点,即 Sentinel只是配置中心不是代理。

Redis Sentinel 节点与普通 Redis 没有区别,要实现读写分离依赖于客户端程序

Sentinel 机制类似于MySQL中的MHA功能,只解决master和slave角色的自动故障转移问题,但单个Master 的性能瓶颈问题并没有解决

2、集群搭建
基础环境:master--10.0.0.136,slave1--10.0.0.133,slave2--10.0.0.138。配置主从,使用哨兵要用三台服务器搭建,防止脑裂现象
第一步:首先在三台服务器上安装redis,可以使用yum或者编译安装。官网:https://redis.io/docs/stack/get-started/install/linux/

第二步:redis的主从模式配置很简单,在所有slave服务器的redis配置文件中添加以下配置项即可,master服务器上只需修改前三项,
       vim /etc/redis.conf
       bind 0.0.0.0 -::1 #配置redis可被其他机器远程连接
       masterauth "123456" #配置密码,这里三台redis的密码必须一致,不然后面无法进行主从自动切换
       requirepass "123456" 
       replicaof 10.0.0.136 6379 #指定master的IP和端口号
重启redis服务,systemctl restart redis

验证主从状态,在master上:

[root@rocky ~]#redis-cli -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> info replication  #此命令也可在slave上查看
# Replication
role:master
connected_slaves:2   #目前连接的slave数量
slave0:ip=10.0.0.133,port=6379,state=online,offset=30031,lag=0
slave1:ip=10.0.0.138,port=6379,state=online,offset=30031,lag=0
master_failover_state:no-failover
master_replid:df285f1f871423aa916866ea9e122ddde2f0910e
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:30031
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:30031
127.0.0.1:6379> 

第三步:配置哨兵--sentinel,Sentinel实际上是一个特殊的redis服务器,有些redis指令支持,但很多指令并不支持.默认监听在26379/tcp端口.哨兵服务可以和Redis服务器分开部署在不同主机,但为了节约成本一般会部署在一起

这里是编译安装的,所有直接把源码目录下的sentinel.conf文件复制到安装目录并稍加修改即可,所有的服务器上sentinel.conf配置文件一致
cp redis-6.2.5/sentinel.conf /apps/redis/etc/sentinel.conf  #复制sentinel.conf
chown redis.redis /apps/redis/etc/sentinel.conf #修改权限

vim /apps/redis/etc/sentinel.conf
bind 0.0.0.0 
port 26379 #端口
daemonize no #这个如果不配置service文件,那就改为yes,可以后台启动。配置了service文件必须改为no,不然服务无法启动
sentinel monitor mymaster 10.0.0.136 6379 2 #mymaster为集群名称,后面跟的是master地址端口及投票数字一般此值是所有sentinel节点(一般总数是>=3的 奇数,如:3,5,7等)的一半以上的整数值,比如,总数是3,即3/2=1.5,取整为2,是master的ODOWN客观下线的依据
sentinel auth-pass mymaster 123456 #集群中master的密码,必须在上一行的下面
sentinel down-after-milliseconds mymaster 3000 #判断集群中所有节点的主观下线时间,默认30000毫秒,也就是30S,这里改为3S,也就是说3S内即可由两个slave判断master是否下线并接管master节点。

此配置文件修改完毕后使用rsync复制到其他两个slave上,保证还是redis用户权限,因为启动服务后会自动在sentinel.conf自动生成sentinel myid 一行ID,如果一样手动修改即可。我这里复制完开启服务的时候不一样。

sentinel.service配置文件

vim /lib/systemd/system/sentinel.service
[Unit]
Description=Redis Sentinel
After=network.target
[Service]
ExecStart=/apps/redis/bin/redis-sentinel /apps/redis/etc/sentinel.conf --supervised systemd
ExecStop=/bin/kill -s QUIT $MAINPID
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target


三台服务器上启动服务:systemctl enable --now sentinel.service

第四步:验证集群状态并测试是否会自动切换master
在master上查看状态:

[root@rocky ~]#redis-cli -p 26379
127.0.0.1:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=10.0.0.136:6379,slaves=2,sentinels=3
127.0.0.1:26379> 

手动关闭master上的redis服务,测试是否会自动切换master
在master上执行:systemctl stop redis 
在slave1上查看是否切换成功:

[root@rocky ~]#redis-cli -a 123456
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=10.0.0.138,port=6379,state=online,offset=320721,lag=0
master_failover_state:no-failover
master_replid:c6359cdad255c8fac11fe03d3585498e24b0932a
master_replid2:df285f1f871423aa916866ea9e122ddde2f0910e
master_repl_offset:320721
second_repl_offset:320429
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:320721
127.0.0.1:6379> 
#这里看到slave1已经切换为master,slave只有一台10.0.0.138,然后在原master10.0.0.136上启动redis查看是否会加入到slave中。

10.0.0.136上执行systemctl start redis,在10.0.0.133上查看结果

[root@rocky ~]#redis-cli -a 123456
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=10.0.0.138,port=6379,state=online,offset=346637,lag=0
slave1:ip=10.0.0.136,port=6379,state=online,offset=0,lag=0
master_failover_state:no-failover
master_replid:c6359cdad255c8fac11fe03d3585498e24b0932a
master_replid2:df285f1f871423aa916866ea9e122ddde2f0910e
master_repl_offset:346637
second_repl_offset:320429
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:346637
127.0.0.1:6379> 

[root@rocky ~]#redis-cli -p 26379
127.0.0.1:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=10.0.0.133:6379,slaves=2,sentinels=3
127.0.0.1:26379> 

#结果来看,10.0.0.136已经切换为slave,配置完成。

二、LVS常用模型工作原理,及实现

LVS工作模型:lvs-nat、lvs-dr、lvs-tun、lvs-fullnat,常用模型为lvs-nat和lvs-dr模型

lvs-nat:本质是多目标IP的DNAT,通过将请求报文中的目标地址和目标端口修改为某挑出的RS的RIP和PORT实现转发
(1)RIP和DIP应在同一个IP网络,且应使用私网地址;RS的网关要指向DIP
(2)请求报文和响应报文都必须经由Director转发,Director易于成为系统瓶颈
(3)支持端口映射,可修改请求报文的目标PORT
(4)VS必须是Linux系统,RS可以是任意OS系统
lvs-dr:Direct Routing,直接路由,LVS默认模式,应用最广泛,通过为请求报文重新封装一个MAC首部进行转发,源MAC是DIP所在的接口的MAC,目标MAC是某挑选出的RS的RIP所在接口的MAC地址;源IP/PORT,以及目标IP/PORT均保持不变

lvs-nat实现:
client:192.168.10.128  #仅主机模式
lvs-server:eth0:192.168.10.129 #仅主机模式
            eth1:10.0.0.133  #nat模式
rs1:10.0.0.136  #nat模式
rs2: 10.0.0.138  #nat模式

1、rs1和rs2上执行:
yum -y install httpd;hostname -I > /var/www/html/index.html;systemctl enable --now httpd
ip route del default via 10.0.0.2 dev eth0 proto dhcp metric 100 #删除默认路由
ip route add default via 10.0.0.133 dev eth0 proto dhcp metric 100  #添加路由为lvs-server

2、lvs-server上操作: 
#首先测试rs1和rs2的网页是否生效,
[21:50:19 root@rocky ~]#curl 10.0.0.136  #可以正常访问
10.0.0.136 
[21:53:32 root@rocky ~]#curl 10.0.0.138  #可以正常访问
10.0.0.138 

3、安装ipvsadm,添加规则。
yum -y install ipvsadm
ipvsadm -A -t 192.168.10.129:80 -s wrr #这里使用加权重轮巡也可使用rr轮巡方式
ipvsadm -a -t 192.168.10.129:80 -r 10.0.0.136:80 -m #添加rs1和rs2的网站
ipvsadm -a -t 192.168.10.129:80 -r 10.0.0.138:80 -m

#开启路由转发功能:
echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf #开启此功能后client在lvs-server访问rs的请求会转发到rs服务器上。
sysctl -p 

4、在client上测试连接:
[root@centos7 ~]# while true;do curl 192.168.10.129;sleep 1; done
10.0.0.138 
10.0.0.136 
10.0.0.138 
10.0.0.136 
10.0.0.138 
10.0.0.136 
10.0.0.138 
10.0.0.136 
10.0.0.138 
10.0.0.136 
10.0.0.138 

#这里没有设置权重,所有每个机器轮流一次。所有的服务器均关闭了防火墙和selinux。
################拓展知识##############
ipvsadm 保存规则:ipvsadm -Sn > /etc/sysconfig/ipvsadm
        清除规则:ipvsadm -C
        查看规则:ipvsadm -Ln
        加载规则:ipvsadm -R < /etc/sysconfig/ipvsadm
修改权重--清除完规则后重新添加:
ipvsadm -a -t 192.168.10.129:80 -r 10.0.0.138:80 -m -w 3 #修改为3后访问rs2服务器3次,访问rs1服务器一次。

三、LVS的负载策略有哪些,各应用在什么场景,通过LVS DR任意实现1-2种场景

LVS负载策略主要分两种,静态方法和动态方法。
根据算法策略调度方法:
静态方法:
1、RR:roundrobin,轮询,较常用,雨露均沾,大锅饭
2、WRR:Weighted RR,加权轮询,较常用
3、SH:Source Hashing,实现session sticky,源IP地址hash;将来自于同一个IP地址的请求始终发往第一次挑中的RS,从而实现会话绑定
4、DH:Destination Hashing;目标地址哈希,第一次轮询调度至RS,后续将发往同一个目标地址的请求始终转发至第一次挑中的RS,典型使用场景是正向代理缓存场景中的负载均衡,如: Web缓存

动态方法:根据每RS当前的负载状态及调度算法进行调度Overhead=value 较小的RS将被调度
1、LC:least connections 适用于长连接应用
2、WLC:Weighted LC,默认调度方法,较常用
3、SED:Shortest Expection Delay,初始连接高权重优先,只检查活动连接,而不考虑非活动连接
4、NQ:Never Queue,第一轮均匀分配,后续SED
5、LBLC:Locality-Based LC,动态的DH算法,使用场景:根据负载状态实现正向代理,实现WebCache等
6、LBLCR:LBLC with Replication,带复制功能的LBLC,解决LBLC负载不均衡问题,从负载重的复制到负载轻的RS,,实现Web Cache等

lvs-dr场景实现:
环境:五台主机
一台:client eth0:仅主机 192.168.10.128/24 GW:192.168.10.200
一台:route
          eth0 :NAT 10.0.0.200/24
          eth1: 仅主机 192.168.10.200/24
启用 IP_FORWARD
一台:lvs
eth1:NAT:DIP:10.0.0.133/24 GW:10.0.0.200
两台rs:
rs1:eth0:NAT:10.0.0.136/24 GW:10.0.0.200  lo:vip:10.0.0.100
rs2:eth0:NAT:10.0.0.138/24 GW:10.0.0.200  lo:vip:10.0.0.100

1、上面的实验中rs1和rs2已经配置好了,重新制定网关即可,lvs-server删除仅主机网卡,指定网关即可,
rs1和rs2:
ip route del default via 10.0.0.133 dev eth0 proto dhcp metric 100 #删除默认路由
ip route add default via 10.0.0.200 dev eth0 proto dhcp metric 100  #添加路由为route
lvs-server:
ip route del default via 10.0.0.2 dev eth0 proto dhcp metric 100 #删除默认路由
ip route add default via 10.0.0.200 dev eth0 proto dhcp metric 100  #添加路由为route

2、route上:
[root@router ~]#echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
[root@router ~]#sysctl -p

3、rs1和rs2的lo网卡配置方法一致:
[root@rocky ~]#echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
[root@rocky ~]#echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
[root@rocky ~]#echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
[root@rocky ~]#echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
[root@rocky ~]#ifconfig lo:1 10.0.0.100/32

4、lvs-server上:
断开eth0的仅主机连接
ifconfig lo:1 10.0.0.100/32
清空之前设置的ipvsadm规则:ipvsadm -C
重新添加规则:
ipvsadm -A -t 10.0.0.100:80 -s rr
ipvsadm -a -t 10.0.0.100:80 -r 10.0.0.136:80 -g
ipvsadm -a -t 10.0.0.100:80 -r 10.0.0.138:80 -g

5、测试访问:
[root@centos7 ~]# while true;do curl 10.0.0.100;sleep 1; done

10.0.0.136 
10.0.0.138 
10.0.0.136 
10.0.0.138 
10.0.0.136 
10.0.0.138 
10.0.0.136 
10.0.0.138 
10.0.0.136 
10.0.0.138 

四、web http协议通信过程,相关技术术语总结

1、服务器创建新的套接字,将套接字绑定到80端口上去,允许套接字进行连接,等待连接。
2、客户端获取IP地址和端口号,创建新的套接字,连接到服务器上IP:PORT上去
3、通知应用程序有连接到来,开始读取请求
4、客户端连接成功,发送http请求,等待http响应
5、处理http请求报文,回送http响应,关闭连接
6、处理http响应,关闭连接

五、总结网络IO模型和nginx架构

5.1网络 IO模型
阻塞型、非阻塞型、复用型、信号驱动型、异步

阻塞IO模型
阻塞IO模型是最简单的I/O模型,用户线程在内核进行IO操作时被阻塞
用户线程通过系统调用read发起I/O读操作,由用户空间转到内核空间。内核等到数据包到达后,然后将接收的数据拷贝到用户空间,完成read操作
用户需要等待read将数据读取到buffer后,才继续处理接收的数据。整个I/O请求的过程中,用户线程是被阻塞的,这导致用户在发起IO请求时,不能做任何事情,对CPU的资源利用率不够
优点:程序简单,在阻塞等待数据期间进程/线程挂起,基本不会占用 CPU 资源
缺点:每个连接需要独立的进程/线程单独处理,当并发请求量大时为了维护程序,内存、线程切换开销较大,apache 的prefork使用的是这种模式。
同步阻塞:程序向内核发送I/O请求后一直等待内核响应,如果内核处理请求的IO操作不能立即返回,则进程将一直等待并不再接受新的请求,并由进程轮询查看I/O是否完成,完成后进程将I/O结果返回给Client,在IO没有返回期间进程不能接受其他客户的请求,而且是有进程自己去查看I/O是否完成,这种方式简单,但是比较慢,用的比较少。

非阻塞IO模型
用户线程发起IO请求时立即返回。但并未读取到任何数据,用户线程需要不断地发起IO请求,直到数据到达后,才真正读取到数据,继续执行。即 “轮询”机制存在两个问题:如果有大量文件描述符都要等,那么就得一个一个的read。这会带来大量的Context Switch(read是系统调用,每调用一次就得在用户态和核心态切换一次)。轮询的时间不好把握。这里是要猜多久之后数据才能到。等待时间设的太长,程序响应延迟就过大;设的太短,就会造成过于频繁的重试,干耗CPU而已,是比较浪费CPU的方式,一般很少直接使用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。

 I/O 多路复用型
IO多路复用(IO Multiplexing) :是一种机制,程序注册一组socket文件描述符给操作系统,表示“我要监视这些fd是否有IO事件发生,有了就告诉程序处理”
IO多路复用一般和NIO一起使用的。NIO和IO多路复用是相对独立的。NIO仅仅是指IO API总是能立刻返回,不会被Blocking;而IO多路复用仅仅是操作系统提供的一种便利的通知机制。操作系统并不会强制这俩必须得一起用,可以只用IO多路复用 + BIO,这时还是当前线程被卡住。IO多路复用和NIO是要配合一起使用才有实际意义
IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,就通知该进程多个连接共用一个等待机制,本模型会阻塞进程,但是进程是阻塞在select或者poll这两个系统调用上,而不是阻塞在真正的IO操作上用户首先将需要进行IO操作添加到select中,同时等待select系统调用返回。当数据到达时,IO被激活,
select函数返回。用户线程正式发起read请求,读取数据并继续执行从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视IO,以及调用select函数的额外操作,效率更差。并且阻塞了两次,但是第一次阻塞在select上时,select可以监控多个IO上是否已有IO操作准备就绪,即可达到在同一个线程内同时处理多个IO请求的目的。而不像阻塞IO那种,一次只能监控一个IO虽然上述方式允许单线程内处理多个IO请求,但是每个IO请求的过程还是阻塞的(在select函数上阻塞),平均时间甚至比同步阻塞IO模型还要长。如果用户线程只是注册自己需要的IO请求,然后去做自己的事情,等到数据到来时再进行处理,则可以提高CPU的利用率IO多路复用是最常使用的IO模型,但是其异步程度还不够“彻底”,因它使用了会阻塞线程的select系统调用。因此IO多路复用只能称为异步阻塞IO模型,而非真正的异步IO

优点:可以基于一个阻塞对象,同时在多个描述符上等待就绪,而不是使用多个线程(每个文件描述符一个线程),这样可以大大节省系统资源
缺点:当连接数较少时效率相比多线程+阻塞 I/O 模型效率较低,可能延迟更大,因为单个连接处理需要 2 次系统调用,占用时间会有增加

IO多路复用适用如下场合
当客户端处理多个描述符时(一般是交互式输入和网络套接口),必须使用I/O复用
当一个客户端同时处理多个套接字时,此情况可能的但很少出现
当一个服务器既要处理监听套接字,又要处理已连接套接字,一般也要用到I/O复用
当一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用
当一个服务器要处理多个服务或多个协议,一般要使用I/O复用

信号驱动式 I/O 模型 
信号驱动I/O的意思就是进程现在不用傻等着,也不用去轮询。而是让内核在数据就绪时,发送信号通知进程。调用的步骤是,通过系统调用 sigaction ,并注册一个信号处理的回调函数,该调用会立即返回,然后主程序可以继续向下执行,当有I/O操作准备就绪,即内核数据就绪时,内核会为该进程产生一个 SIGIO信号,并回调注册的信号回调函数,这样就可以在信号回调函数中系统调用 recvfrom 获取数据,将用户进程所需要的数据从内核空间拷贝到用户空间此模型的优势在于等待数据报到达期间进程不被阻塞。用户主程序可以继续执行,只要等待来自信号处理函数的通知。
在信号驱动式 I/O 模型中,应用程序使用套接口进行信号驱动 I/O,并安装一个信号处理函数,进程继续运行并不阻塞当数据准备好时,进程会收到一个 SIGIO 信号,可以在信号处理函数中调用 I/O 操作函数处理数据。

优点:线程并没有在等待数据时被阻塞,内核直接返回调用接收信号,不影响进程继续处理其他请求因此可以提高资源的利用率
缺点:信号 I/O 在大量 IO 操作时可能会因为信号队列溢出导致没法通知

异步 I/O 模型
异步I/O 与 信号驱动I/O最大区别在于,信号驱动是内核通知用户进程何时开始一个I/O操作,而异步I/O是由内核通知用户进程I/O操作何时完成,两者有本质区别,相当于不用去饭店场吃饭,直接点个外卖,把等待上菜的时间也给省了相对于同步I/O,异步I/O不是顺序执行。用户进程进行aio_read系统调用之后,无论内核数据是否准备
好,都会直接返回给用户进程,然后用户态进程可以去做别的事情。等到socket数据准备好了,内核直接复制数据给进程,然后从内核向进程发送通知。IO两个阶段,进程都是非阻塞的。信号驱动IO当内核通知触发信号处理程序时,信号处理程序还需要阻塞在从内核空间缓冲区拷贝数据到用户空间缓冲区这个阶段,而异步IO直接是在第二个阶段完成后,内核直接通知用户线程可以进行后续操作了
优点:异步 I/O 能够充分利用 DMA 特性,让 I/O 操作与计算重叠
缺点:要实现真正的异步 I/O,操作系统需要做大量的工作。目前 Windows 下通过 IOCP 实现了真正的异步 I/O,在 Linux 系统下,Linux 2.6才引入,目前 AIO 并不完善,因此在 Linux 下实现高并发网络编程时以 IO 复用模型模式+多线程任务的架构基本可以满足需求

Linux提供了AIO库函数实现异步,但是用的很少。目前有很多开源的异步IO库,例如libevent、libev、libuv。
5.2 nginx架构
1、nginx会生成一个主进程。
2、主进程会生成若干子进程,子进程可根据CPU设置数量,
3、子进程为用户提供服务,每个子进程可以为多个用户同时提供服务,并发性高。
4、子进程在linux下使用epoll和select,在windows中只支持select,性能降低很多
5、nginx支持http、fastcgi、memcache等协议
6、nginx可以作为代理和反向代理,也可以作为缓存服务器。

六、nginx总结核心配置和优化

1、关闭nginx显示版本选项
server_tokens off;

2、自定义错误页面
error_page 404 /40x.html;
location = /40x.html {
root /data/html/ ;
}  

3、检测文件是否存在 ,  如果不存在页面, 就转到首页
location / {
	root /data/nginx/html/pc;
	index index.html;
	try_files $uri $uri.html $uri/index.html /about/default.html;
#try_files $uri $uri/index.html $uri.html =489;
}

4、以json格式记录nginx日志,在http中添加
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_protocol_addr",'
'"http_user_agent":"$http_user_agent",'
'"status":"$status"}';
##记录日志格式行
access_log /apps/nginx/logs/access_json.log access_json;
###安装jq工具,可将json日志格式转换成易读字符界面

5、nginx开启压缩功能,可在 http, server, location, if in location`中使用。用于节约带宽
gzip on;#开启gzip压缩
gzip_comp_level 5;#压缩比1-9,默认1
gzip_min_length 1k;#压缩文件的最小值
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/gif image/png;
gzip_vary on;#响应报文头部插入Vary:Accept-Encoding,一般建议打开

6、实现HSTS,客户端访问80端口重定向到443https协议端口
server {
	listen 80;    
	listen 443 ssl;
	ssl_certificate /etc/nginx/ssl/www.magedu.org.crt;
	ssl_certificate_key /etc/nginx/ssl/www.magedu.org.key;
	ssl_session_cache shared:sslcache:20m;
    ssl_session_timeout 10m;
    server_name www.magedu.org;
    root /data/nginx/html/pc;
    if ($scheme = http)	{
    	return https://www.magedu.org/;    
    }
    location = /nginx_status;{
        stub_status;
    }
}

7、生产环境下用户在访问某个页面时由http跳转到https会导致原页面丢失。防止此类情况发生可以使用以下方案。
#全站跳转
location / {
	root /data/nginx/html;
	index index.html;
	if ($scheme = http ){  #在80端口和443端口在一个server中时,如果不加if条件判断会导致死循环
		rewrite ^/(.*) https://$host/$1 redirect;#redirect相当于302,临时跳转
	}
}
#针对特定URL跳转
location /login {
    if ($scheme = http ) {
        rewrite / https://$host/login redirect;
    }
}

8、判断用户浏览器包含哪些字符,通常用于判断用户是用PC还是移动设备访问网站,然后返回给用户对应的网站信息
#如果客户端浏览器包含MSIE,则rewrite客户端请求到/msie/目录下
if ( $http_user_agent ~ MSIE ) {
	rewrite ^(.*)$ /msie/$1 break;
}	

#
案例2: 更换目录访问方式,目录转换为对象存储形式
#要求:
#/20200106/static ->/static?id=20200106
#/20200123/image ->/image?id=20200123
rewrite ^/(\d+)/(.+)/ /$2?id=$l last;
#案例3:多目录转换访问方式
#要求: www.magedu.com/images/20200106/1.jpg => www.magedu.com/index.do?
name=images&dir=20200106=&file=1.jpg
#规则配置:
if ($host ~* (.*)\.magedu\.com) {
	rewrite ^/(.*)/(\d+)/(.*)$ /index.do?name=$1&dir=$2&file=$3 last;
}

9、添加防盗链功能
location /images {
root /data/nginx/html/pc;
index index.html;
valid_referers none blocked server_names
*.example.com example.* www.example.org/galleries/
~\.google\.;
if ($invalid_referer) {
return 403;
}

10、nginx优化之开启缓存功能,性能提高一倍
例:缓存的路径文件目录结构等配置只能放在http模块下
proxy_cache_path /var/cache/nginx/proxy_cache #定义缓存保存路径,proxy_cache会自动创建
levels=1:2:2 #定义缓存目录结构层次,1:2:2可以生成2^4x2^8x2^8=2^20=1048576个目录
keys_zone=proxycache:20m #指内存中缓存的大小,主要用于存放key和metadata(如:使用次数),一般1M可存放8000个左右的key
inactive=120s #缓存有效时间
max_size=10g; #最大磁盘占用空间,磁盘存入文件内容的缓存空间最大值

server{

    location /static { #要缓存的URL 或者放在server配置项对所有URL都进行缓存
		proxy_pass http://10.0.0.18:80;#代理的地址
		proxy_cache proxycache;#缓存名称
		proxy_cache_key $request_uri;#对指定的数据进行MD5的运算做为缓存的key
		#proxy_cache_key $host$uri$is_args$args;
		proxy_cache_valid 200 302 301 10m; #指定的状态码返回数据的缓存时间
		proxy_cache_valid any 5m; #除了指定的状态码其他数据的缓存时间,必须设置
    	#proxy_set_header clientip $remote_addr;
				}
}

11、添加报文头部的响应信息
此语法可以在一下模块使用
http, server, location, if in location
#添加响应报文的自定义首部:
add_header name value [always];
例: add_header Country Nanliang;
#示例:
add_header X-Via $server_addr; #当前nginx主机的IP
add_header X-Cache $upstream_cache_status; #是否缓存命中,命中为HIT,未命中是MISS
add_header X-Accel $server_name; #客户访问的FQDN

12、反向代理实现IP透传
server {
	listen 80;
	server_name www.magedu.org;
	location / {
		index index.html index.php;
		root /data/nginx/html/pc;
		proxy_pass http://10.0.0.18;
		#proxy_set_header X-Real-IP $remote_addr; #只添加客户端IP到请求报文头部,转发至后端服务器
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #添加客户端IP和反
		向代理服务器IP到请求报文头部
			}
} #说明$proxy_add_x_forwarded_for
#此变量表示将客户端IP追加请求报文中X-Forwarded-For首部字段,多个IP之间用逗号分隔,如果请求中没
有X-Forwarded-For,就使用$remote_addr

如果后端服务器是Apache,需在/etc/httpd/conf/httpd.conf中的LogFormat中添加X-Forwarded-For}
例:vim /etc/httpd/conf/httpd.conf
LogFormat "%{X-Forwarded-For}i %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"% {User-Agent}i\"" combined

如果后端服务器是nginx则不用配置,默认nginx有此配置

13、反向代理负载均衡
upstream {
	server 10.0.0.18;
	server 10.0.0.28;
}
格式为:server address parameters;
parameters支持的选项如下
weight=number #设置权重,默认为1,实现类似于LVS中的WRR,WLC等
max_conns=number #给当前后端server设置最大活动链接数,默认为0表示没有限制
max_fails=number #后端服务器的下线条件,当客户端访问时,对本次调度选中的后端服务器连续进行检测
多少次,如果都失败就标记为不可用,默认为1次,当客户端访问时,才会利用TCP触发对探测后端服务器健康性
检查,而非周期性的探测
fail_timeout=time #后端服务器的上线条件,对已经检测到处于不可用的后端服务器,每隔此时间间隔再次
进行检测是否恢复可用,如果发现可用,则将后端服务器参与调度,默认为10秒
backup #设置为备份服务器,当所有后端服务器不可用时,才会启用此备用服务器
#例:18和28服务器都宕机了,可添加一条server 127.0.0.1 backup;
设置此页面为通知或者道歉页面即可
down #标记为down状态,可以平滑下线后端服务器,新用户不再调度到此主机,旧用户不受影响
#例:10.0.0.18要升级版本时,可将server 10.0.0.18;改为server 10.0.0.18 down;
nginx -s reload ,旧用户可不受影响,新用户将访问不到10.0.0.18,等待无连接后可升级,升级完后删除down,nginx -s reload即可。

七、使用脚本完成一键编译安装nginx任意版本

#!/bin/bash
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

八、任意编译一个第3方nginx模块,并使用

上面脚本已经准备好了,首先下载nginx-module-vts模块,然后编译进去。#注意:第三方模块只能第一次编译的时候添加,
cd /data
git clone git://github.com/vozlt/nginx-module-vts.git
############这里clone如果出现问题,推荐使用以下办法解决:
git config --global url."https://github.com".insteadOf git://github.com #如果执行完这个命令不行再执行下面的命令

git config --global --unset http.proxy
git config --global --unset https.proxy

1、把--add-module=/data/nginx-module-vts添加到脚本编译命令最后
./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 --add-module=/data/nginx-module-vts

2、执行脚本即可.
安装完毕后在nginx.conf文件中添加:

http模块下添加:

    vhost_traffic_status_zone;

server模块中添加:

location /status {
         vhost_traffic_status_display;
         vhost_traffic_status_display_format html;
}

3、重启服务:systemctl restart nginx

4、访问:10.0.0.136/status

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注