目录
2. 总结redis cluster工作原理,并搭建集群实现扩缩容。
3. 总结 LVS的NAT和DR模型工作原理,并完成DR模型实战。
1. 总结 哨兵机制实现原理,并搭建主从哨兵集群。
# 要先实现主从复制
[root@slave ~]# vim /apps/redis/etc/redis.conf
replicaof 10.0.0.130 6379
masterauth 123456
[root@slave ~]# systemctl restart redis.service
[root@slave ~]# 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> dbsize
(integer) 2
127.0.0.1:6379> get class
"m48"
127.0.0.1:6379> get course
"linux"
127.0.0.1:6379> info Replication
# Replication
role:slave
master_host:10.0.0.130
master_port:6379
master_link_status:up
master_last_io_seconds_ago:8
master_sync_in_progress:0
slave_repl_offset:745
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:c0d4677496ffdfc7a332b545319a66dca4fdddce
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:745
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:745
配置第二个节点
[root@localhost ~]# 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> set name xie
OK
127.0.0.1:6379> set class 123
OK
127.0.0.1:6379> dbsize
(integer) 2
127.0.0.1:6379> exit
[root@localhost ~]# vim /apps/redis/etc/redis.conf
replicaof 10.0.0.130 6379
masterauth 123456
主节点中修改
[root@localhost ~]# vim /apps/redis/etc/redis.conf
masterauth 123456
[root@master ~]# systemctl restart redis
# 配置哨兵
三个机子的配置都是一样的,但是不能直接复制过去
[root@master ~]# cp /usr/local/src/redis-6.2.5/sentinel.conf /apps/redis/etc/
[root@master ~]# ll /apps/redis/etc/
total 108
-rw-r--r-- 1 redis redis 93807 Mar 17 21:17 redis.conf
-rw-r--r-- 1 root root 13768 Mar 17 21:19 sentinel.conf
[root@master ~]# chown redis. /apps/redis/etc/*
[root@master ~]# vim /apps/redis/etc/sentinel.conf
bind 0.0.0.0
logfile "/apps/redis/log/sentinel.log"
sentinel monitor mymaster 10.0.0.130 6379 2
sentinel auth-pass mymaster 123456
[root@master ~]# chown redis. /apps/redis/log/*
[root@master ~]# chown redis. /apps/redis/etc/*
[root@master ~]# ll /apps/redis/etc/
total 108
-rw-r--r-- 1 redis redis 93807 Mar 17 21:17 redis.conf
-rw-r--r-- 1 redis redis 14174 Mar 17 21:37 sentinel.conf
# service文件·启动
[root@master ~]# vim /lib/systemd/system/redis-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
[root@master ~]# scp /lib/systemd/system/redis-sentinel.service 10.0.0.137:/lib/systemd/system/
[root@master ~]# scp /lib/systemd/system/redis-sentinel.service 10.0.0.131:/lib/systemd/system/
主从都执行
[root@master ~]# systemctl enable --now redis-sentinel.service
[root@master ~]# ss -ntl
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 511 0.0.0.0:26379 0.0.0.0:*
LISTEN 0 511 0.0.0.0:6379 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
LISTEN 0 511 [::1]:6379 [::]:*
# 连接
[root@master ~]# redis-cli -p 26379
127.0.0.1:26379> info
# Server
redis_version:6.2.5
redis_git_sha1:00000000
2. 总结redis cluster工作原理,并搭建集群实现扩缩容。
[root@R9-129 ~]# vim /apps/redis/etc/redis.conf
masterauth 123456
requirepass 123456
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-require-full-coverage no
[root@R9-129 ~]# systemctl restart redis
[root@R9-129 ~]# ps aux|grep redis
redis 1463 0.1 0.3 57680 6792 ? Ssl 17:56 0:00 /apps/redis/bin/redis-server 0.0.0.0:6379 [cluster]
root 1471 0.0 0.1 6636 2172 pts/1 S+ 18:00 0:00 grep --color=auto redis
[root@Rocky2 ~]# cat /apps/redis/data/nodes-6379.conf
29cdc75a983d0ca02b350d8d4f45c332acf9d3b7 :0@0 myself,master - 0 0 0 connected
vars currentEpoch 0 lastVoteEpoch 0
#创建集群
[root@R9-129 ~]# redis-cli -a 123456 --cluster create 10.0.0.129:6379 10.0.0.130:6379 10.0.0.131:6379 10.0.0.135:6379 10.0.0.137:6379 10.0.0.139:6379 --cluster-replicas 1
[root@Rocky2 ~]# cat /apps/redis/data/nodes-6379.conf
acf19c56585ebdfdc02333f366e25da2f9ebfd54 10.0.0.129:6379@16379 master - 0 1679310586000 1 connected 0-5460
edfe77c65617d55232b896b4346a5e8aebed0182 10.0.0.135:6379@16379 slave a300a618fadf30c857e407134c3bd4bbf7d60755 0 1679310586288 3 connected
b9bf0fc7400045183d9d30cdbbe97683d5fe4a29 10.0.0.137:6379@16379 slave acf19c56585ebdfdc02333f366e25da2f9ebfd54 0 1679310586391 1 connected
025ac37084e48dda0cd26fba40880c1e015e1bfd 10.0.0.139:6379@16379 slave 29cdc75a983d0ca02b350d8d4f45c332acf9d3b7 0 1679310587311 2 connected
29cdc75a983d0ca02b350d8d4f45c332acf9d3b7 10.0.0.130:6379@16379 myself,master - 0 1679310585000 2 connected 5461-10922
a300a618fadf30c857e407134c3bd4bbf7d60755 10.0.0.131:6379@16379 master - 0 1679310585473 3 connected 10923-16383
vars currentEpoch 6 lastVoteEpoch 0
查看集群状态
[root@Rocky2 ~]# redis-cli -a 123456 cluster help
查看主从关系
[root@R9-129 ~]# redis-cli -a 123456 -c info replication
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Replication
role:master
connected_slaves:1
slave0:ip=10.0.0.137,port=6379,state=online,offset=308,lag=1
master_failover_state:no-failover
master_replid:a5515bcd55ea3378ea0515c3d2ab21838220aff2
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:308
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:308
查看任何节点的信息
[root@R9-129 ~]# redis-cli -a 123456 --cluster info 10.0.0.135:6379
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
10.0.0.129:6379 (acf19c56...) -> 0 keys | 5461 slots | 1 slaves.
10.0.0.131:6379 (a300a618...) -> 0 keys | 5461 slots | 1 slaves.
10.0.0.130:6379 (29cdc75a...) -> 0 keys | 5462 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
启用集群模式
[root@R9-129 ~]# redis-cli -a 123456 -c
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> set k1 v1
-> Redirected to slot [12706] located at 10.0.0.131:6379
OK
10.0.0.131:6379>
计算槽位
10.0.0.131:6379> cluster keyslot k1
(integer) 12706
# 当一个主节点出现故障时:
暂停10.0.0.130机器
[root@Rocky2 ~]# systemctl stop redis
[root@Rocky2 ~]# ss -ntl
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
139变成了主节点
[root@R9-129 ~]# cat /apps/redis/data/nodes-6379.conf
a300a618fadf30c857e407134c3bd4bbf7d60755 10.0.0.131:6379@16379 master - 0 1679311436118 3 connected 10923-16383
b9bf0fc7400045183d9d30cdbbe97683d5fe4a29 10.0.0.137:6379@16379 slave acf19c56585ebdfdc02333f366e25da2f9ebfd54 0 1679311435000 1 connected
29cdc75a983d0ca02b350d8d4f45c332acf9d3b7 10.0.0.130:6379@16379 master,fail - 1679311419632 1679311413000 2 disconnected
acf19c56585ebdfdc02333f366e25da2f9ebfd54 10.0.0.129:6379@16379 myself,master - 0 1679311433000 1 connected 0-5460
025ac37084e48dda0cd26fba40880c1e015e1bfd 10.0.0.139:6379@16379 master - 0 1679311434060 7 connected 5461-10922
edfe77c65617d55232b896b4346a5e8aebed0182 10.0.0.135:6379@16379 slave a300a618fadf30c857e407134c3bd4bbf7d60755 0 1679311435091 3 connected
vars currentEpoch 7 lastVoteEpoch 7
将130重新开启了
[root@Rocky2 ~]# systemctl start redis
130变成了139的从节点
[root@R9-139 ~]# redis-cli -a 123456 info replication
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Replication
role:master
connected_slaves:1
slave0:ip=10.0.0.130,port=6379,state=online,offset=1218,lag=0
master_failover_state:no-failover
master_replid:0a055d5fa6e18410382dc15cff59b6f243ce4060
master_replid2:ffe025cb4e99f758cd9999e010af1b5bdad2e385
master_repl_offset:1218
second_repl_offset:1135
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1218
将139关闭
127.0.0.1:6379> shutdown
not connected>
130则将变成主节点
[root@Rocky2 ~]# redis-cli -a 123456 info replication
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:c78dc8fb5e055718bf27cfed0633974e0de6f170
master_replid2:0a055d5fa6e18410382dc15cff59b6f243ce4060
master_repl_offset:1330
second_repl_offset:1331
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1135
repl_backlog_histlen:196
启动139
[root@R9-139 ~]# systemctl start redis
在130中可以看到,139变成了130的从节点
[root@Rocky2 ~]# redis-cli -a 123456 info replication
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Replication
role:master
connected_slaves:1
slave0:ip=10.0.0.139,port=6379,state=online,offset=1400,lag=0
master_failover_state:no-failover
master_replid:c78dc8fb5e055718bf27cfed0633974e0de6f170
master_replid2:0a055d5fa6e18410382dc15cff59b6f243ce4060
master_repl_offset:1414
second_repl_offset:1331
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1135
repl_backlog_histlen:280
# 扩容
修改配置
[root@R9-140 ~]# vim /apps/redis/etc/redis.conf
masterauth 123456
requirepass 123456
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-require-full-coverage no
[root@R9-140 ~]# systemctl restart redis
调用加入指令
[root@R9-140 ~]# redis-cli -a 123456 --cluster add-node 10.0.0.140:6379 10.0.0.130:6379
分槽位
[root@R9-140 ~]# redis-cli -a 123456 --cluster reshard 10.0.0.130:6379
16384/4=4096
将上面的长条id抄下来
all
yes
查看全部节点的id
[root@R9-140 ~]# redis-cli -a 1123456 cluster nodes
给140加上从节点
[root@R9-140 ~]# redis-cli -a 123456 --cluster add-node 10.0.0.141:6379 10.0.0.130:6379 --cluster-slave --cluster-master-id +刚刚查询到的140的id
# 缩容
查看140的槽位
[root@R9-140 ~]# redis-cli -a 1123456 cluster nodes
[root@R9-140 ~]# redis-cli -a 123456 --cluster reshard 10.0.0.130:6379
1365
写上129的id
done
yes
[root@R9-140 ~]# redis-cli -a 123456 --cluster reshard 10.0.0.130:6379
1366
写上130的id
done
yes
[root@R9-140 ~]# redis-cli -a 123456 --cluster reshard 10.0.0.130:6379
1365
写上131的id
done
yes
在集群中删除140
[root@R9-140 ~]# redis-cli -a 123456 --cluster del-node 10.0.0.130:6379 +140的id
[root@R9-140 ~]# redis-cli -a 123456 --cluster del-node 10.0.0.130:6379 +141的id
3. 总结 LVS的NAT和DR模型工作原理,并完成DR模型实战。
NAT:
原理:就是把客户端发来的数据包的IP头的目的地址,在负载均衡器上换成其中一台RS的IP地址,并发至此RS来处理,RS处理完成后把数据交给经过负载均衡器,负载均衡器再把数据包的原IP地址改为自己的IP,将目的地址改为客户端IP地址即可。
优点:集群中的物理服务器可以使用任何支持TCP/IP操作系统它只需要一个 IP 地址配置在调度器上,服务器组可以用私有的 IP 地址。
缺点:扩展性有限。当服务器节点(普通PC服务器)增长过多时,负载均衡器将成为整个系统的瓶颈,因为所有的请求包和应答包的流向都经过负载均衡器。当服务器节点过多时,大量的数据包都交汇在负载均衡器那,速度就会变慢。
DR:
原理:负载均衡器和RS都使用同一个IP对外服务,但只有DR对ARP请求进行响应,所有RS对本身这个IP的ARP请求保持静默,也就是说,网关会把对这个服务IP的请求全部定向给DR,而DR收到数据包后根据调度算法,找出对应的RS,把目的MAC地址改为RS的MAC(因为IP一致)并将请求分发给这台RS,这时RS收到这个数据包,处理完成之后,由于IP一致,可以直接将数据返给客户,则等于直接从客户端收到这个数据包无异,处理后直接返回给客户端。由于负载均衡器要对二层包头进行改换,所以负载均衡器和RS之间必须在一个广播域,也可以简单的理解为在同一台交换机上。
优点:VS/DR跟 VS/TUN 方法相同,负载调度器中只负责调度请求,而服务器直接将响应返回给客户,可以极大地提高整个集群系统的吞吐量。
缺点:要求负载均衡器的网卡必须与物理网卡在一个物理段上
[root@route ~]# ip a a 172.16.0.200/24 dev eth0 label eth0:1
[root@route ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:0c:29:1d:35:83 brd ff:ff:ff:ff:ff:ff
inet 10.0.0.137/24 brd 10.0.0.255 scope global noprefixroute eth0
valid_lft forever preferred_lft forever
inet 172.16.0.200/24 scope global eth0:1
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fe1d:3583/64 scope link
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:0c:29:1d:35:8d brd ff:ff:ff:ff:ff:ff
inet 192.168.10.200/24 brd 192.168.10.255 scope global noprefixroute eth1
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fe1d:358d/64 scope link
valid_lft forever preferred_lft forever
[root@route ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.0.0.2 0.0.0.0 UG 100 0 0 eth0
10.0.0.0 0.0.0.0 255.255.255.0 U 100 0 0 eth0
172.16.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
192.168.10.0 0.0.0.0 255.255.255.0 U 101 0 0 eth1
[root@Web1 ~]# vim lvs_dr_rs.sh
#!/bin/bash
#Author:wangxiaochun
#Date:2017-08-13
vip=172.16.0.100
mask='255.255.255.255'
dev=lo:1
case $1 in
start)
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
ifconfig $dev $vip netmask $mask
echo "The RS Server is Ready!"
;;
stop)
ifconfig $dev down
echo 0 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 0 > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo 0 > /proc/sys/net/ipv4/conf/all/arp_announce
echo 0 > /proc/sys/net/ipv4/conf/lo/arp_announce
echo "The RS Server is Canceled!"
;;
*)
echo "Usage: $(basename $0) start|stop"
exit 1
;;
esac
root@Web1 ~]# chmod +x lvs_dr_rs.sh
[root@Web1 ~]# ./lvs_dr_rs.sh start
The RS Server is Ready!
[root@Web1 ~]# scp lvs_dr_rs.sh 10.0.0.139:
[root@Web2 ~]# bash lvs_dr_rs.sh
Usage: lvs_dr_rs.sh start|stop
[root@Web2 ~]# bash lvs_dr_rs.sh start
The RS Server is Ready!
[root@LVS ~]# vim lvs_dr_vs.sh
[root@LVS ~]# cat lvs_dr_vs.sh
#!/bin/bash
#Author:wangxiaochun
#Date:2017-08-13
vip='172.16.0.100'
iface='lo:1'
mask='255.255.255.255'
port='80'
rs1='10.0.0.129'
rs2='10.0.0.139'
scheduler='wrr'
type='-g'
rpm -q ipvsadm &> /dev/null || yum -y install ipvsadm &> /dev/null
case $1 in
start)
ifconfig $iface $vip netmask $mask #broadcast $vip up
iptables -F
ipvsadm -A -t ${vip}:${port} -s $scheduler
ipvsadm -a -t ${vip}:${port} -r ${rs1} $type -w 1
ipvsadm -a -t ${vip}:${port} -r ${rs2} $type -w 1
echo "The VS Server is Ready!"
;;
stop)
ipvsadm -C
ifconfig $iface down
echo "The VS Server is Canceled!"
;;
*)
echo "Usage: $(basename $0) start|stop"
exit 1
;;
esac
[root@LVS ~]# ipvsadm -C
[root@LVS ~]# bash lvs_dr_vs.sh start
The VS Server is Ready!
[root@LVS ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.16.0.100:80 wrr
-> 10.0.0.129:80 Route 1 0 0
-> 10.0.0.139:80 Route 1 0 0
4. 总结 http协议的通信过程详解
1 建立TCP连接
在HTTP工作开始之前,Web浏览器首先要通过网络与Web服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建Internet,即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则,只有低层协议建立之后才能进行更深层协议的连接,因此,首先要建立TCP连接,一般TCP连接的端口号是80。
2 Web浏览器向Web服务器发送请求命令
一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令
例如:GET/sample/hello.jsp HTTP/1.1。
3 Web浏览器发送请求头信息
浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。
4 Web服务器应答
客户机向服务器发出请求后,服务器会客户机回送应答,
HTTP/1.1 200 OK
应答的第一部分是协议的版本号和应答状态码。
5 Web服务器发送应答头信息
正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。
6 Web服务器向浏览器发送数据
Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。
7 Web服务器关闭TCP连接
一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码
Connection:keep-alive
TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。
5. 总结 网络IO模型和nginx架构
网络IO模型:
四种IO模型
- 同步阻塞IO(Blocking IO):
- 同步非阻塞IO(Non-Blocking IO)
- IO多路复用(IO Multiplexing)
- 异步IO(Asynchronous IO)
同步阻塞IO(Blocking IO)
在同步阻塞IO模型中,程序用IO调用开始,直到系统调用返回,在这段时间内,进程是阻塞的。直到返回成功后,应用程序开始处理用户空间的缓存区数据.主要分为两个阶段:
等待数据就绪:网络IO就是等待远端数据陆续到达;磁盘IO就是等到磁盘数据从磁盘读取到内核缓冲区。
数据复制: 用户空间的程序没有权限直接读取内核缓冲区的数据(操作系统处于安全的考虑),因此内核与需要把内核缓冲区的数据复制一份到进程缓冲区。
阻塞IO的特点是:在内核进行IO执行的两个阶段,用户线程都被阻塞了。
同步非阻塞IO(None Blocking IO)
将Socket设置为non-blocking,当前连接就变成了非阻塞IO。使用非阻塞模式的IO读写,叫做同步非阻塞IO(None Blocking IO),简称NIO模型。
在同步非阻塞IO模型中,会出现下面几种情况:
在内核缓冲区没有数据的情况下,系统调用会立即返回,返回一个调用失败的信息。这样请求就不会阻塞。
用户线程需要不断的发起IO系统调用,测试内核数据是否准备好。
在内核缓冲区有数据的情况下,是阻塞的。直到内核缓冲区的数据全部复制到进程缓冲区,系统调用成功。
同步非阻塞IO特点:程序需要不断的进行IO系统调用,轮询数据是否准备好,如果没有准备好,就继续轮询。
IO多路复用模型(IO Multiplexing)
在IO多路复用模型中,引入了一种新的系统调用select/epoll,查询IO的就绪状态。通过该系统调用可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓存区可读/可写),内核能够将就绪的状态返回给应用程序。随后,应用程序根据就绪的状态,进行相应的IO系统调用。
在IO多路复用模型中通过select/epoll系统调用,单个应用程序的线程,可以不断轮询成百上千的socket连接,当某个或者某些socket网络连接有IO就绪的状态,就返回对应的可以执行的读写操作。
IO多路复用模型的特点:IO多路复用模型涉及两种系统调用,一种是就绪查询(select/epoll),一种是IO操作。
多路复用IO也需要轮询。负责就绪状态查询系统调用的线程,需要不断的进行select/epoll轮询,查找出达到IO操作就绪的socket连接。
异步IO模型(Asynchronous IO)
异步IO模型(Asynchronous IO)简称AIO,其基本流程为:用户线程通过系统调用,向内核注册某个IO操作。内核在整个IO操作(包括数据准备、数据复制)完成后,通知用户程序,执行后续的业务操作。
在异步IO模型中,整个内核的数据处理过程中,包括内核将数据从网络物理设备(网卡)读取到内核缓存区、将内核缓冲区的数据复制到用户缓冲区,用户程序都不需要阻塞。
异步IO模型的特点:在内核等待数据和复制数据的两个阶段,用户线程都不是阻塞的。当内核的IO操作(等待数据和复制数据)全部完成后,内核会通知应用程序读数据。
四种IO模型的优缺点
3.1 同步阻塞IO
优点:程序开发简单;在阻塞等待数据期间,用户线程挂起,不占用CPU资源。
缺点:一个线程维护一个IO流的读写,在高并发应用场景下,需要大量的线程来维护大量的网络连接,内存、线程切换开销会十分巨大,BIO模型在高并发场景下是不可用的。
3.2 同步非阻塞IO
优点:内核缓冲区没有数据的情况下,发起的系统调用不会阻塞,用户程序不会阻塞,实时性较好。
缺点:需要不断地重复地发起IO调用,这种不断轮询,不断询问内核的方式,会占用CPU大量的时间,资源利用率比较低;在内核缓冲区有数据的情况下,也是阻塞的。NIO模型在高并发场景下是不可用的。
3.3 IO多路复用
优点:select/epoll可以同时处理成百上千的连接,与之前的一个线程维护一个连接相比,IO多路复用则不需要创建线程,也就不需要维护,从而减少系统开销.
缺点: select/epoll系统调用,属于阻塞的模式。读写事件就绪之后,用户自己进行读写,这个读写过程也是阻塞的。
3.4 异步IO
优点:在内核等待数据和复制数据的两个阶段,用户线程都不是阻塞的。
缺点:需要事件的注册,就需要操作系统。
nginx框架:
Nginx功能
- 静态的web资源服务器html,图片、js、css、txt等静态资源
- http/https协议的方向代理
- 结合FastCGI/uWSGI/SCDI等协议方向代理动态资源请求
- tcp/udp协议的请求转发(反向代理)
- imap4/pop3协议的反向代理
基础特性
- 模块化设计,较好的扩展性
- 高可靠性
- 支持热部署:不停机更新配置文件,升级版本,更新日志文件
- 低内存消耗:10000个keep-aliva连接模式下的非活动连接,仅需要2.5M的内存
web服务想关的功能
- 虚拟主机(server)
- 支持keep-aliva和管道连接(利用一个连接做多次请求)
- 访问日志(支持基于日志缓冲,提高其性能)
- url rewirte
- 路径别名
- 基于IP及用户的访问控制
- 支持速率限制及并发数限制
- 重新配置和在线升级而无需终端客户的工作进程
Nginx模块
- 核心模块:正常运行必不可少的
- 标准http模块:提供http协议解析相关功能
- 可选http模块:扩展标准的http功能
- 邮件服务模块:支持Nginx的邮件服务
- stream服务模块:是相反代理功能,包括TCP协议代理
- 第三方模块:扩展Nginx的服务器应用,完成开发者自定义功能
6. 完成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
7. 总结nginx核心配置,并实现nginx多虚拟主机
# 事件驱动相关的配置
event {
……
}
# http/https 协议相关的配置
http {
……
}
# 默认配置文件不包括下面两个快
# mail 协议相关的配置
mail {
……
}
# stream 服务器相关的配置
stream {
……
}
# 自动实现相匹配的核数,实现多worker运作
[root@Rocky2 ~]# vim /apps/nginx/conf/nginx.conf
worker_processes auto;
worker_cpu_affinity auto;
root@Rocky2 ~]# nginx -s reload
[root@Rocky2 ~]# ps auxf |grep nginx
root 17003 0.0 0.1 6408 2148 pts/0 S+ 14:36 0:00 | \_ grep --color=auto nginx
root 16768 0.0 0.3 9892 6640 ? S 13:36 0:00 nginx: master process /apps/nginx/sbin/nginx -c /apps/nginx/conf/nginx.conf
nginx 16998 0.0 0.2 13720 4748 ? S 14:34 0:00 \_ nginx: worker process
nginx 16999 0.0 0.2 13720 4756 ? S 14:34 0:00 \_ nginx: worker process
# 调整工作进程优先级
[root@Rocky2 ~]# vim /apps/nginx/conf/nginx.conf
worker_priority 0;
worker_rlimit_nofile 1000000
events {
worker_connections 10240;
accept_mutex on; # 轮流叫醒,避免惊群现象
}
8. 总结nginx日志格式定制
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"}';