检测两台服务器指定目录下的文件一致性 #!/bin/bash ##################################### #检测两台服务器指定目录下的文件一致性 ##################################### #通过对比两台服务器上文件的md5值，达到检测一致性的目的 dir=/data/web b_ip=192.168.88.10 #将指定目录下的文件全部遍历出来并作为md5sum命令的参数，进而得到所有文件的md5值，并写入到指定文件中 find $dir -type f|xargs md5sum > /tmp/md5_a.txt ssh $b_ip "find $dir -type f|xargs md5sum > /tmp/md5_b.txt" scp $b_ip:/tmp/md5_b.txt /tmp #将文件名作为遍历对象进行一一比对 for f in `awk '{print 2} /tmp/md5_a.txt'` do #以a机器为标准，当b机器不存在遍历对象中的文件时直接输出不存在的结果 if grep -qw "$f" /tmp/md5_b.txt then md5_a=`grep -w "$f" /tmp/md5_a.txt|awk '{print 1}'` md5_b=`grep -w "$f" /tmp/md5_b.txt|awk '{print 1}'` #当文件存在时，如果md5值不一致则输出文件改变的结果 if [ $md5_a != $md5_b ] then echo "$f changed." fi else echo "$f deleted." fi done定时清空文件内容，定时记录文件大小#!/bin/bash ################################################################ #每小时执行一次脚本（任务计划），当时间为0点或12点时，将目标目录下的所有文件内 #容清空，但不删除文件，其他时间则只统计各个文件的大小，一个文件一行，输出到以时#间和日期命名的文件中，需要考虑目标目录下二级、三级等子目录的文件 ################################################################ logfile=/tmp/`date +%H-%F`.log n=`date +%H` if [ $n -eq 00 ] || [ $n -eq 12 ] then #通过for循环，以find命令作为遍历条件，将目标目录下的所有文件进行遍历并做相应操作 for i in `find /data/log/ -type f` do true > $i done else for i in `find /data/log/ -type f` do du -sh $i >> $logfile done fi检测网卡流量，并按规定格式记录在日志中#!/bin/bash ####################################################### #检测网卡流量，并按规定格式记录在日志中 #规定一分钟记录一次 #日志格式如下所示: #2019-08-12 20:40 #ens33 input: 1234bps #ens33 output: 1235bps ######################################################3 while : do #设置语言为英文，保障输出结果是英文，否则会出现bug LANG=en logfile=/tmp/`date +%d`.log #将下面执行的命令结果输出重定向到logfile日志中 exec >> $logfile date +"%F %H:%M" #sar命令统计的流量单位为kb/s，日志格式为bps，因此要*1000*8 sar -n DEV 1 59|grep Average|grep ens33|awk '{print $2,"\t","input:","\t",$5*1000*8,"bps","\n",$2,"\t","output:","\t",$6*1000*8,"bps"}' echo "####################" #因为执行sar命令需要59秒，因此不需要sleep done计算文档每行出现的数字个数，并计算整个文档的数字总数 #!/bin/bash ######################################################### #计算文档每行出现的数字个数，并计算整个文档的数字总数 ######################################################## #使用awk只输出文档行数（截取第一段） n=`wc -l a.txt|awk '{print $1}'` sum=0 #文档中每一行可能存在空格，因此不能直接用文档内容进行遍历 for i in `seq 1 $n` do #输出的行用变量表示时，需要用双引号 line=`sed -n "$i"p a.txt` #wc -L选项，统计最长行的长度 n_n=`echo $line|sed s'/[^0-9]//'g|wc -L` echo $n_n sum=$[$sum+$n_n] done echo "sum:$sum"杀死所有脚本 #!/bin/bash if [ $# -ne 1 ]; then echo "Usage: $0 filename" fi dir=$(dirname $1) file=$(basename $1) ftp -n -v << EOF # -n 自动登录 open 192.168.1.10 # ftp服务器 user admin password binary # 设置ftp传输模式为二进制，避免MD5值不同或.tar.gz压缩包格式错误 cd $dir get "$file" EOF从 FTP 服务器下载文件 #!/bin/bash if [ $# -ne 1 ]; then echo "Usage: $0 filename" fi dir=$(dirname $1) file=$(basename $1) ftp -n -v << EOF # -n 自动登录 open 192.168.1.10 # ftp服务器 user admin password binary # 设置ftp传输模式为二进制，避免MD5值不同或.tar.gz压缩包格式错误 cd $dir get "$file" EOF连续输入5个100以内的数字，统计和、最小和最大 #!/bin/bash COUNT=1 SUM=0 MIN=0 MAX=100 while [ $COUNT -le 5 ]; do read -p "请输入1-10个整数：" INT if [[ ! $INT =~ ^[0-9]+$ ]]; then echo "输入必须是整数！" exit 1 elif $INT -gt 100 ; then echo "输入必须是100以内！" exit 1 fi SUM=$(($SUM+$INT)) [ $MIN -lt $INT ] && MIN=$INT [ $MAX -gt $INT ] && MAX=$INT let COUNT++ done echo "SUM: $SUM" echo "MIN: $MIN" echo "MAX: $MAX"用户猜数字 #!/bin/bash # 脚本生成一个 100 以内的随机数,提示用户猜数字,根据用户的输入,提示用户猜对了, # 猜小了或猜大了,直至用户猜对脚本结束。 # RANDOM 为系统自带的系统变量,值为 0‐32767的随机数 # 使用取余算法将随机数变为 1‐100 的随机数num=$[RANDOM%100+1]echo "$num" # 使用 read 提示用户猜数字 # 使用 if 判断用户猜数字的大小关系:‐eq(等于),‐ne(不等于),‐gt(大于),‐ge(大于等于), # ‐lt(小于),‐le(小于等于) while : do read -p "计算机生成了一个 1‐100 的随机数,你猜: " cai if [ $cai -eq $num ] then echo "恭喜,猜对了" exit elif [ $cai -gt $num ] then echo "Oops,猜大了" else echo "Oops,猜小了" fi done监测Nginx访问日志 502 情况，并做相应动作假设服务器环境为lnmp，近期访问经常出现502现象，且502错误在重启php-fpm服务后消失，因此需要编写监控脚本，一旦出现502，则自动重启php-fpm服务。 #场景： #1.访问日志文件的路径：/data/log/access.log #2.脚本死循环，每10秒检测一次，10秒的日志条数为300条，出现502的比例不低于10%（30条）则需要重启php-fpm服务 #3.重启命令为：/etc/init.d/php-fpm restart #!/bin/bash ########################################################### #监测Nginx访问日志502情况，并做相应动作 ########################################################### log=/data/log/access.log N=30 #设定阈值 while : do #查看访问日志的最新300条，并统计502的次数 err=`tail -n 300 $log |grep -c '502" '` if [ $err -ge $N ] then /etc/init.d/php-fpm restart 2> /dev/null #设定60s延迟防止脚本bug导致无限重启php-fpm服务 sleep 60 fi sleep 10 done将结果分别赋值给变量应用场景：希望将执行结果或者位置参数赋值给变量，以便后续使用。方法一for i in $(echo "4 5 6"); do eval a$i=$i done echo $a4 $a5 $a6方法2 将位置参数192.168.1.1{1,2}拆分为到每个变量num=0 for i in $(eval echo $*);do #eval将{1,2}分解为1 2 let num+=1 eval node${num}="$i" done echo $node1 $node2 $node3 # bash a.sh 192.168.1.1{1,2} 192.168.1.11 192.168.1.12方法3arr=(4 5 6) INDEX1=$(echo ${arr[0]}) INDEX2=$(echo ${arr[1]}) INDEX3=$(echo ${arr[2]})批量修改文件名示例# touch article_{1..3}.html # lsarticle_1.html article_2.html article_3.html目的：把 article 改为 bbs方法1for file in $(ls *html); do mv $file bbs_${file#*_} # mv $file $(echo $file |sed -r 's/.*(_.*)/bbs\1/') # mv $file $(echo $file |echo bbs_$(cut -d_ -f2)方法2 for file in $(find . -maxdepth 1 -name "*html"); do mv $file bbs_${file#*_}done方法3# rename article bbs *.html把一个文档前五行中包含字母的行删掉，同时删除6到10行包含的所有字母准备测试文件，文件名为2.txt 第1行1234567不包含字母 第2行56789BBBBBB 第3行67890CCCCCCCC 第4行78asdfDDDDDDDDD 第5行123456EEEEEEEE 第6行1234567ASDF 第7行56789ASDF 第8行67890ASDF 第9行78asdfADSF 第10行123456AAAA 第11行67890ASDF 第12行78asdfADSF 第13行123456AAAA脚本如下 #!/bin/bash ############################################################### 把一个文档前五行中包含字母的行删掉，同时删除6到10行包含的所有字母 ############################################################## sed -n '1,5'p 2.txt |sed '/[a-zA-Z]/'d sed -n '6,10'p 2.txt |sed s'/[a-zA-Z]//'g sed -n '11,$'p 2.txt #最终结果只是在屏幕上打印结果，如果想直接更改文件，可将输出结果写入临时文件中，再替换2.txt或者使用-i选项统计当前目录中以 .html 结尾的文件总大方法1# find . -name "*.html" -exec du -k {} \; |awk '{sum+=$1}END{print sum}' 方法2for size in $(ls -l *.html |awk '{print $5}'); do sum=$(($sum+$size)) done echo $sum 扫描主机端口状态#!/bin/bash HOST=$1 PORT="22 25 80 8080" for PORT in $PORT; do if echo &>/dev/null > /dev/tcp/$HOST/$PORT; then echo "$PORT open" else echo "$PORT close" fi done用 shell 打印示例语句中字母数小于 6 的单词#示例语句： #Bash also interprets a number of multi-character options. #!/bin/bash ############################################################## #shell打印示例语句中字母数小于6的单词 ############################################################## for s in Bash also interprets a number of multi-character options. do n=`echo $s|wc -c` if [ $n -lt 6 ] then echo $s fi done输入数字运行相应命令 #!/bin/bash ############################################################## #输入数字运行相应命令 ############################################################## echo "*cmd menu* 1-date 2-ls 3-who 4-pwd 0-exit " while : do #捕获用户键入值 read -p "please input number :" n n1=`echo $n|sed s'/[0-9]//'g` #空输入检测 if [ -z "$n" ] then continue fi #非数字输入检测 if [ -n "$n1" ] then exit 0 fi break done case $n in 1) date ;; 2) ls ;; 3) who ;; 4) pwd ;; 0) break ;; #输入数字非1-4的提示 *) echo "please input number is [1-4]" esacExpect 实现 SSH 免交互执行命令Expect是一个自动交互式应用程序的工具，如telnet，ftp，passwd等，需先安装expect软件包。方法1(EOF标准输出作为expect标准输入)#!/bin/bash USER=root PASS=123.com IP=192.168.1.120 expect << EOFset timeout 30spawn ssh $USER@$IP expect { "(yes/no)" {send "yes\r"; exp_continue} "password:" {send "$PASS\r"} } expect "$USER@*" {send "$1\r"} expect "$USER@*" {send "exit\r"} expect eof EOF方法2 #!/bin/bash USER=root PASS=123.com IP=192.168.1.120 expect -c " spawn ssh $USER@$IP expect { \"(yes/no)\" {send \"yes\r\"; exp_continue} \"password:\" {send \"$PASS\r\"; exp_continue} \"$USER@*\" {send \"df -h\r exit\r\"; exp_continue} }"方法3 (EOF标准输出作为expect标准输入)登录脚本 # cat login.exp #!/usr/bin/expect set ip [lindex $argv 0] set user [lindex $argv 1] set passwd [lindex $argv 2] set cmd [lindex $argv 3] if { $argc != 4 } { puts "Usage: expect login.exp ip user passwd" exit 1 } set timeout 30 spawn ssh $user@$ip expect { "(yes/no)" {send "yes\r"; exp_continue} "password:" {send "$passwd\r"} } expect "$user@*" {send "$cmd\r"} expect "$user@*" {send "exit\r"} expect eof执行命令脚本：写个循环可以批量操作多台服务器#!/bin/bash HOST_INFO=user_info.txt for ip in $(awk '{print $1}' $HOST_INFO) do user=$(awk -v I="$ip" 'I==$1{print $2}' $HOST_INFO) pass=$(awk -v I="$ip" 'I==$1{print $3}' $HOST_INFO) expect login.exp $ip $user $pass $1 doneLinux主机SSH连接信息# cat user_info.txt 192.168.1.120 root 123456创建10个用户，并分别设置密码，密码要求10位且包含大小写字母以及数字，最后需要把每个用户的密码存在指定文件中 。#!/bin/bash ############################################################## #创建10个用户，并分别设置密码，密码要求10位且包含大小写字母以及数字 #最后需要把每个用户的密码存在指定文件中 #前提条件：安装mkpasswd命令 ############################################################## #生成10个用户的序列（00-09） for u in `seq -w 0 09` do #创建用户 useradd user_$u #生成密码 p=`mkpasswd -s 0 -l 10` #从标准输入中读取密码进行修改（不安全） echo $p|passwd --stdin user_$u #常规修改密码 echo -e "$p\n$p"|passwd user_$u #将创建的用户及对应的密码记录到日志文件中 echo "user_$u $p" >> /tmp/userpassword done监控 httpd 的进程数，根据监控情况做相应处理 #!/bin/bash ############################################################################################################################### #需求： #1.每隔10s监控httpd的进程数，若进程数大于等于500，则自动重启Apache服务，并检测服务是否重启成功 #2.若未成功则需要再次启动，若重启5次依旧没有成功，则向管理员发送告警邮件，并退出检测 #3.如果启动成功，则等待1分钟后再次检测httpd进程数，若进程数正常，则恢复正常检测（10s一次），否则放弃重启并向管理员发送告警邮件，并退出检测 ############################################################################################################################### #计数器函数 check_service() { j=0 for i in `seq 1 5` do #重启Apache的命令 /usr/local/apache2/bin/apachectl restart 2> /var/log/httpderr.log #判断服务是否重启成功 if [ $? -eq 0 ] then break else j=$[$j+1] fi #判断服务是否已尝试重启5次 if [ $j -eq 5 ] then mail.py exit fi done } while : do n=`pgrep -l httpd|wc -l` #判断httpd服务进程数是否超过500 if [ $n -gt 500 ] then /usr/local/apache2/bin/apachectl restart if [ $? -ne 0 ] then check_service else sleep 60 n2=`pgrep -l httpd|wc -l` #判断重启后是否依旧超过500 if [ $n2 -gt 500 ] then mail.py exit fi fi fi #每隔10s检测一次 sleep 10 done批量修改服务器用户密码# cat old_pass.txt 192.168.18.217 root 123456 22 192.168.18.218 root 123456 22内容格式（IP User Password Port）SSH远程修改密码脚本：新密码随机生成 #!/bin/bash OLD_INFO=old_pass.txt NEW_INFO=new_pass.txt for IP in $(awk '/^[^#]/{print $1}' $OLD_INFO); do USER=$(awk -v I=$IP 'I==$1{print $2}' $OLD_INFO) PASS=$(awk -v I=$IP 'I==$1{print $3}' $OLD_INFO) PORT=$(awk -v I=$IP 'I==$1{print $4}' $OLD_INFO) NEW_PASS=$(mkpasswd -l 8) # 随机密码 echo "$IP $USER $NEW_PASS $PORT" >> $NEW_INFO expect -c " spawn ssh -p$PORT $USER@$IP set timeout 2 expect { \"(yes/no)\" {send \"yes\r\";exp_continue} \"password:\" {send \"$PASS\r\";exp_continue} \"$USER@*\" {send \"echo \'$NEW_PASS\' |passwd --stdin $USER\r exit\r\";exp_continue} }" done 生成新密码文件# cat new_pass.txt 192.168.18.217 root n8wX3mU% 22 192.168.18.218 root c87;ZnnL 22iptables 自动屏蔽访问网站频繁的IP场景：恶意访问,安全防范屏蔽每分钟访问超过200的IP方法1(根据访问日志，以Nginx为例) #!/bin/bash DATE=$(date +%d/%b/%Y:%H:%M) ABNORMAL_IP=$(tail -n5000 access.log |grep $DATE |awk '{a[$1]++}END{for(i in a)if(a[i]>100)print i}') #先tail防止文件过大，读取慢，数字可调整每分钟最大的访问量。awk不能直接过滤日志，因为包含特殊字符。 for IP in $ABNORMAL_IP; do if [ $(iptables -vnL |grep -c "$IP") -eq 0 ]; then iptables -I INPUT -s $IP -j DROP fi done 方法2（通过TCP建立的连接） #!/bin/bash ABNORMAL_IP=$(netstat -an |awk '$4~/:80$/ && $6~/ESTABLISHED/{gsub(/:[0-9]+/,"",$5);{a[$5]++}}END{for(i in a)if(a[i]>100)print i}') #gsub是将第五列（客户端IP）的冒号和端口去掉 for IP in $ABNORMAL_IP; do if [ $(iptables -vnL |grep -c "$IP") -eq 0 ]; then iptables -I INPUT -s $IP -j DROP fi done屏蔽每分钟SSH尝试登录超过10次的IP方法1（通过lastb获取登录状态） #!/bin/bash DATE=$(date +"%a %b %e %H:%M") #星期月天时分 %e单数字时显示7，而%d显示07 ABNORMAL_IP=$(lastb |grep "$DATE" |awk '{a[$3]++}END{for(i in a)if(a[i]>10)print i}') for IP in $ABNORMAL_IP; do if [ $(iptables -vnL |grep -c "$IP") -eq 0 ]; then iptables -I INPUT -s $IP -j DROP fi done方法2 （通过日志获取登录状态） #!/bin/bash DATE=$(date +"%b %d %H") ABNORMAL_IP="$(tail -n10000 /var/log/auth.log |grep "$DATE" |awk '/Failed/{a[$(NF-3)]++}END{for(i in a)if(a[i]>5)print i}')" for IP in $ABNORMAL_IP; do if [ $(iptables -vnL |grep -c "$IP") -eq 0 ]; then iptables -A INPUT -s $IP -j DROP echo "$(date +"%F %T") - iptables -A INPUT -s $IP -j DROP" >>~/ssh-login-limit.log fi done 根据web访问日志，封禁请求量异常的IP，如IP在半小时后恢复正常，则解除封禁 #!/bin/bash #################################################################################### #根据web访问日志，封禁请求量异常的IP，如IP在半小时后恢复正常，则解除封禁 #################################################################################### logfile=/data/log/access.log #显示一分钟前的小时和分钟 d1=`date -d "-1 minute" +%H%M` d2=`date +%M` ipt=/sbin/iptables ips=/tmp/ips.txt block() { #将一分钟前的日志全部过滤出来并提取IP以及统计访问次数 grep '$d1:' $logfile|awk '{print $1}'|sort -n|uniq -c|sort -n > $ips #利用for循环将次数超过100的IP依次遍历出来并予以封禁 for i in `awk '$1>100 {print $2}' $ips` do $ipt -I INPUT -p tcp --dport 80 -s $i -j REJECT echo "`date +%F-%T` $i" >> /tmp/badip.log done } unblock() { #将封禁后所产生的pkts数量小于10的IP依次遍历予以解封 for a in `$ipt -nvL INPUT --line-numbers |grep '0.0.0.0/0'|awk '$2<10 {print $1}'|sort -nr` do $ipt -D INPUT $a done $ipt -Z } #当时间在00分以及30分时执行解封函数 if [ $d2 -eq "00" ] || [ $d2 -eq "30" ] then #要先解再封，因为刚刚封禁时产生的pkts数量很少 unblock block else block fi判断用户输入的是否为IP地址方法1 #!/bin/bash function check_ip(){ IP=$1 VALID_CHECK=$(echo $IP|awk -F. '$1< =255&&$2<=255&&$3<=255&&$4<=255{print "yes"}') if echo $IP|grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$">/dev/null; then if [ $VALID_CHECK == "yes" ]; then echo "$IP available." else echo "$IP not available!" fi else echo "Format error!" fi } check_ip 192.168.1.1 check_ip 256.1.1.1方法2 #!/bin/bash function check_ip(){ IP=$1 if [[ $IP =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then FIELD1=$(echo $IP|cut -d. -f1) FIELD2=$(echo $IP|cut -d. -f2) FIELD3=$(echo $IP|cut -d. -f3) FIELD4=$(echo $IP|cut -d. -f4) if [ $FIELD1 -le 255 -a $FIELD2 -le 255 -a $FIELD3 -le 255 -a $FIELD4 -le 255 ]; then echo "$IP available." else echo "$IP not available!" fi else echo "Format error!" fi } check_ip 192.168.1.1 check_ip 256.1.1.1方法3 （增加版，加个死循环，如果IP可用就退出，不可用提示继续输入，并使用awk判断。） #!/bin/bash function check_ip(){ local IP=$1 VALID_CHECK=$(echo $IP|awk -F. '$1< =255&&$2<=255&&$3<=255&&$4<=255{print "yes"}') if echo $IP|grep -E "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" >/dev/null; then if [ $VALID_CHECK == "yes" ]; then return 0 else echo "$IP not available!" return 1 fi else echo "Format error! Please input again." return 1 fi } while true; do read -p "Please enter IP: " IP check_ip $IP [ $? -eq 0 ] && break || continue done
禁止访问PHP脚本可以通过Nginx服务器配置中的多种方式来实现。以下是其中的一些常见方法，您可以根据实际需求选择合适的方式：禁用PHP解析(在Nginx配置中，确保PHP脚本无法被解析，从而禁止PHP执行。)location ~ \.php$ { deny all; }限制访问路径(限制只允许访问特定路径下的PHP脚本，其他路径禁止。)location /public { # 允许访问 /public 下的 PHP 脚本 location ~ \.php$ { fastcgi_pass unix:/var/run/php-fpm.sock; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } } # 禁止访问其他路径的 PHP 脚本 location ~ \.php$ { deny all; }使用HTTP Basic认证(要求用户提供用户名和密码以访问PHP脚本。)location ~ \.php$ { auth_basic "Restricted"; # 使用 htpasswd 生成密码文件 auth_basic_user_file /etc/nginx/.htpasswd; # ... }IP地址访问控制(限制只有特定的IP地址可以访问PHP脚本)location ~ \.php$ { allow 192.168.1.1; # 允许的 IP 地址 deny all; # 禁止其他 IP 地址 # ... }Referer 检查(检查HTTP Referer头，只允许特定来源的请求。)location ~ \.php$ { valid_referers none blocked example.com; # 允许的 Referer if ($invalid_referer) { return 403; # 拒绝无效的 Referer } # ... }User-Agent检查(检查User-Agent头，只允许特定User-Agent访问。)location ~ \.php$ { if ($http_user_agent ~* (bad-agent|another-bad-agent)) { return 403; # 拒绝不良 User-Agent } # ... }URL匹配(通过正则表达式匹配URL，只允许特定URL访问PHP。)location ~ \.php$ { if ($request_uri ~* ^/allowed-path/) { # 允许的 URL 路径 # ... } return 403; # 其他路径禁止 }文件类型匹配(只允许特定文件类型的PHP文件执行。)location ~ \.php$ { if ($request_filename ~* \.php$) { # 允许的文件类型 # ... } return 403; # 禁止其他文件类型 }设置文件权限(通过文件系统的权限设置，限制PHP脚本的访问。)location ~ \.php$ { # 设置文件的访问权限为 600 或更高 # ... }利用Nginx的map模块(使用Nginx的map模块来根据条件禁止PHP访问。)map $remote_addr $php_disabled { default 0; 192.168.1.1 0; # 允许的 IP 地址 ~^192\.168\.2\. 1; # 允许的 IP 地址范围 } location ~ \.php$ { if ($php_disabled) { return 403; # 禁止访问 } # ... }利用Nginx的geo模块(使用Nginx的geo模块根据地理位置禁止PHP访问。)geo $allowed_country { default 0; US 1; # 允许的国家或地区 CA 1; } location ~ \.php$ { if ($allowed_country = 0) { return 403; # 禁止访问 } # ... }禁止特定HTTP方法 (限制只有特定的HTTP方法可以访问PHP脚本)location ~ \.php$ { if ($request_method !~ ^(GET|POST)$) { return 403; # 禁止其他方法 } # ... }限制访问时间(限制只有在特定时间段内可以访问PHP脚本。)location ~ \.php$ { if ($time_iso8601 !~ "T(08|09|10|11|12|13):") { return 403; # 限制访问时段 } # ... }HTTP访问速率限制(限制访问速率以减轻服务器负载。)location ~ \.php$ { limit_req zone=mylimit burst=5 nodelay; limit_req_status 403; # ... }使用Nginx内置变量(使用Nginx的内置变量结合条件语句来根据特定条件禁止PHP访问。)location ~ \.php$ { if ($http_cookie ~* "adminuser=") { return 403; # 如果包含特定 Cookie } # ... }请注意，上述方法可以单独使用或组合使用，具体的选择取决于您的安全需求和服务器配置。同时，确保在配置中使用适当的测试和调试方法，以确保您的 Nginx 服务器按照预期运行。
根据url页面id 区间代理location ~ ^/(game|app|soft|azyx|azrj)/([0-9]+)\.html$ { set $id $2; set_by_lua $isProxy ' local id = tonumber(ngx.var.id) if id >= 270000 then return "1" else return "0" end '; if ($isProxy = 1) { proxy_pass http://proxy.t4t5.cn; } }根据静态文件页面是否存在代理 location ~ ^/appheji/([a-zA-Z0-9]+)/$ { if (!-e $request_filename) { proxy_pass http://proxy.t4t5.cn; } }
内网穿透内网穿透，也即 NAT 穿透，进行 NAT 穿透是为了使具有某一个特定源 IP 地址和源端口号的数据包不被 NAT 设备屏蔽而正确路由到内网主机。下面就相互通信的主机在网络中与 NAT 设备的相对位置介绍内网穿透方法。其实说白了就是让你的局域网/内网可以被公共场合访问。内网穿透主要功能在内网环境下的服务器和应用只有在同一个内网环境下才能访问连接，内网穿透可以实现外网对内网的访问连接。如需要出差在外想要访问在公司内的电脑远程桌面连接，使用内网穿透即可以实现。网站应用需要外网访问且考虑收录时，使用80网站应用类型的映射方式将内网网站映射到外网直接访问。SVN等443端口https协议应用的使用场景，使用443https应用的应用类型的映射方式将其映射到外网直接访问。办公OA等不考虑收录的WEB网站应用，使用非80网站的应用类型的映射方式，映射后外网访问带外网端口。固定端口和UDP等在访问端也适合安装使用客户端的场景，使用全端口映射方式实现外网的访问。远程桌面、数据库、等其他普通TCP应用外网直接访问场景需求，使用其他非网站映射方式。FRP 高性能反向代理应用frp 是一个高性能的反向代理应用，支持 tcp, udp, http, https 协议。frp 的作用利用处于防火墙后的机器，对外网环境提供 http 或 https 服务。对于 http, https 服务支持基于域名的虚拟主机，支持自定义域名绑定，使多个域名可以共用一个80端口。利用处于防火墙后的机器，对外网环境提供 tcp 和 udp 服务，例如在家里通过 ssh 访问处于公司局部环境的主机。开发状态frp 仍然处于前期开发阶段，未经充分测试与验证，官方不推荐用于生产环境 。当然，如果你可容忍其部分的不稳定，可以玩玩。FRP 安装配置简单使用原理图准备测试环境一台公网linux服务器(39.153.56.3)一台内网win10服务器根据自己电脑配置下载对应的FRPfrpgithub下载地址服务端安装启动在公网linux服务器搭建FRP服务端 tar -zxvf frp_0.48.0_linux_amd64.tar.gz # 解压 cd frp_0.48.0_linux_amd64/ rm -rf frpc* # 删除客户端软件相关的 vim frps.ini # 修改配置文件frps.ini[common] # 与client通讯的端口 bind_port = 10001 # 验证的token token = tY7rxYs75XddMbAhwdd # dashboard的端口 dashboard_port = 6600 # 用户名 dashboard_user = admin # 密码 dashboard_pwd = 123456 # 日志文件路径 log_file = ./frps.log # 日志级别 log_level = info指定配置文件启动./frps -c frps.ini客户端安装启动内网win10服务器搭建FRP客户端解压、删除多余的服务端程序(文件名称为frps开头的)配置文件配置一个mysql映射、一个web服务器映射、一个远程桌面控制[common] ;公网frp服务器 server_addr = 39.153.56.3 server_port = 10001 token = tY7rxYs75XddMbAhwdd log_file = ./frpc.log [mysql] type = tcp local_ip = 127.0.0.1 local_port = 3306 remote_port = 3306 [webserver] type = tcp local_ip = 127.0.0.1 local_port = 80 remote_port = 80 # 打开设置--->系统---->远程桌面---->启用远程桌面 [remote] type = tcp local_ip = 127.0.0.1 # 打开win10设置--->系统---->远程桌面---->高级设置--->当前远程桌面端口 local_port = 3389 remote_port = 3389运行frp客户端frpc.exe -c frpc.ini测试注意 你的公网服务器是否放开了对应端口以及是否有安全组之类的设置。注意 你的内网/局域网win10电脑启动了mysql数据库，开启了nginx程序的80端口web服务。注意 远程桌面控制需要你win10电脑开启远程链接限制 打开设置--->系统---->远程桌面---->启用远程桌面使用公网服务器 ip + port 即可访问到内网/局域网的数据库/http服务以及使用远程桌面。在win10上以守护进程方式启动frp客户端NSSM 是一个服务封装程序，它可以将普通 exe 程序 或 Java程序 或 Nodejs 项目封装成服务，像 windows 服务一样运行。支持普通 exe 程序（控制台程序或者带界面的 Windows 程序都可以）安装简单，修改方便可以重定向输出（并且支持 Rotation）可以自动守护封装了的服务，程序挂掉了后可以自动重启可以自定义环境变量下载 NSSM 官网下载链接解压，把 nssm.exe文件放到frpc的根目录在本文件夹运行 nssm.exe install
查看linux系统内核版本uname -a # Linux iZ8vbcsg5lal7crq11jflxziz23yZ 4.18.0-193.14.2.el8_2.x86_64 #1 SMP Sun Jul 26 03:54:29 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux uname -r # 4.18.0-193.14.2.el8_2.x86_64 cat /proc/version # Linux version 4.18.0-193.14.2.el8_2.x86_64 (mockbuild@kbuilder.bsys.centos.org) (gcc version 8.3.1 20191121 (Red Hat 8.3.1-5) (GCC)) #1 SMP Sun Jul 26 03:54:29 UTC 2020查看linux系统版本lsb_release -a # LSB Version: :core-4.1-amd64:core-4.1-noarch # Distributor ID: CentOS # Description: CentOS Linux release 8.2.2004 (Core) # Release: 8.2.2004 # Codename: Core cat /etc/redhat-release # CentOS Linux release 8.2.2004 (Core) 查看linux系统的架构是amd还是armarch # x86_64 返回x86_64就是amd的查看linux系统是32还是64getconf LONG_BIT # 64 查看系统是实体机还是虚拟机lspci | grep -i "VMware" # 是否有返回值，实体机无返回值查看网卡是千兆还是万兆先ifconfig查看使用的网卡再使用ethtool(ethtool 网络接口名)网卡查看，显示的Speed就是，单位是Mbit查看系统cpucat /proc/cpuinfo processor : 0 # 逻辑处理核编号 vendor_id : GenuineIntel # CPU制造商 cpu family : 6 # CPU产品系列代号 model : 79 # CPU属于其系列中的哪一代的代号 model name : Intel(R) Xeon(R) CPU E5-2682 v4 @ 2.50GHz # CPU属于的名字及其编号、标称主频 stepping : 1 # CPU属于制作跟新版本 microcode : 0x1 # CPU微代码 cpu MHz : 2499.996 # CPU的实际试用主频 cache size : 40960 KB # CPU二级缓存大小 physical id : 0 # 物理CPU的标号 siblings : 1 # 单个CPU逻辑物理核数 core id : 0 # 当前物理核在其所处CPU中的编号 cpu cores : 1 # 该逻辑核所处CPU的物理核数 apicid : 0 # 区分不同逻辑核的编号 initial apicid : 0 # 初始的apicid数值 fpu : yes # 是否具有浮点运算单元 fpu_exception : yes # 是否支持浮点运算异常 cpuid level : 20 # 执行cpuid指令前，eax寄存器中的值 wp : yes # 当前cpu是否在内核态支持对用户空间的写保护 flags : fpu vme de pse tsc msr ... # 当前cpu支持的功能 bugs : cpu_meltdown spectre_v1 ... bogomips : 4999.99 # 粗略测算的cpu速度 clflush size : 64 # 每次刷新缓存的大小单位 cache_alignment : 64 # 缓存地址对齐单位 address sizes : 46 bits physical, 48 bits virtual # 可访问地址空间位数 power management: # 对能源管理的支持 查看系统内存free -m cat /proc/meminfo # 详细情况查看系统磁盘df -Th # Filesystem Type Size Used Avail Use% Mounted on # devtmpfs devtmpfs 395M 0 395M 0% /dev # tmpfs tmpfs 410M 16K 410M 1% /dev/shm # tmpfs tmpfs 410M 496K 409M 1% /run # tmpfs tmpfs 410M 0 410M 0% /sys/fs/cgroup # /dev/vda1 xfs 40G 28G 13G 70% / # tmpfs tmpfs 82M 0 82M 0% /run/user/0 查看磁盘是ssd还是satacat /sys/block/*/queue/rotational # 返回是0是ssd盘，返回是1则不是。*里用具体的盘符代替，比如sdc等查看当前登录用户，并剔出指定用户, 查看用户登录历史w # 查看登录的用户 # 10:04:52 up 18 days, 21:35, 1 user, load average: 0.00, 0.01, 0.00 # USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT # root pts/2 221.225.22.34 09:36 0.00s 0.25s 0.00s w # 踢用户下线 pkill -kill -t pts/2 #(终端) pkill -kill -u root #(用户) last # 查看用户登录历史
GeoIP介绍GeoIP库可以根据IP地址(支持IPv4 和 IPv6), 定位该IP所在的 洲、经纬度、国家、省市、ASN 等信息。GeoIP目前已经升级到GeoIP2，GeoIP2有两个版本，一个免费版(GeoLite2)，一个收费版本(GeoIP2, 200$起步)。收费版本的准确率稍高一些，更新频率为每周二更新一次, 免费版是每月第一个周二更新一次。MaxMindMaxMind提供了GeoLite2下载GeoLite2数据库登录 / 注册我们需要使用的二进制库文件格式,如下图所示，下载即可。libmaxminddblibmaxminddb(api库) 用于处理 MaxMind 数据库文件的库, libmaxminddb(api库)下载地址安装 # centos7 yum -y install wget gcc gcc-c++ make wget --no-check-certificate https://github.com/maxmind/libmaxminddb/releases/download/1.7.1/libmaxminddb-1.7.1.tar.gz cd libmaxminddb-1.7.1 ./configure && make && make install echo /usr/local/lib >> /etc/ld.so.conf.d/local.conf ldconfig unlink /usr/sbin/mmdblookup && ln -s /usr/local/bin/mmdblookup /usr/sbin/mmdblookup测试libmaxminddb库地理位置api和数据库功能 # 解压GeoLite2数据库 tar -zxvf GeoLite2-ASN_20230411.tar.gz # 用于产看IP地址的拥有者(7M左右),需要注意的是City和Country库中不含ASN信息 tar -zxvf GeoLite2-City_20230411.tar.gz # 精确到城市(大小70M左右) tar -zxvf GeoLite2-Country_20230411.tar.gz # 精确到国家(4M左右) # 格式 mmdblookup --file GeoLite2数据库物理地址路径 --ip 你需要测试的IP地址 mmdblookup --file /etc/nginx/geoip/resource/GeoLite2-ASN_20230411/GeoLite2-ASN.mmdb --ip 113.215.102.222 mmdblookup --file /etc/nginx/geoip/resource/GeoLite2-City_20230411/GeoLite2-City.mmdb --ip 113.215.102.222 mmdblookup --file /etc/nginx/geoip/resource/GeoLite2-Country_20230411/GeoLite2-Country.mmdb --ip 113.215.102.222返回[root@controller resource]# mmdblookup --file /etc/nginx/geoip/resource/GeoLite2-City_20230411/GeoLite2-City.mmdb --ip 114.217.102.222 { "city": { "geoname_id": 1886760 <uint32> "names": { "de": "Suzhou" <utf8_string> "en": "Suzhou" <utf8_string> "es": "Suzhou" <utf8_string> "fr": "Suzhou" <utf8_string> "ja": "蘇州市" <utf8_string> "pt-BR": "Suzhou" <utf8_string> "ru": "Сучжоу" <utf8_string> "zh-CN": "苏州市" <utf8_string> } } "continent": { "code": "AS" <utf8_string> "geoname_id": 6255147 <uint32> "names": { "de": "Asien" <utf8_string> "en": "Asia" <utf8_string> "es": "Asia" <utf8_string> "fr": "Asie" <utf8_string> "ja": "アジア" <utf8_string> "pt-BR": "Ásia" <utf8_string> "ru": "Азия" <utf8_string> "zh-CN": "亚洲" <utf8_string> } } "country": { "geoname_id": 1814991 <uint32> "iso_code": "CN" <utf8_string> "names": { "de": "China" <utf8_string> "en": "China" <utf8_string> "es": "China" <utf8_string> "fr": "Chine" <utf8_string> "ja": "中国" <utf8_string> "pt-BR": "China" <utf8_string> "ru": "Китай" <utf8_string> "zh-CN": "中国" <utf8_string> } } "location": { "accuracy_radius": 100 <uint16> "latitude": 31.309300 <double> "longitude": 120.602000 <double> "time_zone": "Asia/Shanghai" <utf8_string> } "registered_country": { "geoname_id": 1814991 <uint32> "iso_code": "CN" <utf8_string> "names": { "de": "China" <utf8_string> "en": "China" <utf8_string> "es": "China" <utf8_string> "fr": "Chine" <utf8_string> "ja": "中国" <utf8_string> "pt-BR": "China" <utf8_string> "ru": "Китай" <utf8_string> "zh-CN": "中国" <utf8_string> } } "subdivisions": [ { "geoname_id": 1806260 <uint32> "iso_code": "JS" <utf8_string> "names": { "en": "Jiangsu" <utf8_string> "fr": "Province de Jiangsu" <utf8_string> "zh-CN": "江苏省" <utf8_string> } } ] }Nginx安装GeoIp2模块下载ngx_http_geoip2_module模块wget https://github.com/leev/ngx_http_geoip2_module/archive/refs/tags/3.4.tar.gz tar -zxvf 3.4.tar.gz cd ngx_http_geoip2_module-3.4/ # pwd查看模块路径，等编译nginx的时候需要加上 pwd /www/software/ngx_http_geoip2_module-3.4如果你nginx是yum或者apt安装的，查看nginx版本情况以及扩展模块情况，去官方nginx下载 下载对应版本的nginx源码如果你nginx是源码编译安装的，直接进入源码目录即可nginx -V # 输出信息 nginx version: nginx/1.22.1 built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC) built with OpenSSL 1.0.2k-fips 26 Jan 2017 TLS SNI support enabled configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' --with-http_realip_module # nginx是yum或者apt安装的,我这里是v1.22.1 wget https://nginx.org/download/nginx-1.22.1.tar.gz tar -zxvf nginx-1.22.1.tar.gz # nginx是源码编译安装直接进入源码目录即可 cd nginx-1.22.1/ # 编译时加上 ./configure --add-dynamic-module=/path/to/ngx_http_geoip2_module，/path/to/ngx_http_geoip2_module 是你ngx_http_geoip2_module模块目录路径,我的ngx_http_geoip2_module解压到了/www/software/ngx_http_geoip2_module-3.4,所以就是 --add-dynamic-module=/www/software/ngx_http_geoip2_module-3.4 ./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' --with-http_realip_module --add-dynamic-module=/www/software/ngx_http_geoip2_module-3.4 make && make install # 再此查看扩展模块情况已经加载了ngx_http_geoip2_module nginx -V # 输出信息 nginx version: nginx/1.22.1 built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC) built with OpenSSL 1.0.2k-fips 26 Jan 2017 TLS SNI support enabled configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' --with-http_realip_module --add-dynamic-module=/www/software/ngx_http_geoip2_module-3.4 nginx使用GeoIp2模块user www www; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid; load_module modules/ngx_http_geoip2_module.so; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; #真实ip获取 set_real_ip_from 192.168.56.2; set_real_ip_from 192.168.56.3; set_real_ip_from 192.168.56.5; real_ip_header X-Forwarded-For; real_ip_recursive on; map $http_x_forwarded_for $realip { # ~ 大小写敏感,~* 大小写不敏感 ~^(\d+\.\d+\.\d+\.\d+) $realip; default $remote_addr; } # geoIp2 config geoip2 /etc/nginx/geoip/database/GeoLite2-Country.mmdb { #国家编码 $geoip2_country_code source=$realip country iso_code; #国家英文名 $geoip2_country_name_en source=$realip country names en; #国家中文名 $geoip2_country_name_cn source=$realip country names zh-CN; } geoip2 /etc/nginx/geoip/database/GeoLite2-City.mmdb { # 城市编码 $geoip2_city_code source=$realip city geoname_id; # 城市英文名，大多是拼音，有重复情况 $geoip2_city_name_en source=$realip city names en; # 城市中文名，部分城市没有中文名 $geoip2_city_name_cn source=$realip city names zh-CN; # 经度，longitude $geoip2_longitude source=$realip location longitude; # 维度，latitude $geoip2_latitude source=$realip location latitude; } include /etc/nginx/conf.d/*.conf; } server { listen 80; server_name localhost; access_log /var/log/nginx/host.access.log main; # 输出IP信息 location ^~ /proxy1/ { charset utf-8; default_type application/json; set $resp_json '{"ip": $realip,"countryCode": $geoip2_country_code,"countryNameEn": $geoip2_country_name_en,"countryNameCn": $geoip2_country_name_cn,"cityCode": $geoip2_city_code,"cityNameEn": $geoip2_city_name_en,"cityNameCn": $geoip2_city_name_cn,"longitude": $geoip2_longitude,"latitude": $geoip2_latitude}'; return 200 $resp_json; } location / { root /usr/share/nginx/html; index index.php index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ \.php$ { root /usr/share/nginx/html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} }测试浏览器 http://192.168.56.2/proxy1/{"ip": 180.103.200.225,"countryCode": CN,"countryNameEn": China,"countryNameCn": 中国,"cityCode": ,"cityNameEn": ,"cityNameCn": ,"longitude": 113.72200,"latitude": 34.77320}GeoIP数据的自动更新Updating GeoIP and GeoLite Databases 官网下载 geoipupdate更新工具 | v4.11.1wget https://github.com/maxmind/geoipupdate/releases/download/v4.11.1/geoipupdate_4.11.1_linux_386.tar.gz tar -zxvf geoipupdate_4.11.1_linux_386.tar.gz cd geoipupdate_4.11.1_linux_386/ cp ./geoipupdate /usr/sbin/ cd /etc/nginx/geoip/ # 创建geoipupdate配置文件 vim GeoIP.confGeoIP.conf# GeoIP.conf file for `geoipupdate` program, for versions >= 3.1.1. # Used to update GeoIP databases from https://www.maxmind.com. # For more information about this config file, visit the docs at # https://dev.maxmind.com/geoip/updating-databases?lang=en. # `AccountID` is from your MaxMind account. AccountID 你的UID # Replace YOUR_LICENSE_KEY_HERE with an active license key associated # with your MaxMind account. LicenseKey 你的key # `EditionIDs` is from your MaxMind account. EditionIDs GeoLite2-ASN GeoLite2-City GeoLite2-Country### 查看版本 geoipupdate -v # 输出信息 这是默认GeoIP.conf 配置目录和数据库目录/usr/local/share/GeoIP geoipupdate version 4.11.1 Using config file /usr/local/etc/GeoIP.conf Using database directory /usr/local/share/GeoIP error retrieving updates: error creating database writer for GeoLite2-ASN: database directory is not available: stat /usr/local/share/GeoIP: no such file or directory ### 查看帮助 geoipupdate -h # 输出信息 可以用-f 指定自定义配置文件路径，使用-d 指定自定义数据库目录 -f, --config-file string Configuration file (default "/usr/local/etc/GeoIP.conf") -d, --database-directory string Store databases in this directory (uses config if not specified) -h, --help Display help and exit --stack-trace Show a stack trace along with any error message. -v, --verbose Use verbose output -V, --version Display the version and exit ### 更新命令 geoipupdate -f /etc/nginx/geoip/GeoIP.conf -d /etc/nginx/geoip/datebase/ # crontab保持更新 crontab -e ### 指定自定义配置文件路径和指定自定义数据库目录 2 8 * * 6,4 /usr/local/bin/geoipupdate -f /etc/nginx/geoip/GeoIP.conf -d /etc/nginx/geoip/datebase/ ### 默认配置文件/usr/local/etc/GeoIP.conf 和 默认数据库目录 /usr/local/share/GeoIP 2 8 * * 6,4 /usr/local/bin/geoipupdate
Nginx配置⽂件Nginx主配置⽂件 /etc/nginx/nginx.conf 是⼀个纯⽂本类型的⽂件，整个配置⽂件是以区块的形式组织的。⼀般，每个区块以⼀对⼤括号 {} 来表示开始与结束。Main位于nginx.conf配置⽂件的最⾼层Main层下可以有Event、HTTP层HTTP层下⾯有允许有多个Server层, ⽤于对不同的⽹站做不同的配置Server层也允许有多个Location, ⽤于对不同的路径进⾏不同模块的配置nginx默认配置语法user nginx; # 设置nginx服务的系统使⽤⽤户 worker_processes auto; # ⼯作进程,配置和CPU个数保持⼀致 error_log /var/log/nginx/error.log notice; # 错误⽇志,后⾯接⼊的是存放文件路径 pid /var/run/nginx.pid; # Nginx服务启动时的pid,,后⾯接⼊的是存放文件路径 events { worker_connections 1024; } # ⾮虚拟主机的配置或公共配置定义在http{}段内, server{}段外 http { # 必须使⽤虚拟机配置站点, 每个虚拟机使⽤⼀个server{}段 server { listen 80; # 监听端⼝, 默认80 server_name localhost; # 提供服务的域名或主机名 #控制⽹站访问路径 location / { root /usr/share/nginx/html; # 存放⽹站路径 index index.html index.htm; # 默认访问⾸⻚⽂件 } # 指定错误代码, 统⼀定义错误⻚⾯, 错误代码重定向到新的Locaiton error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } # 第⼆个虚拟主机配置 server { } } Nginx⽇志配置规范配置语法包括access.logerror.logNginx默认日志格式设置log_format main '$remote_addr - $remote_user [$time_local] "$request"' '$status $body_bytes_sent "$http_referer"' '"$http_user_agent" "$http_x_forwarded_for"'; Nginx⽇志变量参数说明$remote_addr客户端地址$remote_user客户端用户名称$time_local访问时间和时区$request请求的URI和HTTP协议$http_host请求地址，即浏览器中你输入的地址（IP或域名）$statusHTTP请求状态$upstream_statusupstream状态$body_bytes_sent发送给客户端文件内容大小$http_refererurl跳转来源$http_user_agent用户终端浏览器等信息$ssl_protocolSSL协议版本$ssl_cipher交换数据中的算法$upstream_addr后台upstream的地址，即真正提供服务的主机地址$request_time整个请求的总时间$upstream_response_time请求过程中，upstream响应时间Nginx状态监控开启Nginx监控模块 (--with-http_stub_status_module)具体配置如下location /mystatus { stub_status on; access_log off; }状态监控结果Active connections: 2 server accepts handled requests 2 2 1 Reading: 0 Writing: 1 Waiting: 1 状态监控结果详细解读Active connections: 2 当前nginx正处理的活动连接数server accepts handled requests 3 3 10 总共处理了3次连接，成功创建了3次连接，共请求了10次。总连接数-成功连接数为失败连接数Reading: o Writing: 1 Waiting: 1reading为nginx读取到客户端的header信息数Writing为nginx返回给客户端的Header信息数,Waiting开启 keep-alive的情况下，这个值等于active-(reading+writing)，意思指nginx已经处理完正在等候下一次请求的驻留连接Nginx下载站点autoindex常⽤参数autoindex_exact_size off;默认为on， 显示出⽂件的确切⼤⼩，单位是bytes。修改为off，显示出⽂件的⼤概⼤⼩，单位是kB或者MB或者GB。autoindex_localtime on;默认为off，显示的⽂件时间为GMT时间。修改为on， 显示的⽂件时间为⽂件的服务器时间。charset utf-8,gbk;默认中⽂⽬录乱码，添加上解决乱码配置⽬录浏览功能location /www { #root /data; alias /data/www/; # 指定目录 autoindex on; # 列出整个目录列表 autoindex_localtime on; # 显示的文件时间为文件的服务器时间 charset utf-8,gbk; # 解决中文乱码 autoindex_exact_size off; # 以kB或者MB或者GB单位显示出文件的大小，而不是字节 }Nginx访问限制nginx 文档地址ngx_http_limit_req_module 模块文档地址配置 http { # http段配置请求限制, rate限制速率，限制⼀秒钟最多⼀个IP请求 limit_req_zone $binary_remote_addr zone=req_zone:10m rate=1r/s; server {} }key 定义用于限制请求的变量，在这个示例中使用的是NGINX的自带变量 $binary_remote_addr(客户端的ip地址)zone 定义用于存储前面定义的key变量,如zone=mylimit:10m 就是一个名为mylimit的大小为10m的共享内存空间rate 允放相同标识的客户端的访问频次， 在这个例子中：就是同一个ip地址在每秒内只能访问1次使用server { location / { # limit_req zone=req_zone; # limit_req zone=perip burst=2; limit_req zone=perip burst=2 nodelay; } } burst 如上例子burst=2，允许2个突发，有大量请求时，超过频次限制的请求，会允许2个访问，注意：burst指定的请求数量，不会马上进行处理，而是按照rate指定的值，以固定的速率进行处理。nodelay 只是对放到burst队列中的请求立即处理，但处理完成后队列并不立即清空，队列清空的速度仍然按原来的速度每秒一个清空，所以当再有请求过来时，并不会马上又有两个burst请求被处理。安装压力测试,以每秒处理1个请求的速率做限制 limit_req_zone $binary_remote_addr zone=req_zone:10m rate=1r/s; # centos sudo yum install -y httpd-tools # ubuntu sudo apt-get install -y apache2-utils测试1 location / { root /usr/share/nginx/html; index index.html index.htm; limit_req zone=req_zone; }测试1重启nginx后执行ab vagrant@swarm3:/etc/nginx$ ab -n 10 -c 10 http://192.168.56.3/ Concurrency Level: 10 Time taken for tests: 0.002 seconds Complete requests: 10 Failed requests: 9 (Connect: 0, Receive: 0, Length: 9, Exceptions: 0)只有一个成功，有9个请求失败，时长是0.002 seconds，同一个ip地址在每秒内只能访问1次测试2 location / { root /usr/share/nginx/html; index index.html index.htm; limit_req zone=req_zone burst=2; }测试2重启nginx后执行ab vagrant@swarm3:/etc/nginx$ ab -n 10 -c 10 http://192.168.56.3/ Concurrency Level: 10 Time taken for tests: 2.001 seconds Complete requests: 10 Failed requests: 7 (Connect: 0, Receive: 0, Length: 7, Exceptions: 0)有7个请求失败，注意时间变成了2.001 seconds，burst=2允许2个突发, 有大量请求时，超过频次限制的请求，会允许2个访问，burst指定的请求数量， 不会马上进行处理， 而是按照rate指定的值，以固定的速率进行处理。测试3 location / { root /usr/share/nginx/html; index index.html index.htm; limit_req zone=req_zone burst=2 nodelay; }测试3重启nginx后执行ab vagrant@swarm3:/etc/nginx$ ab -n 10 -c 10 http://192.168.56.3/ Concurrency Level: 10 Time taken for tests: 0.002 seconds Complete requests: 10 Failed requests: 7 (Connect: 0, Receive: 0, Length: 7, Exceptions: 0)有7个请求失败，注意时间:这次是0.002 seconds，burst的队列虽然可以处理用户的需求，但需要用户按照处理时间等待， 对用户不够友好， nodelay参数允许请求在排队的时候就立即被处理， 这里有一点要注意：因为nodelay允许立即处理，也就是有并发请求时，事实上已经超过了rate设置的处理速率了， 所以要根据机器的实际情况设置这个值Nginx连接限制Nginx连接限制类似于Nginx请求限制，这里不再过多介绍，需要了解使用请移步ngx_http_limit_req_module 连接限制模块文档连接限制没有请求限制有效?多个请求可以建⽴在⼀次的TCP连接之上, 那么我们对请求的精度限制，当然⽐对⼀个连接的限制会更加的有效。因为同⼀时刻只允许⼀个连接请求进⼊，但是同⼀时刻多个请求可以通过⼀个连接进⼊，所以请求限制才是⽐较优的解决⽅案。Nginx访问控制文档地址基于IP的访问控制 ngx_http_access_module 模块基于⽤户登陆认证 ngx_http_auth_basic_module 模块基于IP的访问控制配置拒绝某⼀个IP, 其他全部允许location / { deny 192.168.56.1; allow all; }只允许某⼀个⽹段访问,其它全部拒绝location / { allow 192.168.56.0/24; deny all; }缺陷当真实客户端直接访问目标服务器被deny拒绝之后，真实客户端通过多层代理服务器访问目标服务器仍然是可以访问的。解决方法采用别的http头信息访问控制，如HTTP_X_FORWARDED_FOR。多层代理服务器以及目标服务开启http_x_forwarded_for，这个局限性很大，因为代理服务器可能不遵循开启http_x_forwarded_for，这是一个协议要求的，并不是所有的cdn和代理厂商它会按照要求来做，甚至x_forwarded_for存在被修改的可能，因为只是一个头信息，所以最终还是不真实。http_x_forwarded_for 也是Nginx的http头变量的一个常用的变量，它和remote_addr是有区别的。不同的是，x_forwarded_for是http协议中规定头中要携带的，所以在客户端访问中间件，再访问服务端的时候，那么服务端通过Nginx会记录真实IP和中间件的IP。格式为http_x_forwarded_for = 客户端ip,第一台代理ip,第二台代理ip,第N台代理ip，所以http_x_forwarded_for是由一连串以逗号分隔的ip组成的。192.168.56.1(客户端) -> 192.168.56.2(代理机) -> 192.168.56.3(代理机) -> 192.168.56.5(目标机器) # 192.168.56.2 location ^~ /proxy1/ { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_pass http://192.168.56.3:80; proxy_redirect off; } # 192.168.56.3 location ^~ /proxy1/ { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_pass http://192.168.56.5:80; proxy_redirect off; } # 192.168.56.5 location / { root /usr/share/nginx/html; index index.php index.html index.htm; } location ~ \.php$ { root /usr/share/nginx/html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }结合geo模块 想了解的话可以移步到 使用Nginx+GeoIP获取IP信息通过HTTP自定义变量传递基于IP的访问控制(待完善)Nginx虚拟主机
Nginx基本简述Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件（IMAP/POP3）代理服务器(代理中间件),是一个开源且高性能、可靠的HTTP中间件、代理服务。Nginx应⽤场景静态处理反向代理负载均衡资源缓存安全防护访问限制访问认证Nginx优秀特性采用I/O多路复用epoll模型IO 多路复用的场景是我们要设计一个高性能的网络服务器，这个网络服务器它可以供多个客户端进行连接并且可以处理这些客户端传上来的请求。当然对于一般来说，应对并发可以写一个多线程的程序，每个传上来的请求都是一个线程。但是多线程的方式存在一个很大的弊端，就是它需要cpu上下文切换，我们知道cpu上下文切换需要处理一些操作句柄，这些过程是非常繁琐的，在上下文切换开销在线程不多、切换不频繁的应用场景下问题不大，但是尤其是客户端的连接非常多的时候，这个cpu上下文切换带来的代价非常的高，因而多线程并不是最好的解决方案。多线程的方案既然被排除了，那我们把目光转回到单线程，如何用单线程的方案处理大量的客户端的并发连接呢？首先我们先抛出一个疑问，多个客户端同时连接网络服务器，而单线程一次只能处理一个客户端连接，其他连接是否会被抛弃呢？当然肯定是不会被抛弃的，原因是接受处理客户端的连接的并不是cpu,因为我们有专门专注处理IO的DNA控制器。I/O多路复用以及关于DMA介绍 请移步 《线程/协程/异步的编程模型解读》 了解[post cid="3023" /]IO多路复⽤的实现⽅式有 select、poll、Epool模型 请移步 《什么是select/poll/epoll模型？》 了解[post cid="3044" /]轻量级功能模块化代码模块化CPU亲和(affinity)将CPU核⼼和Nginx⼯作进程绑定⽅式，把每个worker进程固定在⼀个cpu上执⾏，减少切换cpu的 cache miss ，获得更好的性能。sendfile传统⽂件传输, 在实现上其实是⽐较复杂的, 其具体流程细节如下：调⽤read函数，⽂件数据被复制到内核缓冲read函数返回，⽂件数据从内核缓冲区复制到⽤户缓冲区write函数调⽤，将⽂件数据从⽤户缓冲区复制到内核与socket相关的缓冲区。数据从socket缓冲区复制到相关协议引擎。传统⽂件传输数据实际上是经过了四次复制操作:硬盘—>内核buf—>⽤户buf—>socket缓冲区(内核)—>协议引擎也就是说传统的⽂件传输需要经过多次上下⽂的切换才能完成拷⻉或读取, 效率不⾼。sendfile⽂件传输是在内核中操作完成的, 函数直接在两个⽂件描述符之间传递数据, 从⽽避免了内核缓冲区数据和⽤户缓冲区数据之间的拷⻉, 操作效率很⾼, 被称之为零拷⻉。系统调⽤sendfile函数通过 DMA 把硬盘数据拷⻉到 kernel buffer，数据被 kernel 直接拷⻉到另外⼀个与 socket 相关的 kernel buffer。DMA把数据从kernel buffer直接拷⻉给协议栈。这⾥没有⽤户空间和内核空间之间的切换，在内核中直接完成了从⼀个buffer到另⼀个buffer的拷⻉。Nginx快速安装版本Mainline version 开发版Stable version 稳定版Legacy version 历史版本安装请移步到nginx官网我这里提供 Ubuntu/Centos安装最新版nginx点击这里 ::aru:clap::Nginx安装⽬录路径类型作⽤/etc/nginx/etc/nginx/nginx.conf/etc/nginx/conf.d/etc/nginx/conf.d/default.conf配置⽂件Nginx主配置⽂件/etc/nginx/fastcgi_params/etc/nginx/scgi_params/etc/nginx/uwsgi_params配置⽂件Cgi、Fastcgi、Uwcgi配置⽂件/etc/nginx/win-utf/etc/nginx/koi-utf/etc/nginx/koi-win配置⽂件Nginx编码转换映射⽂件/etc/nginx/mime.types配置⽂件http协议的Content-Type与扩展名/usr/lib/systemd/system/nginx.service配置⽂件配置系统守护进程管理器/etc/logrotate.d/nginx配置⽂件Nginx⽇志轮询,⽇志切割/usr/sbin/nginx/usr/sbin/nginx-debug命令Nginx终端管理命令/etc/nginx/modules/usr/lib64/nginx/usr/lib64/nginx/modules⽬录Nginx模块⽬录/usr/share/nginx/usr/share/nginx/html/usr/share/nginx/html/50x.html/usr/share/nginx/html/index.html⽬录Nginx默认站点⽬录/usr/share/doc/nginx-1.12.2/usr/share/man/man8/nginx.8.gz⽬录Nginx的帮助⼿册/var/cache/nginx⽬录Nginx的缓存⽬录/var/log/nginx⽬录Nginx的⽇志⽬录Nginx编译参数编译选项作⽤--prefix=/etc/nginx--sbin-path=/usr/sbin/nginx--modules-path=/usr/lib64/nginx/modules--conf-path=/etc/nginx/nginx.conf--error-log-path=/var/log/nginx/error.log--http-log-path=/var/log/nginx/access.log--pid-path=/var/run/nginx.pid--lock-path=/var/run/nginx.lock程序安装⽬录和路径--http-client-body-temp-path=/var/cache/nginx/client_tem--http-proxy-temp-path=/var/cache/nginx/proxy_temp--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp--http-scgi-temp-path=/var/cache/nginx/scgi_temp临时缓存⽂件--user=nginx--group=nginx设定Nginx进程启动⽤户和组(安全)--with-cc-opt设置额外的参数将被添加到CFLAGS变量--with-ld-opt设置附加的参数, 链接系统库Nginx常⽤模块Nginx编译选项模块作⽤ngx_http_core_module包含⼀些核⼼的http参数配置，对应Nginx的配置区块部分ngx_http_access_module访问控制模块，⽤来控制⽹站⽤户对Nginx的访问ngx_http_gzip_module压缩模块，对Nginx返回的数据压缩，属于性能优化模块ngx_http_fastcgi_modulefastci模块，和动态应⽤相关的模块，例如PHPngx_http_proxy_moduleproxy代理模块ngx_http_upstream_module负载均衡模块，可以实现⽹站的负载均衡功能及节点的健康检查。ngx_http_rewrite_moduleURL地址重写模块ngx_http_limit_conn_module限制⽤户并发连接数及请求数模块ngx_http_limit_req_module限制Nginx request processing rate根据定义的keyNginx常⽤模块ngx_http_log_module访问⽇志模块，以指定的格式记录Nginx客户访问⽇志等信息ngx_http_auth_basic_moduleWeb认证模块，设置Web⽤户通过账号密码访问Nginxnginx_http_ssl_modulessl模块，⽤于加密的http连接，如httpsNginx内置变量http核⼼模块的内置变量http请求变量Nginx内置变量⾃定义变量$uri: 当前请求的uri，不带参数 $request_uri: 请求的uri，带完整参数 $host: http请求报⽂中host⾸部,如果没有则以处理此请求的虚拟主机的主机名代替 $hostname: nginx服务运⾏在主机的主机名 $remote_addr: 客户端IP $remote_port: 客户端端⼝ $remote_user: 使⽤⽤户认证时客户端⽤户输⼊的⽤户名 $request_filename: ⽤户请求中的URI经过本地root或alias转换后映射的本地⽂件路径 $request_method: 请求⽅法, GET POST PUT $server_addr: 服务器地址 $server_name: 服务器名称 $server_port: 服务器端⼝ $server_protocol: 服务器向客户端发送响应时的协议, 如http/1.1 http/1.0 $scheme:在请求中使⽤scheme, 如http://xxx.com中的http $http_HEADER: 匹配请求报⽂中指定的HEADER $http_host: 匹配请求报⽂中的host⾸部 $document_root: 当前请求映射到的root配置 Nginx状态码[post cid="3071" /]
模型图单线程处理我们自己会怎样写单线程处理多个客户端连接呢？我们知道在linux里面中每个网络连接在内核中都是文件描述符(Fd)的形式存在,为了使大家看得明白，我们使用一段伪代码来编写一个单线程网络服务器，以下伪代码中我们需要用程序判断当前Fdx是否有数据，这个其实过程还是有些慢的，下面让我们看一下select/poll/epoll的原代码是怎么写的。while (1){ for(Fdx in (Fd1~Fd5)) { # Fdx 为当前遍历到的文件;Fd1~Fd5为这5个网络连接在内核中的文件描述符 if (Fdx) { # 是否有数据 # 读取Fdx并处理数据 } } }select 函数源码# 准备文件描述符的数组 Fds # 创建socket服务端 sockfd = socket(AF_INET, SOCK_STREAM, 0); memset(&addr, 0, sizeof (addr)); addr.sin_family = AF_INET; addr.sin_port = htons(2000); addr.sin_addr.s_addr = INADDR_ANY; bind(sockfd,(struct sockaddr*)&addr ,sizeof(addr)); listen (sockfd, 5); # 创建了5个文件描述符 for (i=0;i<5;i++) { memset(&client, 0, sizeof (client)); addrlen = sizeof(client); fds[i] = accept(sockfd,(struct sockaddr*)&client, &addrlen); # 计算出一个最大的文件描述符 if(fds[i] > max) max = fds[i]; } # 通过以上程序我们准备好了一个文件描述符的集合假如是1 3 6 7 9 和 一个最大的文件描述符 9 while(1){ FD_ZERO(&rset); for (i = 0; i< 5; i++ ) { FD_SET(fds[i],&rset); } puts("round again"); # select参数依次含义：最大的文件描述符 + 1 (用于内核截取文件描述符); 读文件描述符集合; 写文件描述符集合;异常文件描述符集合; 超时时间; # rset参数接受的并不是Fd集合，而是接受了FD_SET，FD_SET的类型是一个bitmap # 假如我们文件描述符集合是 1 3 6 7 9，那么我们bitmap里面存的值就是0101001101000... (1024位01的位置坑位)。 # 无数据时select阻塞 select(max+1, &rset, NULL, NULL, NULL); # 有数据时select会把Fd置位并返回向下执行。 for(i=0;i<5;i++) { # 循环判断哪个Fd被置位了，然后读取数据并且处理 if (FD_ISSET(fds[i], &rset)){ memset(buffer,0,MAXBUF); read(fds[i], buffer, MAXBUF); puts(buffer); } } }select 函数执行过程过程中会把用户态空间rset的bitmap类型的数据复制到内核态一份，由内核判断Fd是否有数据。我们知道用户态程序判断效率一定是低于内核去判断的，因为用户态判断也是在询问内核，它是有一个用户态到内核态的切换的，判断每一个Fd都需要用户态到内核态的切换。如果没数据的话，select函数是一个阻塞函数，会一直阻塞在select所在的行。如果有数据的话，内核会把有数据的Fd置位(可以理解为标识成有数据)并且返回，不在阻塞。select 缺点rset 的 bitmap 类型有大小限制1024位，即FD_SET的大小限制为1024位FD_SET是不可重用，rset 的数据会被内核置位修改，每次while循环的时候都需要重新赋值虽然是比我们在用户态判断的消耗好些，但是 rset 数据从用户态复制到内核态仍然是有很大的开销的select 函数返回的时候说明至少有一个Fd有数据(被置位了)，但是我们并不知道是哪一个Fd或者那些Fd被置位了，需要我们再去遍历一边哪个被置位了，需要o(n)的复杂度的。poll 函数源码# 声明结构图pollfd struct pollfd { int fd; short events; short revents; }; # 构造pollfd数据 for (i=0;i<5;i++) { memset(&client, 0, sizeof (client)); addrlen = sizeof(client); pollfds[i].fd = accept(sockfd,(struct sockaddr*)&client, &addrlen); # POLLIN 读事件 pollfds[i].events = POLLIN; } sleep(1); while(1){ puts("round again"); # 传入pollfds 结构体 # 5 传入的数量 # 50000 超时时间 # 也是阻塞函数 poll(pollfds, 5, 50000); for(i=0;i<5;i++) { if (pollfds[i].revents & POLLIN){ pollfds[i].revents = 0; memset(buffer,0,MAXBUF); read(pollfds[i].fd, buffer, MAXBUF); puts(buffer); } } }同select一样，poll函数也阻塞函数，只不过poll传入的是一个数组类型的结构体，poll函数也会把用户态数据复制到内核态置位，但是select置位的是bitmap(导致不可从重用),而poll置位的是结构体中的revents(初始化为0，有数据置位为POLLIN)字段。同理，无数据时poll堵塞，有数据时执行读操作并处理数据，并把revents设置为0，所以pollfd数据并没有发生改变可以复用。poll函数解决了select的哪些问题pollfd这种结构体，解决了bitmap 1024位的限制结构体的revents字段在有数据是被置位(POLLIN),我们遍历读取的时候重新设置为0，所以pollfds可以重用，解决了select中 FD_SET 不可重用的弊端。epoll 函数源码 # 创建一个epfd,在执行epoll_wait时需要，epfd 就相当于一种空的数据格式 struct epoll_event events[5]; int epfd = epoll_create(10); ... ... for (i=0;i<5;i++) { static struct epoll_event ev; memset(&client, 0, sizeof (client)); addrlen = sizeof(client); ev.data.fd = accept(sockfd,(struct sockaddr*)&client, &addrlen); ev.events = EPOLLIN; # 对 epfd配置，相当于poll声明的结构体，定义一种数据格式，epoll_ctl相当于构造epfd这种数据格式，大概是一个文件描述符对应的一个events事件 (fd-events) epoll_ctl(epfd, EPOLL_CTL_ADD, ev.data.fd, &ev); } # 准备好epfd执行下面程序 while(1){ puts("round again"); nfds = epoll_wait(epfd, events, 5, 10000); for(i=0;i<nfds;i++) { memset(buffer,0,MAXBUF); read(events[i].data.fd, buffer, MAXBUF); puts(buffer); } }epoll 也是阻塞函数，无数据时阻塞；有数据时，它也是置位，不过它的置位其实是一种变相的位，会把有数据的fd重拍到最前面位置，之前的select和poll都没有返回值，而epoll是有返回值的，它的返回值是有数据的个数。这样的话我们后续的遍历读取处理也简单了，遍历复杂度由 o(n)次简化为 o(1)次了。select/poll/epoll 源码参考文章
线程线程是CPU调度的最小单位CPU 执行过程图比如我们有一个单核CPU，目前有3个想要执行的线程，这3个线程在执行过程中先执行1,在下一个时间片cpu让渡出来执行线程2，在下一个时间片cpu让渡出来执行线程3，以此类推，最终将3个线程执行完毕。如果我们不将CPU进行分片，按顺序先执行完线程1，再执行线程2，最后执行线程3。我们是否有一个疑惑，前者(CPU切片)和后者(顺序执行)使用的时间貌似是一样的，并且后者(顺序执行)只切换了2次上下文，这样是不是执行的效率更高呢？那多线程存在的意义有体现在哪里呢?提到了上面的疑问，我们就不得不说道一下I/O(磁盘的读写、网络的数据传输、音视频的输入输出等)，那么我们在编程的中的I/O主要是磁盘I/O和网络I/O，I/O是非常耗时间的。假如我们有CPU、硬盘、内存，要想文件读取，我们最先想到的是CPU直接读取硬盘数据放入内存的变量中，这样在整个读取的过程中整个I/O都被占用的，因而这种方式会造成前者(CPU切片)和后者(顺序执行)执行效率差不多的。但是实际的I/O并不是这样的，实际的I/O的过程中有一个非常重要的东西DMA控制器。在读取文件的过程中，其实CPU不会直接操作硬盘，而是给DMA控制器下达命令来完成文件的读取。首先，CPU 对DMA下达指令(这个指令中包含有磁盘设备信息和要读取的文件位置信息)，然后DMA告知硬盘进行文件读取并将读取的文件内容加载到内存变量中，读取完毕之后硬盘会返回反馈一个读取结果完成的信息给到DMA，DMA之后以终端的形式通知CPU，然后CPU去内存中拉取数据，最终拿到数据。我们通过步骤一下达指令之后，其实CPU就处于闲置状态，就可以执行其他线程。我们现在再此回看单核cpu处理3个线程读取文件，CPU下达指令给DMA后(CPU闲置)，假如线程2拿到CPU控制权，同样CPU下达指令给DMA后(CPU闲置)，线程3拿到CPU控制权，同样CPU下达指令给DMA后(CPU闲置)，CPU可以执行其他线程，最终实现并行读取，提高CPU利用率。另外，DMA进行数据读取是可以复用的,CPU的总线具有多条线路,DMA就可以充分利用这些线路,最终实现并行的读取文件。协程协程是类似于编程语言级别的线程，一种用户态的轻量级线程,协程不像线程和进程需要进行系统内核上的上下文切换,协程的上下文切换是由用户自己决定的,有自己的上下文,所以说是轻量级的线程,也称之为用户级别的线程就叫协程,一个线程可以多个协程,线程进程都是同步机制,而协程则是异步。协程没有用户态和内核态的转化，性能会比线程效率更高。异步cpu 发送指令给DMA就是异步执行读取文件，cpu则会被空闲出来执行别的线程。程序级别的也有很多异步操作，比如充值成功短信通知用户的操作，一般都是异步存于队列。