Skip to content

IIS 运维与开发实践指南 ​

IIS (Internet Information Services) 是 Windows 平台核心所在的 Web 服务器。对于 .NET 开发人员和系统运维人员,掌握 IIS 的底层机制、性能调优和日志分析是确保线上业务稳定运行的关键。

1.1 核心组件与请求流转 ​

IIS 的演进经历了从早期的经典模式到现代集成模式的重大变革,理解其底层工作原理对于解决复杂的性能和兼容性问题至关重要。

1.1.1 核心组件角色 ​

  • HTTP.SYS (内核模式驱动):
    • 作为 Windows TCP/IP 网络子系统的一部分,运行在 Kernel Mode。
    • 职责:侦听 HTTP 请求(来自于 TCPIP.sys),并根据 URL 将请求转发给正确的应用程序池(Application Pool)。
    • 应答缓存:HTTP.SYS 内部建立了一个缓冲区,将最近的请求结果保存起来。对于命中缓存的请求,直接在内核模式下完成响应,无需切换到用户模式,性能极高。
    • 可靠性:不运行用户代码。即使工作进程崩溃,HTTP.SYS 仍能继续接收并排队请求,用户只会感到延迟而不会直接连接失败。
  • Worker Process (w3wp.exe):
    • 实际承载站点的容器,运行在用户模式。
    • 内部加载 ISAPI 扩展(如 aspnet_isapi.dll)并托管 CLR 环境,负责执行 ASP.NET 托管代码。
  • WAS (Web Admin Service):
    • 监控程序,从 applicationHost.config 读取配置。
    • 职责:负责创建、监视和回收工作进程。它监控 w3wp.exe 的健康状况,必要时关闭老进程并启动新进程(回收机制)。

1.1.2 IIS 历史架构对比 (5.0 vs 6.0 vs 7.0+) ​

版本监听机制隔离模式核心改进
IIS 5.0Inetinfo.exe (用户模式)进程内/外隔离 (DLLHost.exe)稳定性较差,进程崩溃易导致全局崩溃。
IIS 6.0HTTP.SYS (内核模式)工作进程隔离模式引入内核模式监听,可靠性大幅提升。
IIS 7.0+HTTP.SYS (内核模式)集成管道模式彻底统一 ASP.NET 与 IIS 管道,支持 C# 扩展。

1.1.3 请求处理流转过程 ​

  1. 监听:HTTP.sys 拦截来自外部的 HTTP 请求。
  2. 联系 WAS:HTTP.sys 联系 WAS 以获取配置信息(来自 applicationHost.config)。
  3. 配置驱动:WAS 向配置存储中心请求配置信息,WWW 服务使用配置信息去配置 HTTP.sys 的处理策略。
  4. 进程创建:WAS 为请求创建一个工作进程(如果不存在的话)。
  5. 响应:工作进程处理请求并将结果发回给 HTTP.sys,由其最终返回给客户端。

2. 托管管道模式:经典 vs 集成 ​

IIS 7.0+ 提供了两种管道模式,这是运维中经常遇到的兼容性分水岭。

2.1 经典模式 (Classic Mode) ​

  • 兼容性:与 IIS 6.0 保持一致。ASP.NET 作为一个 ISAPI 插件挂载在 IIS 管道中。
  • 局限性:IIS 管道与 ASP.NET 管道是分离的。只有显式映射到 ASP.NET 的扩展名(如 .aspx)才会触发托管代码。
  • 处理程序:在“处理程序映射”中,可以看到 aspx 类型对应的处理程序是 aspnet_isapi.dll。

2.2 集成模式 (Integrated Mode - 推荐) ​

  • 原理:将 ASP.NET 管道彻底融入 IIS 管道。所有请求(即使是 .html 静态页)都会经过统一的管道阶段。
  • 优势:
    • 高度集成:不再通过 ISAPI 转换,提高了速度和稳定性。
    • 全量拦截:允许在 ASP.NET 中编写 Module 来改变 IIS 的行为(例如对静态资源进行统一的权限验证)。
    • 处理程序:aspx 类型不再对应 DLL,而是对应 System.Web.UI.PageHandlerFactory 类型。

3. ASP.NET 内部运行机制与生命周期 ​

当请求进入托管环境(.NET Runtime)后,会经历一系列复杂的驱动过程。

3.1 核心对象创建 ​

  1. HostingEnvironment:.NET Runtime 准备宿主环境。
  2. AppDomain 分配:ApplicationManager 为请求动态分配 AppDomain。
  3. ISAPIRuntime 接管:通过 ProcessRequest() 得到 HttpWorkerRequest 对象。
  4. HttpRuntime 启动:创建 HttpContext(包含 Request 和 Response 对象)并获取 HttpApplication 实例。

