友儿屋 - 架构
type: channel
link: https://niuzheng.net/category/jiagou/
description: 架构学习
items: 值得你拥有的Shell脚本
items: nginx禁止访问php的各种形式
items: nginx 常用代理
items: 内网穿透
items: 一些常用命令使用方法简述之Linux
items: 使用Nginx+GeoIP获取IP信息
items: Nginx基本配置介绍(待完善)
items: nginx 应用指南
items: 什么是select/poll/epoll模型？
items: 线程/协程/异步的编程模型解读

值得你拥有的Shell脚本
type: item
date: 2023-09-07T14:46:00+08:00
description: 检测两台服务器指定目录下的文件一致性 #!/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 ######################################################### #计算文档每行出现的数字个数，并计算整个文档…

nginx禁止访问php的各种形式
type: item
date: 2023-09-04T11:03:00+08:00
description: 禁止访问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; # 限制访问时段 } # ...…

nginx 常用代理
type: item
date: 2023-08-22T16:14:00+08:00
description: 根据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; } }

内网穿透
type: item
date: 2023-05-08T16:50:00+08:00
description: 内网穿透内网穿透，也即 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上以守护进程方式…

一些常用命令使用方法简述之Linux
type: item
date: 2023-05-08T10:12:49+08:00
description: 查看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 …

使用Nginx+GeoIP获取IP信息
type: item
date: 2023-04-12T15:56:00+08:00
description: 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>…

Nginx基本配置介绍(待完善)
type: item
date: 2023-02-24T17:27:00+08:00
description: 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;默认为o…

nginx 应用指南
type: item
date: 2023-02-03T09:20:00+08:00
description: 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/…

什么是select/poll/epoll模型？
type: item
date: 2023-02-02T14:34:00+08:00
description: 模型图单线程处理我们自己会怎样写单线程处理多个客户端连接呢？我们知道在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); poll…

线程/协程/异步的编程模型解读
type: item
date: 2023-02-02T10:04:00+08:00
description: 线程线程是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则会被空闲出来执行别的线程。程序级别的也有很多异步操作，比如充值成功短信通知用户的操作，一般都是异步存于队列。