1.
Select模型:select系统调用将轮询 的操作交给了内核来帮我们完成,从而避免在用户空间不断发起的轮询所带来的系统开销。
1 用户态发起 select 调用: 在用户态,应用程序执行 select 系统调用,此时用户线程会被阻塞。 select 用于让内核帮忙监视一组文件描述符(fd)是否就绪。 2 拷贝 fd 数组到内核态: 将需要监视的文件描述符数组从用户态拷贝到内核态。这样内核才能对这些文件描述符进行后续检查。 3 内核态遍历检查: 在内核态,内核开始遍历接收到的文件描述符数组,查找其中哪些文件描述符对应的 I/O 操作已经就 绪。 比如检查哪些fd对应的socket套接字有数据可读,哪些可以进行写操作等。 4 标记就绪 fd: 内核找到就绪的文件描述符后,会将这些就绪 I/O 对应的文件描述符数组Bitmap的值位置标记为 1, 若没有数据到来,则为0。从而实现标记哪些文件描述符是就绪的。 5 拷贝结果回用户态: 内核将修改后的文件描述符数组拷贝回用户态。 5 用户态遍历查找: 在用户态,阻塞被解除,用户线程开始遍历文件描述符数组,寻找那些被标记为就绪的文件描述符,然后 对这些就绪的文件描述符进行相应的 I/O 操作。 操作完毕后,需要重置fd数组,并需要重新调用select传入充值后的fd数组,让内核发起新一轮遍历轮 询。
poll模型:没有最大文件描述符数量的限制(基于链表存储)的select
epoll模型:使用epoll专用的文件描述符和内核中的红黑树及就绪队列。
用户空间 epoll_create:用户空间通过调用epoll_create函数在内核空间创建一个 epoll 实例。- 红黑树(rbr)用于管理所有被监控的文件描述符(如 socket)- 就绪链表(rdllist)用于存放有事件发生(就绪)的文件描述符- 等待队列(wq)用于存储等待事件发生的进程。 epoll_ctl:调用epoll_ctl函数可向已创建的 epoll 实例中添加|修改|删除需要监控的 socket。- 添加时,对应的 socket 信息会被添加到红黑树中进行管理。 epoll_wait:用户空间调用epoll_wait函数,主动放弃 CPU 资源进入等待状态。- 此时内核会检查就绪队列中是否有就绪的文件描述符。- 若有,内核会唤醒对应的用户进程,并返回相关事件信息;- 若无,则进程会一直等待,直到有事件发生或者超时。
2.
#!/bin/bash
# Nginx 编译安装脚本
# 适用于 CentOS/RHEL 和 Ubuntu/Debian 系统
# 定义版本和安装路径
NGINX_VERSION="1.24.0"
PCRE_VERSION="8.45"
ZLIB_VERSION="1.2.13"
OPENSSL_VERSION="3.0.8"
NGINX_PREFIX="/usr/local/nginx"
SOURCE_DIR="/usr/local/src"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # 无颜色
# 错误处理函数
handle_error() {
echo -e "${RED}错误: $1${NC}"
exit 1
}
# 检查是否为 root 用户
check_root() {
if [ "$(id -u)" -ne 0 ]; then
handle_error "请使用 root 用户运行此脚本"
fi
}
# 安装依赖
install_dependencies() {
echo -e "${YELLOW}正在安装编译依赖...${NC}"
# 检测操作系统
if [ -f /etc/redhat-release ]; then
# CentOS/RHEL
yum install -y wget gcc gcc-c++ make libtool tar gzip || handle_error "依赖安装失败"
elif [ -f /etc/debian_version ]; then
# Ubuntu/Debian
apt-get update || handle_error "更新软件源失败"
apt-get install -y wget build-essential libtool tar gzip || handle_error "依赖安装失败"
else
handle_error "不支持的操作系统"
fi
echo -e "${GREEN}依赖安装完成${NC}"
}
# 创建目录
create_directories() {
echo -e "${YELLOW}正在创建目录...${NC}"
mkdir -p "$SOURCE_DIR" || handle_error "创建源目录失败"
mkdir -p "$NGINX_PREFIX" || handle_error "创建安装目录失败"
echo -e "${GREEN}目录创建完成${NC}"
}
# 下载源码
download_sources() {
echo -e "${YELLOW}正在下载源码...${NC}"
cd "$SOURCE_DIR" || handle_error "无法进入源目录"
# 下载 Nginx
if [ ! -f "nginx-$NGINX_VERSION.tar.gz" ]; then
wget "https://nginx.org/download/nginx-$NGINX_VERSION.tar.gz" || handle_error "下载 Nginx 失败"
fi
# 下载 PCRE
if [ ! -f "pcre-$PCRE_VERSION.tar.gz" ]; then
wget "https://ftp.pcre.org/pub/pcre/pcre-$PCRE_VERSION.tar.gz" || handle_error "下载 PCRE 失败"
fi
# 下载 Zlib
if [ ! -f "zlib-$ZLIB_VERSION.tar.gz" ]; then
wget "https://zlib.net/zlib-$ZLIB_VERSION.tar.gz" || handle_error "下载 Zlib 失败"
fi
# 下载 OpenSSL
if [ ! -f "openssl-$OPENSSL_VERSION.tar.gz" ]; then
wget "https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz" || handle_error "下载 OpenSSL 失败"
fi
echo -e "${GREEN}源码下载完成${NC}"
}
# 解压源码
extract_sources() {
echo -e "${YELLOW}正在解压源码...${NC}"
cd "$SOURCE_DIR" || handle_error "无法进入源目录"
# 解压 Nginx
if [ ! -d "nginx-$NGINX_VERSION" ]; then
tar -zxf "nginx-$NGINX_VERSION.tar.gz" || handle_error "解压 Nginx 失败"
fi
# 解压 PCRE
if [ ! -d "pcre-$PCRE_VERSION" ]; then
tar -zxf "pcre-$PCRE_VERSION.tar.gz" || handle_error "解压 PCRE 失败"
fi
# 解压 Zlib
if [ ! -d "zlib-$ZLIB_VERSION" ]; then
tar -zxf "zlib-$ZLIB_VERSION.tar.gz" || handle_error "解压 Zlib 失败"
fi
# 解压 OpenSSL
if [ ! -d "openssl-$OPENSSL_VERSION" ]; then
tar -zxf "openssl-$OPENSSL_VERSION.tar.gz" || handle_error "解压 OpenSSL 失败"
fi
echo -e "${GREEN}源码解压完成${NC}"
}
# 编译安装 Nginx
compile_nginx() {
echo -e "${YELLOW}正在编译安装 Nginx...${NC}"
cd "$SOURCE_DIR/nginx-$NGINX_VERSION" || handle_error "无法进入 Nginx 源码目录"
# 配置
./configure \
--prefix="$NGINX_PREFIX" \
--with-pcre="$SOURCE_DIR/pcre-$PCRE_VERSION" \
--with-zlib="$SOURCE_DIR/zlib-$ZLIB_VERSION" \
--with-openssl="$SOURCE_DIR/openssl-$OPENSSL_VERSION" \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_gzip_static_module \
--with-http_stub_status_module \
--with-threads \
--with-stream \
--with-stream_ssl_module || handle_error "Nginx 配置失败"
# 编译
make -j "$(nproc)" || handle_error "Nginx 编译失败"
# 安装
make install || handle_error "Nginx 安装失败"
echo -e "${GREEN}Nginx 编译安装完成${NC}"
}
# 创建系统服务
create_systemd_service() {
echo -e "${YELLOW}正在创建系统服务...${NC}"
cat > /etc/systemd/system/nginx.service << EOF
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
PIDFile=$NGINX_PREFIX/logs/nginx.pid
ExecStartPre=$NGINX_PREFIX/sbin/nginx -t
ExecStart=$NGINX_PREFIX/sbin/nginx
ExecReload=$NGINX_PREFIX/sbin/nginx -s reload
ExecStop=$NGINX_PREFIX/sbin/nginx -s stop
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
# 重载 systemd
systemctl daemon-reload || handle_error "重载 systemd 失败"
# 设置开机启动
systemctl enable nginx || handle_error "设置 Nginx 开机启动失败"
echo -e "${GREEN}Nginx 系统服务创建完成${NC}"
}
# 配置防火墙
configure_firewall() {
echo -e "${YELLOW}正在配置防火墙...${NC}"
# 检测操作系统
if [ -f /etc/redhat-release ]; then
# CentOS/RHEL
if command -v firewalld >/dev/null 2>&1 && systemctl is-active firewalld >/dev/null 2>&1; then
firewall-cmd --permanent --add-service=http || handle_error "添加 HTTP 服务到防火墙失败"
firewall-cmd --permanent --add-service=https || handle_error "添加 HTTPS 服务到防火墙失败"
firewall-cmd --reload || handle_error "重载防火墙失败"
echo -e "${GREEN}防火墙配置完成${NC}"
else
echo -e "${YELLOW}Firewalld 未运行,跳过防火墙配置${NC}"
fi
elif [ -f /etc/debian_version ]; then
# Ubuntu/Debian
if command -v ufw >/dev/null 2>&1 && ufw status | grep -q "Status: active"; then
ufw allow "Nginx Full" || handle_error "添加 Nginx 规则到防火墙失败"
echo -e "${GREEN}防火墙配置完成${NC}"
else
echo -e "${YELLOW}UFW 未运行,跳过防火墙配置${NC}"
fi
fi
}
# 验证安装
verify_installation() {
echo -e "${YELLOW}正在验证安装...${NC}"
# 检查 Nginx 版本
"$NGINX_PREFIX/sbin/nginx" -v || handle_error "Nginx 版本检查失败"
# 启动 Nginx
systemctl start nginx || handle_error "启动 Nginx 失败"
# 检查 Nginx 状态
systemctl is-active --quiet nginx || handle_error "Nginx 未运行"
echo -e "${GREEN}Nginx 安装验证通过${NC}"
echo -e "${GREEN}Nginx 已安装在: $NGINX_PREFIX${NC}"
echo -e "${GREEN}Nginx 配置文件位于: $NGINX_PREFIX/conf/nginx.conf${NC}"
echo -e "${GREEN}使用 systemctl 命令管理 Nginx:${NC}"
echo -e "${YELLOW}启动: systemctl start nginx${NC}"
echo -e "${YELLOW}停止: systemctl stop nginx${NC}"
echo -e "${YELLOW}重启: systemctl restart nginx${NC}"
echo -e "${YELLOW}重载配置: systemctl reload nginx${NC}"
}
# 主函数
main() {
check_root
install_dependencies
create_directories
download_sources
extract_sources
compile_nginx
create_systemd_service
configure_firewall
verify_installation
echo -e "${GREEN}Nginx 安装完成!${NC}"
}
# 执行主函数
main
3.- 新版本或配置准备: 准备好新版本的 Nginx 可执行文件或配置文件,并确保它们没有问题- 新版本资源替换旧版本资源: 进行资源替换,此步骤要备份旧版本资源,可以用作回滚- 发送信号: 使用 nginx -s SIGUSR2,nginx -s reload 等方式向 Nginx 主进程发送重新加载的信号- 新的工作进程启动: Nginx 主进程接收到重新加载信号后,会启动新的工作进程,并使用新的配置文件或软件版本- 平滑过渡: 新的工作进程逐渐接管现有的连接。现有的连接会在旧的工作进程中继续处理,而新的连接会由新的工作 进程处理- 旧的进程退出: 当旧的工作进程不再有活动连接时,它会被关闭
4.
# --- 全局配置段 ---
user ...;
worker_processes ...;
...
#所有变量查询索引
# --- 事件驱动相关的配置 ---
events {
...;
}
# --- http/https 协议相关配置段 --
http {
...;
server {
...;
location {
...;
}
}
}
# --- mail 协议相关配置段,默认被注释 --
mail {
server {
...
}
}
# --- stream 协议相关配置 --
stream {
...;
}
5.
创建日志格式
root@ubuntu24:~# cat > log <<-eof
#自定义访问日志格式 - 字符串
log_format basic '\$remote_addr - \$remote_user [\$time_local]
"\$request" '
'\$status \$body_bytes_sent "\$http_referer" '
'"\$http_user_agent" "\$http_x_forwarded_for"';
eof
#自定义访问日志格式 - json 字符串
log_format json_basic '{"remote_addr": "\$remote_addr", '
'"remote_user": "\$remote_user", '
'"time_local": "\$time_local", '
'"request": "\$request", '
'"status": "\$status", '
'"body_bytes_sent": "\$body_bytes_sent", '
'"http_referer": "\$http_referer", '
'"http_user_agent": "\$http_user_agent", '
'"http_x_forwarded_for": "\$http_x_forwarded_for"}';
添加配置
root@ubuntu24:~# num=$(grep -n access_log /etc/nginx/nginx.conf | awk -F':'
'{print $1}')
root@ubuntu24:~# let num-=2
root@ubuntu24:~# sed -i "${num}r log" /etc/nginx/nginx.conf
因为日志文件需要创建,所以,需要有对应的权限
root@ubuntu24:~# grep user /etc/nginx/nginx.conf
user www-data;
root@ubuntu24:~# chown -R www-data:www-data /var/log/nginx/
定制配置文件
root@ubuntu24:~# cat > /etc/nginx/conf.d/vhost.conf <<-eof
server {
listen 80 default_server;
access_log /var/log/nginx/\${host}_access.log basic;
location /web1/ {
alias /data/server/nginx/web1/;
}
# 此资源记录json日志
location /json{
access_log /var/log/nginx/\${host}_json_access.log json_basic;
return 200 "json\n";
}
#不记录access log
location /test{
access_log off;
return 200 "test\n";
}
}
eof
重启服务
root@ubuntu24:~# systemctl restart nginx