3.2 管道事件 (Pipeline Events) ​

ASP.NET 处理请求会触发一系列标准的管道事件,这是编写 HttpModule 的核心切入点:

顺序事件名称说明
1BeginRequest请求开始,这是最早被触发的事件。
2AuthenticateRequest身份验证开始,确认用户身份。
3PostAuthenticateRequest身份验证完成。
4AuthorizeRequest权限授权检查,判断用户是否有权访问资源。
5ResolveRequestCache检查缓存,如果命中则直接跳到 EndRequest。
6PostResolveRequestCache缓存检查完成。
7MapRequestHandler根据扩展名查找对应的 Handler。
8AcquireRequestState获取 Session 状态,此时可以访问 Session 对象。
9PreRequestHandlerExecute执行 Handler 之前,最后修改 Header 的机会。
10ProcessRequest核心阶段:执行具体页面逻辑 (aspx/ashx Handler)。
11PostRequestHandlerExecuteHandler 执行完毕。
12ReleaseRequestState释放 Session 状态并保存。
13UpdateRequestCache更新输出缓存。
14EndRequest请求结束,资源清理。

4. 安装与初始配置 ​

4.1 角色安装要点 ​

在 Windows Server 环境下,安装 IIS 时需注意:

  • .NET Framework:必须勾选对应的版本(3.5 或 4.x)。
  • CGI/ISAPI:如果运行 PHP 或传统 ISAPI 程序,必须手动勾选。
  • 性能组件:勾选“静态内容压缩”和“动态内容压缩”以节省带宽。

4.2 跨域 (CORS) 精细化配置 ​

除了简单的全局 * 配置外,生产环境通常需要多域名支持。

方案:利用 URL Rewrite 插件动态注入标头

xml
<rewrite>
  <outboundRules>
    <rule name="AddCrossDomain">
      <match serverVariable="RESPONSE_Access_Control_Allow_Origin" pattern=".*" />
      <conditions logicalGrouping="MatchAll">
        <add input="{HTTP_ORIGIN}" pattern="(http(s)?://(domain1\.com|domain2\.com|localhost:8080))" />
      </conditions>
      <action type="Rewrite" value="{C:0}" />
    </rule>
    <rule name="AddExposeHeaders">
      <match serverVariable="RESPONSE_Access-Control-Expose-Headers" pattern=".*" />
      <action type="Rewrite" value="Content-Disposition" />
    </rule>
  </outboundRules>
</rewrite>

4.3 HTTPS 证书安装 (MMC 方式) ​

  1. 导入证书:
    • 运行 mmc,添加“证书”管理单元(选择“计算机帐户”)。
    • 定位到“个人 -> 证书”,右键选择“所有任务 -> 导入”。
    • 选择 .pfx 文件并输入密码(勾选“将此密钥标记为可导出的”以便备份)。
  2. IIS 绑定:
    • 在站点“编辑绑定”中添加 https,端口 443。
    • 勾选“需要服务器名称指示 (SNI)”(如果单 IP 托管多个证书)。

5. 性能优化:彻底解决“首次访问慢” ​

5.1 应用程序初始化 (Preload) ​

针对高频业务系统,IIS 默认的“按需加载”策略会导致重启后首个用户访问极慢。

  1. 应用程序池:Start Mode 设置为 AlwaysRunning,Idle Time-out 设置为 0。
  2. 站点配置:Preload Enabled 设置为 True。

5.2 重叠回收与 Web 园 (Web Garden) ​

  • 重叠回收 (Overlapped Recycle):回收时先起新进程再关旧进程,确保服务不断档。旧进程处理完剩余请求后自动关闭,若超时仍未关闭则强制终止。
  • Web 园 (Web Garden):将“最大工作进程数”设置为 >1。
    • 优势:多核 CPU 利用率更高,单个进程死锁不影响全站。
    • 避坑:必须使用外部 Session 存储(Redis/SQL),否则 Session 会因进程随机切换而丢失。

5.3 运行状况监控 (Health Monitoring) ​

为了确保应用程序池的稳定性,建议配置以下监控项:

  • Ping 机制:W3SVC 定期对工作进程执行 Ping 操作,以获取其存活状态。
  • 快速失败保护 (Rapid Fail Protection):如果在指定时间段内(如 5 分钟),失败次数达到阈值(如 5 次),则自动禁用该应用程序池,防止持续崩溃拖垮系统。
  • CPU 监视:限制单个应用程序池的 CPU 占用率,防止某个“僵尸进程”耗尽服务器资源。

