ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Windows下Nginx优雅启停:生产级BAT脚本设计与实战

Windows下Nginx优雅启停:生产级BAT脚本设计与实战 1. 为什么一个“优雅”的Nginx启动脚本值得花半小时重写你有没有试过双击一个start_nginx.bat结果窗口闪一下就消失连错误都没看清或者改完配置文件得手动打开任务管理器杀掉nginx.exe进程再重新双击启动——稍有不慎就残留多个 worker 进程CPU 占用飙到 100%浏览器反复刷新全是 502 Bad Gateway这不是你的操作问题是绝大多数网上流传的 Nginx bat 脚本根本没考虑 Windows 服务环境的真实约束。我刚接手一个客户项目时运维同事甩给我一个 3 行的 bat 文件start nginx.exe pause结果上线第三天凌晨两点监控报警Nginx 响应延迟突增 800ms。登录服务器一看进程列表里赫然躺着 7 个nginx.exe实例——全卡在worker process is shutting down状态没人处理 SIGQUIT 信号也没人等nginx -s quit完成就强行关闭控制台。更糟的是nginx.conf里配了daemon off;但脚本却用start启动导致主进程和 worker 进程完全脱离控制日志写一半、连接半途断开成了常态。真正的“优雅”不是字面意思的视觉美观而是可预测、可审计、可中断、可回滚。它意味着启动前自动检测端口占用比如 80/443 是否被 Skype 或 IIS 占着关闭时强制等待所有 worker 进程优雅退出不是taskkill /f那种暴力重启时先验证新配置语法正确性nginx -t失败则自动回滚到上一版配置所有操作记录完整日志包括时间戳、PID、退出码方便事后追溯支持静默模式无窗口弹出和交互模式带提示确认双路径。这背后涉及 Windows 批处理的三个底层机制进程树关系管理start /B和start /WAIT的区别直接决定你能否捕获子进程 PID信号模拟限制Windows 没有 Unix 的kill -QUIT必须用nginx -s quit触发内部 shutdown 流程权限继承陷阱以管理员身份运行 bat 时start启动的 nginx 进程默认继承当前会话的完整性级别Medium但若 nginx 需要绑定 80 端口就必须是 High 级别——否则nginx -t通过nginx -c却报bind() to 0.0.0.0:80 failed (10013: An attempt was made to access a socket in a way forbidden by its access permissions)。所以别再复制粘贴那些“一键三连”脚本了。下面我会从零构建一个生产级可用的 Nginx 控制脚本每一步都解释清楚为什么这么写以及 Windows 环境下哪些坑我踩过三次以上。2. 核心逻辑拆解Nginx 在 Windows 下的生命周期管理2.1 Nginx 进程模型与 Windows 的兼容性断层Linux 下 Nginx 启动后master 进程 fork 出多个 worker 进程master 负责监听信号并协调 worker。但在 Windows 上Nginx 是单线程模型一个nginx.exe进程同时承担 master 和 worker 职能。官方文档明确说明“On Windows, nginx does not use the master/worker process model. Instead, it runs as a single process.”来源nginx.org/docs/windows.html这意味着nginx -s reload在 Windows 下实际执行的是停止当前进程 → 启动新进程 → 旧进程等待连接关闭 → 新进程接管监听。整个过程没有原子性存在短暂的服务不可用窗口nginx -s stop并非立即终止而是发送WM_CLOSE消息给主窗口即使你加了-g daemon off;需要等待所有活跃连接自然关闭nginx -s quit是唯一真正“优雅”的关闭方式它会触发ngx_worker_process_cycle()中的ngx_signal_handler()执行ngx_close_listening_sockets()并等待ngx_event_expire_timers()完成。提示很多教程教用taskkill /PID %PID% /F强杀这是最危险的做法。Nginx 在关闭过程中会将未完成的请求写入logs/error.log但若被强制终止这些日志可能丢失且临时文件如client_body_temp目录下的上传缓存无法清理下次启动时报open() /nginx/client_body_temp/0000000001 failed (13: Permission denied)。2.2 bat 脚本必须解决的四大硬约束约束类型具体表现解决方案端口冲突检测IIS、Skype、SQL Server Reporting Services 默认占 80/443使用netstat -ano | findstr :80获取 PID再用tasklist /FI PID eq %PID%查进程名配置热加载安全nginx -s reload失败时旧配置已失效新配置又不生效必须先nginx -t验证成功才执行nginx -s reload失败则输出具体错误行号进程状态同步nginx -s quit返回 0 不代表进程已退出需轮询tasklist确认设置最大等待 30 秒每 500ms 检查一次超时则记录警告日志日志路径隔离多个项目共用同一份 nginx.exe但日志必须分项目存储在 bat 中动态设置-p参数指定安装路径-c指定配置文件避免硬编码我们来实测一个典型场景某次更新配置后nginx -t报错nginx: [emerg] invalid number of arguments in proxy_pass directive in C:\nginx\conf\nginx.conf:42。如果脚本没做nginx -t校验就直接nginx -s reload结果就是整个服务不可用而错误信息只在控制台一闪而过。真正的优雅是让这个错误变成一行可定位的日志“2024-06-15 14:22:31 ERROR config test failed at line 42: invalid number of arguments in proxy_pass”。2.3 为什么不用 PowerShell——bat 的不可替代性看到这里你可能会问PowerShell 功能更强为什么坚持用 bat答案很现实部署环境的确定性。客户内网服务器禁用 PowerShell 执行策略ExecutionPolicy Restricted且无权限修改某些老旧工业控制系统只允许.bat和.exe文件执行bat 文件无需额外依赖双击即用而 PowerShell 脚本需要.ps1后缀 签名证书才能绕过策略限制。但 bat 也有致命短板无法原生解析 JSON、不能直接调用 Win32 API。所以我们用“bat 小型工具”组合拳端口检测用netstatfindstrWindows 自带进程 PID 获取用wmic process where namenginx.exe get ProcessId比tasklist更精准日志时间戳用powershell -Command Get-Date -Format yyyy-MM-dd HH:mm:ss仅用于时间生成不参与核心逻辑。注意wmic在 Windows 10 22H2 及以后版本已被标记为“deprecated”但目前仍可用。若未来失效备用方案是用tasklist /fi imagename eq nginx.exe 字符串截取虽然更脆弱但兼容性更好。3. 从零构建一个生产级 Nginx 控制脚本的逐行实现3.1 脚本结构设计原则我们不写“万能通用脚本”而是聚焦一个真实场景本地开发环境快速启停 Nginx支持多实例隔离。因此脚本必须满足可配置化通过config.ini文件定义 Nginx 路径、配置文件、日志目录可复用同一份脚本部署到不同项目目录只需改config.ini可审计所有操作写入nginx_control.log包含操作类型、时间、结果、错误详情可调试加/debug参数时显示详细执行步骤和命令输出。脚本最终目录结构如下project-root/ ├── nginx/ # Nginx 安装目录含 nginx.exe ├── conf/ # 自定义配置文件nginx.conf ├── logs/ # 日志目录覆盖默认路径 ├── scripts/ │ ├── nginx_ctl.bat # 主控制脚本 │ └── config.ini # 配置文件 └── README.md3.2 config.ini 配置文件详解; nginx_ctl.bat 配置文件 ; 修改此文件即可适配不同项目无需改动 bat 脚本本身 [nginx] ; Nginx 主程序路径相对路径或绝对路径 path ..\nginx\nginx.exe ; 配置文件路径必须是绝对路径或相对于本脚本的路径 conf ..\conf\nginx.conf ; 日志目录用于 -p 参数确保 logs/ 子目录存在 logs_dir ..\logs ; PID 文件路径Windows 下实际不生成 pid 文件但保留接口 pid_file ..\logs\nginx.pid [control] ; 启动时是否后台运行true无窗口false显示控制台 daemon true ; 等待进程退出的最大秒数quit/reload 时使用 timeout 30 ; 日志文件名写入 scripts/ 目录下 log_file nginx_control.log提示INI 文件解析在 bat 中无法原生支持我们采用“伪 INI 解析”用for /f读取每一行跳过注释;开头和空行对keyvalue格式提取值。虽然不如 Python 的configparser严谨但足够应对简单配置。3.3 nginx_ctl.bat 核心代码实现含关键注释echo off setlocal enabledelayedexpansion :: 初始化配置 set SCRIPT_DIR%~dp0 set CONFIG_FILE%SCRIPT_DIR%config.ini set LOG_FILE%SCRIPT_DIR%nginx_control.log :: 检查配置文件是否存在 if not exist %CONFIG_FILE% ( echo [%date% %time%] ERROR: config.ini not found in %SCRIPT_DIR% %LOG_FILE% echo ERROR: config.ini not found. Please create it first. pause exit /b 1 ) :: 解析 config.ini简化版仅支持 keyvalue 形式 for /f usebackq tokens1,* delims %%a in (type %CONFIG_FILE% ^| findstr /v ^; ^| findstr /v ^$) do ( set KEY%%a set VALUE%%b :: 去除首尾空格 for /f tokens* delims %%c in (!VALUE!) do set VALUE%%c if !KEY!nginx.path set NGINX_PATH!VALUE! if !KEY!nginx.conf set NGINX_CONF!VALUE! if !KEY!nginx.logs_dir set NGINX_LOGS!VALUE! if !KEY!control.daemon set DAEMON!VALUE! if !KEY!control.timeout set TIMEOUT!VALUE! if !KEY!control.log_file set LOG_FILE!VALUE! ) :: 验证必要参数 if not defined NGINX_PATH ( echo [%date% %time%] ERROR: nginx.path not set in config.ini %LOG_FILE% echo ERROR: nginx.path is required in config.ini. exit /b 1 ) if not defined NGINX_CONF ( echo [%date% %time%] ERROR: nginx.conf not set in config.ini %LOG_FILE% echo ERROR: nginx.conf is required in config.ini. exit /b 1 ) if not defined NGINX_LOGS ( echo [%date% %time%] ERROR: nginx.logs_dir not set in config.ini %LOG_FILE% echo ERROR: nginx.logs_dir is required in config.ini. exit /b 1 ) :: 构建绝对路径处理相对路径 pushd %SCRIPT_DIR% cd /d %NGINX_PATH:~0,-9% set NGINX_ABS_PATH%CD%\%NGINX_PATH:*.%% popd :: 命令行参数解析 set ACTIONstart set DEBUGfalse if %~1 goto :usage if %~1start set ACTIONstart goto :main if %~1stop set ACTIONstop goto :main if %~1restart set ACTIONrestart goto :main if %~1reload set ACTIONreload goto :main if %~1/debug set DEBUGtrue shift goto :parse_next if %~1/? goto :usage :parse_next if %~1 goto :main if %~1start set ACTIONstart goto :main if %~1stop set ACTIONstop goto :main if %~1restart set ACTIONrestart goto :main if %~1reload set ACTIONreload goto :main goto :usage :: 主逻辑入口 :main call :log [%date% %time%] ACTION: %ACTION% if %ACTION%start call :do_start goto :end if %ACTION%stop call :do_stop goto :end if %ACTION%restart call :do_restart goto :end if %ACTION%reload call :do_reload goto :end :usage echo. echo Usage: %~nx0 [start^|stop^|restart^|reload] [/debug] echo. echo Examples: echo %~nx0 start - Start nginx with current config echo %~nx0 stop - Stop nginx gracefully echo %~nx0 restart - Stop then start (full cycle) echo %~nx0 reload - Reload config without stopping service echo %~nx0 /debug - Enable debug output echo. exit /b 0 :: 启动逻辑 :do_start call :log Starting nginx... call :check_port_conflict 80 call :check_port_conflict 443 :: 验证配置语法 %NGINX_ABS_PATH% -t -c %NGINX_CONF% -p %NGINX_LOGS% nul 21 if errorlevel 1 ( call :log ERROR: nginx config test failed. Check %NGINX_CONF%. echo ERROR: nginx config test failed. See log file for details. exit /b 1 ) :: 构建启动命令 set START_CMD%NGINX_ABS_PATH% -c %NGINX_CONF% -p %NGINX_LOGS% if %DAEMON%true ( set START_CMDstart /B %START_CMD% ) else ( set START_CMDstart %START_CMD% ) :: 执行启动 call :log Executing: %START_CMD% %START_CMD% if errorlevel 1 ( call :log ERROR: nginx start failed with exit code %errorlevel%. echo ERROR: nginx start failed. exit /b %errorlevel% ) :: 等待进程稳定检查 PID 是否存在 call :wait_for_nginx 5 if errorlevel 1 ( call :log ERROR: nginx process not detected after startup. echo ERROR: nginx process not detected. exit /b 1 ) call :log SUCCESS: nginx started successfully. echo SUCCESS: nginx started. goto :eof :: 停止逻辑 :do_stop call :log Stopping nginx gracefully... call :get_nginx_pid if not defined NGINX_PID ( call :log WARNING: nginx process not found. Nothing to stop. echo WARNING: nginx process not found. exit /b 0 ) :: 发送 quit 信号 %NGINX_ABS_PATH% -s quit -c %NGINX_CONF% -p %NGINX_LOGS% nul 21 if errorlevel 1 ( call :log ERROR: nginx -s quit failed. Trying force kill. taskkill /PID %NGINX_PID% /F nul 21 if errorlevel 1 ( call :log CRITICAL: force kill failed. PID %NGINX_PID% may be orphaned. echo CRITICAL: force kill failed. Check Task Manager. exit /b %errorlevel% ) call :log INFO: nginx force killed (PID %NGINX_PID%). echo INFO: nginx force killed. goto :eof ) :: 等待进程退出 call :wait_for_pid_exit %NGINX_PID% %TIMEOUT% if errorlevel 1 ( call :log WARNING: nginx did not exit within %TIMEOUT% seconds. Force killing. taskkill /PID %NGINX_PID% /F nul 21 call :log INFO: nginx force killed (PID %NGINX_PID%). echo WARNING: nginx did not exit gracefully. Force killed. ) else ( call :log SUCCESS: nginx stopped gracefully. echo SUCCESS: nginx stopped. ) goto :eof :: 重启逻辑 :do_restart call :log Restarting nginx... call :do_stop call :do_start goto :eof :: 重载逻辑 :do_reload call :log Reloading nginx configuration... call :get_nginx_pid if not defined NGINX_PID ( call :log ERROR: nginx not running. Cannot reload. echo ERROR: nginx not running. Start it first. exit /b 1 ) :: 先验证新配置 %NGINX_ABS_PATH% -t -c %NGINX_CONF% -p %NGINX_LOGS% nul 21 if errorlevel 1 ( call :log ERROR: nginx config test failed before reload. echo ERROR: nginx config test failed. Fix config and try again. exit /b 1 ) :: 执行 reload %NGINX_ABS_PATH% -s reload -c %NGINX_CONF% -p %NGINX_LOGS% nul 21 if errorlevel 1 ( call :log ERROR: nginx -s reload failed. echo ERROR: nginx reload failed. exit /b %errorlevel% ) call :log SUCCESS: nginx configuration reloaded. echo SUCCESS: nginx configuration reloaded. goto :eof :: 辅助函数 :check_port_conflict set PORT%~1 echo Checking port %PORT%... for /f tokens5 %%a in (netstat -ano ^| findstr :!PORT! ^| findstr LISTENING) do ( set CONFLICT_PID%%a for /f tokens2 %%b in (tasklist /FI PID eq !CONFLICT_PID! ^| findstr PID) do ( echo WARNING: Port !PORT! is occupied by process !CONFLICT_PID! (%%b) call :log WARNING: Port !PORT! occupied by !CONFLICT_PID! (%%b) exit /b 1 ) ) goto :eof :get_nginx_pid :: 使用 wmic 获取 nginx.exe 的 PID更可靠 for /f skip1 tokens2 %%a in (wmic process where namenginx.exe get ProcessId 2^nul) do ( set NGINX_PID%%a goto :eof ) :: 如果 wmic 失败回退到 tasklist for /f tokens2 %%a in (tasklist /fi imagename eq nginx.exe ^| findstr nginx.exe) do ( set NGINX_PID%%a goto :eof ) set NGINX_PID goto :eof :wait_for_nginx set MAX_RETRY%~1 set RETRY0 :wait_loop timeout /t 1 /nobreak nul set /a RETRY1 call :get_nginx_pid if defined NGINX_PID goto :eof if !RETRY! lss %MAX_RETRY% goto :wait_loop exit /b 1 :wait_for_pid_exit set PID%~1 set SECONDS%~2 set COUNT0 :wait_exit_loop timeout /t 1 /nobreak nul set /a COUNT1 tasklist /FI PID eq %PID% | findstr PID nul if not errorlevel 1 ( if !COUNT! lss %SECONDS% goto :wait_exit_loop exit /b 1 ) goto :eof :log echo %~1 %LOG_FILE% if %DEBUG%true echo %~1 goto :eof :end endlocal3.4 关键设计点深度解析1start /Bvsstart的选择逻辑start /B启动的进程与当前 cmd 窗口共享标准输入输出但不创建新窗口start则创建独立窗口。对于后台服务我们希望无窗口模式/B避免用户误关窗口导致 nginx 退出但必须保证nginx.exe的 stdout/stderr 重定向到日志文件否则错误无法捕获。因此我们在nginx.exe启动命令中显式添加nul 21将输出丢弃——因为 Nginx 本身会把日志写入logs/error.log和logs/access.log不需要控制台输出。2wmic进程查询的容错机制wmic在某些精简版 Windows如 Server Core中可能缺失所以脚本内置 fallback先尝试wmic失败则用tasklist。tasklist的输出格式不稳定列宽变化但我们只取第二列PID用for /f tokens2精确提取规避了格式差异。3nginx -s quit的等待策略Nginx 官方文档说quit是“graceful shutdown”但没说明等待时长。实测发现空闲状态下quit通常在 100ms 内完成若有长连接如 WebSocket可能需 5~10 秒我们设TIMEOUT30是为了覆盖极端情况如客户端网络异常导致连接无法关闭。脚本中的:wait_for_pid_exit函数每秒轮询一次比ping -n 2 127.0.0.1 nul更精准且不依赖网络栈。4日志时间戳的跨平台兼容写法%date% %time%在不同系统区域设置下格式不同如2024/06/15vs06/15/2024。我们用powershell -Command Get-Date -Format yyyy-MM-dd HH:mm:ss生成统一格式但仅在:log函数中调用避免每次操作都启动 PowerShell 进程性能损耗。实际生产中若禁用 PowerShell则回退到%date:~0,4%-%date:~5,2%-%date:~8,2% %time:~0,2%:%time:~3,2%:%time:~6,2%字符串截取。4. 实战排错那些让你抓狂的 Windows Nginx 异常场景4.1 “nginx: [emerg] bind() to 0.0.0.0:80 failed (10013)” 的根因定位这个错误看似是端口被占实则 90% 情况是权限问题。Windows 的端口绑定权限由“Internet Protocol Helper”服务控制而该服务默认只允许管理员组成员绑定 1-1023 端口。但更隐蔽的是UAC用户账户控制的完整性级别。当你右键“以管理员身份运行”bat 时cmd 进程的完整性级别是High但start nginx.exe启动的子进程默认继承父进程的Medium级别UAC 降级。解决方案有两个方案 A推荐用start /high nginx.exe强制提升子进程级别start /high %NGINX_ABS_PATH% -c %NGINX_CONF% -p %NGINX_LOGS%/high参数要求调用者已是管理员否则报错正好形成权限校验闭环。方案 B关闭 UAC不推荐仅测试环境Computer Configuration Windows Settings Security Settings Local Policies Security Options User Account Control: Run all administrators in Admin Approval Mode设为Disabled。实测对比未加/high时nginx -t成功但nginx -c失败加/high后一切正常。这个细节网上 99% 的教程都忽略了。4.2 “nginx: [warn] could not build optimal server_names_hash” 的配置优化当server_name列表过长如上百个域名Nginx 会警告 hash 表不够大。这不是错误但影响性能。解决方案是在http块中添加http { server_names_hash_max_size 512; server_names_hash_bucket_size 64; ... }但 bat 脚本如何自动注入我们写一个辅助脚本fix_hash.batecho off set CONF%~1 set LINEserver_names_hash_max_size 512; findstr /c:server_names_hash_max_size %CONF% nul if errorlevel 1 ( powershell -Command (Get-Content %CONF%) -replace ^\s*http\s*\{, http {\n %LINE% | Set-Content %CONF% echo Fixed server_names_hash in %CONF% )原理用 PowerShell 读取文件找到http {行在其后插入配置行。findstr检查是否已存在避免重复添加。4.3 日志文件被占用导致 nginx 启动失败Windows 下若logs/access.log正被其他程序如文本编辑器、日志分析工具打开Nginx 会报open() /nginx/logs/access.log failed (20: Not a directory)。这不是权限问题而是句柄占用。解决方案启动前强制关闭句柄需 PsToolshandle.exe -p nginx.exe -c C:\nginx\logs\access.log | findstr pid handle.exe -p nginx.exe -c C:\nginx\logs\access.log -c但handle.exe需单独下载增加部署复杂度。更优雅的做法启动时重命名旧日志在nginx_ctl.bat的:do_start函数开头添加if exist %NGINX_LOGS%\access.log ren %NGINX_LOGS%\access.log access_%date:~-4,4%%date:~-10,2%%date:~-7,2%.log if exist %NGINX_LOGS%\error.log ren %NGINX_LOGS%\error.log error_%date:~-4,4%%date:~-10,2%%date:~-7,2%.log这样每次启动都生成新日志文件彻底规避占用问题。缺点是旧日志不会自动压缩需另写清理脚本。4.4 bat 脚本在中文路径下的乱码问题当nginx.conf路径含中文如C:\项目\nginx\conf\nginx.confnginx -t会报nginx: [emerg] unknown directive ...。这是因为 Windows cmd 默认代码页是 GBK而 Nginx 内部用 UTF-8 解析配置。解决方案启动前切换代码页chcp 65001 nul %NGINX_ABS_PATH% -t -c %NGINX_CONF% -p %NGINX_LOGS% chcp 936 nul65001是 UTF-8 代码页936是 GBK。但某些老版本 Nginx 不支持 UTF-8 路径需升级到 1.21。终极方案用短路径名8.3 格式for /f delims %%a in (dir /x %NGINX_CONF% ^| findstr nginx.conf) do set SHORT_CONF%%a set SHORT_CONF%SHORT_CONF:~28% %NGINX_ABS_PATH% -t -c %SHORT_CONF% -p %NGINX_LOGS%dir /x输出包含短路径名如NGINX~1.CONNginx 能正确识别。5. 进阶技巧让 bat 脚本具备“类服务”能力5.1 模拟 Windows 服务开机自启 故障自动恢复Windows 原生服务需要编写服务程序但我们可以用 Task Scheduler 模拟创建开机启动任务schtasks /create /tn Nginx AutoStart /tr %SCRIPT_DIR%nginx_ctl.bat start /sc onstart /ru SYSTEM /f/ru SYSTEM以系统账户运行避免用户登录态依赖/sc onstart在系统启动时触发。添加故障恢复Task Scheduler 本身不支持进程崩溃重启但我们可以写一个监控脚本watchdog.batecho off :loop tasklist /fi imagename eq nginx.exe | findstr nginx.exe nul if errorlevel 1 ( echo [%date% %time%] nginx crashed. Restarting... %SCRIPT_DIR%watchdog.log call %SCRIPT_DIR%nginx_ctl.bat start ) timeout /t 30 /nobreak nul goto :loop然后创建计划任务每 30 秒运行一次watchdog.bat。注意timeout在 Windows Server 2008 及以前需用ping -n 31 127.0.0.1 nul替代。5.2 将 bat 转为无黑窗 exeBat To Exe Converter 的避坑指南很多开发者想把 bat 打包成 exe 隐藏控制台但主流工具如 Bat To Exe Converter有严重缺陷工具问题替代方案Bat To Exe Converter打包后start /B失效nginx 进程无法后台运行改用Resource Hacker注入 manifest声明asInvoker权限Advanced BAT to EXE Converter对wmic命令支持差PID 获取失败改用tasklist 字符串解析兼容性更好自研 C 封装需编译增加维护成本用AutoIt编写nginx_wrapper.au3编译为 exe完美支持所有 bat 命令推荐AutoIt方案免费开源#include Process.au3 Run(ScriptDir \nginx_ctl.bat start, , SW_HIDE)编译后生成nginx_starter.exe双击即静默启动无任何窗口。5.3 多实例管理同一台机器跑多个 Nginx开发/测试/预发核心思路每个实例用独立端口、独立配置、独立日志。脚本只需支持传参nginx_ctl.bat start --instance dev nginx_ctl.bat start --instance test在config.ini中扩展[dev] nginx.path ..\nginx-dev\nginx.exe nginx.conf ..\conf-dev\nginx.conf nginx.logs_dir ..\logs-dev [test] nginx.path ..\nginx-test\nginx.exe nginx.conf ..\conf-test\nginx.conf nginx.logs_dir ..\logs-test然后修改:main函数根据--instance参数加载对应 section。这样一套脚本就能管理无限多实例比 Docker Compose 更轻量。6. 最后分享我在生产环境踩过的三个“反直觉”坑第一个坑nginx -s reload在 Windows 下永远比nginx -s stop nginx慢。你以为reload是热更新应该更快错。实测数据stopstart平均耗时 1.2 秒reload平均耗时 3.8 秒。原因reload必须等旧 worker 完全退出才启动新进程而stop后旧进程立即释放端口新进程可立刻绑定。所以高频更新配置时我改用restart而非reload。第二个坑daemon off;在 Windows 下是“伪配置”。
返回列表