5.4 Web.config 性能优化建议 ​

通过在 Web.config 中进行微调,可以显著提升静态资源的响应速度:

  • 启用静态内容过期:减少客户端重复请求。
  • HTTP 压缩:对文本类资源进行 Gzip/Brotli 压缩。
xml
<system.webServer>
  <staticContent>
    <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
  </staticContent>
  <urlCompression doStaticCompression="true" doDynamicCompression="true" />
</system.webServer>

6. 日志分析:运维的“上帝视角” ​

6.1 核心字段与 Log Parser 工具 ​

在“日志 -> 选择字段”中,务必勾选 time-taken、sc-win32-status、sc-bytes 和 cs-bytes。

6.1.1 导入 SQL Server 分析 ​

推荐使用 Log Parser 将日志转为数据库表,方便进行深度统计:

bash
logparser.exe "SELECT TO_LOCALTIME(TO_TIMESTAMP(ADD(TO_STRING(date, 'yyyy-MM-dd '), TO_STRING(time, 'hh:mm:ss')), 'yyyy-MM-dd hh:mm:ss')) AS RequestTime, * INTO MyWebLog FROM u_ex*.log" -i:IISW3C -o:SQL -oConnString:"Driver={SQL Server};server=.;database=LogDB;Integrated Security=SSPI" -createtable:ON

6.2 深度排障 SQL 实践 ​

1. 寻找性能瓶颈 (time-taken) ​

time-taken 定义为:从服务器收到第一个字节开始,到发送完最后一个字节为止的总时间(包含网络传输)。

sql
-- 查看最慢的 20 个页面请求
SELECT TOP 20 cs-uri-stem, AVG(time-taken) as AvgTime, MAX(time-taken) as MaxTime, COUNT(*) as HitCount
FROM MyWebLog
WHERE cs-uri-stem LIKE '%.aspx'
GROUP BY cs-uri-stem ORDER BY AvgTime DESC

2. 用户流失与网络分析 (sc-win32-status = 64) ​

  • 状态码 64:指定的网络名不再可用。通常意味着客户端在服务器发送完响应前主动断开了连接。
  • 分析:如果某个 URL 的 64 错误占比很高,通常暗示该页面处理太慢,用户等不及就关闭了浏览器。
sql
SELECT cs-uri-stem, COUNT(*) as AbandonCount
FROM MyWebLog
WHERE sc-win32-status = 64
GROUP BY cs-uri-stem ORDER BY AbandonCount DESC

3. 系统级别状态码对照表 (sc-win32-status) ​

通过 net helpmsg [code] 可以查询具体含义:

状态码含义运维建议
0操作成功完成正常请求。
64指定的网络名不再可用客户端主动断开连接(如关掉浏览器)。
121信号灯超时时间已到传输超时,检查网络质量或大文件处理。
1236本地系统已断开网络连接服务器端网络中断或负载过高导致丢包。

4. ViewState 与带宽优化 (sc-bytes) ​

如果 sc-bytes(服务端发送字节数)异常大,应检查页面是否滥用了 ViewState 或未进行分页。

sql
SELECT TOP 10 cs-uri-stem, AVG(sc-bytes) as AvgSize, AVG(time-taken) as AvgTime
FROM MyWebLog
GROUP BY cs-uri-stem ORDER BY AvgSize DESC

7. 安全运维与 WebShell 溯源 ​

7.1 溯源排查 SOP ​

  1. 文件落地线:扫描关键时间窗内新增的 .aspx/.ashx/.config 文件。
  2. 配置审计线:检查 applicationHost.config 是否被篡改,新增了异常的虚拟目录。
  3. 执行轨迹线:检查 IIS 日志中是否有针对可疑文件的 POST 请求。特别注意 time-taken 极短且 sc-status=200 的异常请求。

7.2 权限加固红线 ​

  • 上传目录:必须在 IIS 中取消“脚本”执行权限,仅保留“读/写”。
  • ACL 最小化:仅授权 IIS AppPool\YourApp 账号,严禁使用 Everyone 或 NetworkService 这种大范围权限。

8. 避坑指南与架构建议 ​

  • 慎用 Iframe:time-taken 无法统计包含 iframe 的完整页面加载时间,因为子页面是独立的 HTTP 请求。
  • 避免 PostBack:WebForms 的所有操作都在同一个 URL 上,导致日志分析无法区分具体业务动作。建议在 URL 中显式区分 Action。
  • 定期清理日志:IIS 日志默认不自动清理,长期运行会导致磁盘爆满。建议编写脚本定期压缩或迁移旧日志。

9. 参考资料 ​

方舟 · Ark