我在IIS7配置Gzip压缩一文中介绍了IIS7对于gzip压缩的相关配置,以及默认情况下,由于IIS7将Javascript配置为动态压缩(Dynamic Compression),受CPU还有其他因素的影响,不一定会返回gzip压缩后的内容。 但是前阵子发现,尽管将Javascript配置成了静态压缩,请求Javascript脚本文件的时候偶尔还是会出现没有gzip的情况。这篇文章和大家分享一下我遇到的这个问题以及解决这个问题的过程和思路,希望对大家有所帮助。
“随机”的gzip行为
问题的症状非常简单而又奇怪。有时候连续访问脚本多次,刚开始没有gzip,再次访问又有gzip。等过好一会再去访问,gzip又没有了。有的时候则是无论怎么访问都不给我返回gzip后的响应。
问题的分析
排除了CPU负载过大的因素,因为发现当深夜机器CPU负载正常的时候也一样出现这样的问题。
在系统日志中发现以下Warning条目,引起了我的注意:
Warning 2010/4/13 6:32:41 IIS-W3SVC-WP 2264 None
The directory specified for caching compressed content C:\inetpub\temp\IIS Temporary Compressed Files\XXX is invalid. Static compression is being disabled.
通过日志筛选,发现从09年7月份就一直出现这样的Warning了,而且出现频率是隔1-3天左右。
这个文件夹是IIS用于存放压缩后的静态内容缓存。按照上面日志的意思,是说这个目录出了问题,导致静态压缩功能被禁用了。但是我明明可以访问这个文件夹,而且里头确实还放着缓存的脚本。
IIS论坛上提这个问题的帖子很多,但最终得到解答的没看到。不过其中一些人的回复倒是给了我不少思路。其中有人说道可能是文件夹权限问题,我看了看那个压缩缓存目录,上面的权限的确是应用程序池的身份,没有问题。使用ProcessMonitor监测了w3wp.exe进程的活动情况,没有看到这个进程在访问文件系统时出现错误。
后来在网上看到了Kanwaljeet Singla(IIS开发团队成员)的一篇文章,介绍IIS7相比IIS6在压缩模块上的改动,文中提到了一点,从IIS7开始,为了降低开销,IIS只对那些频繁访问的静态资源启用压缩。通过IIS7新引入的两个配置选项frequentHitThreshold和frequentHitTimePeriod,我们可以设定,当某个资源在frequentHitTimePeriod时间内被连续访问了frequentHitThreshold次,那么就算是“频繁访问”了。默认的配置是10秒钟内连续对同一Url发起2次请求就算此Url对应的资源属于频繁访问的资源,IIS服务器就会对其进行压缩,然后丢入缓存目录。
这也就解释了为什么有时候第一次请求脚本的时候,服务器没有返回gzip压缩后的内容,而是等你接着下一次访问的时候才会返回gzip响应。
但是按照这篇文章提到的IIS的设计理念,一旦资源被压缩缓存过,那么以后就不会再应用这个“频繁访问”的检测逻辑了,因为毕竟已经没有压缩开销了。而我非常确信这个脚本已经被缓存在服务器硬盘上了,那为什么我过一段时间再去访问的时候还是没有gzip呢?
在IIS7中有一个功能,叫做失败请求跟踪(Failed request tracing),允许技术人员跟踪那些出问题的请求在IIS内部都发生了什么事。为了揭开我的疑问,我对此网站开启了失败请求跟踪。
IIS7的Bug?!
打开IIS7管理器,定位到相应的网站右侧的操作面板,开启“失败请求跟踪”。默认跟踪日志文件是存放在%SystemDrive%\inetpub\logs\FailedReqLogFiles目录中。
然后在功能面板中找到“失败请求跟踪规则”进行配置:
为了防止其他脚本的干扰,在这里我限定了只跟踪测试的脚本
响应状态填写200即可
接下来是选择跟踪提供程序,也就是给你提供跟踪日志源的程序,由于我们的脚本只经过静态文件处理器,因此这里我们只需要选择www server处理程序即可。
然后我们就可以去测试一下访问出问题的脚本了。一旦IIS中途处理遇到些问题,那么就会以xml形式记录下相关的日志。打开FailedReqLogFiles目录,里头会有一些fr000001.xml这样格式的文件,每个文件代表一次失败请求。我们打开这个文件,查找compression关键词,发现了以下内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
< System >
< Provider Name = "WWW Server" Guid = "{3A2A4E84-4C21-4981-AE10-3FDA0D9B0F83}" />
< EventID >0</ EventID >
< Version >1</ Version >
< Level >4</ Level >
< Opcode >3</ Opcode >
< Keywords >0x40</ Keywords >
< TimeCreated SystemTime = "2010-05-13T16:59:08.086Z" />
< Correlation ActivityID = "{00000000-0000-0000-FD2B-008041000061}" />
< Execution ProcessID = "10752" ThreadID = "14504" />
< Computer >DMZ</ Computer >
</ System >
< EventData >
< Data Name = "ContextId" >{00000000-0000-0000-FD2B-008041000061}</ Data >
< Data Name = "Reason" >14</ Data >
</ EventData >
< RenderingInfo Culture = "zh-CN" >
< Opcode >STATIC_COMPRESSION_NOT_SUCCESS</ Opcode >
< Keywords >
< Keyword >Compression</ Keyword >
</ Keywords >
< freb:Description Data = "Reason" >NOT_FREQUENTLY_HIT</ freb:Description >
</ RenderingInfo >
< EventGuid >{E60CEE96-4472-448D-A13C-2170B18220EC}</ EventGuid >
</ ExtendedTracingInfo > </ Event > |
在上面,我们看到了NOT_FREQUENTLY_HIT的字样,看上去响应没有gzip的原因,还是因为没有达到“频繁访问”的要求,即使请求的资源已经被压缩过然后存放在硬盘上了。似乎这个压缩缓存是有一定的时效的,就是过了多长时间就得重新验证一下这个“频繁访问”的逻辑了。
我又进行了大量的测试,发现这个失效时间大约是在5分钟。也就是说,当你频繁访问一个资源,IIS开始对其进行压缩并存放在本地压缩缓存目录之后,大约过5分钟,你再去访问,还是会重新执行“频繁访问”这个验证逻辑。
为了证实我得到的结论,我在推特上向Kanwaljeet Singla(他的推特是@kjsingla)询问了这个问题。出乎我的意料,他很快就回复了我,估计是因为我在做这个测试的时候已经半夜了,他那边刚好是白天的缘故吧。他经过测试,证实“频繁访问”的逻辑检测发生在“响应是否已经压缩”逻辑之前,这确实是IIS7/7.5的一个Bug。
解决办法倒是很简单,只需要将“频繁访问”定义得更宽泛一些即可。例如1分钟内1次访问。
1
2
3
4
5
|
< configuration >
< system.webServer >
< serverRuntime frequentHitThreshold = "1" frequentHitTimePeriod = "00:01:00" />
</ system.webServer > </ configuration > |
问题真的解决了吗?
当我以为问题圆满解决,满意的睡着之后,第二天测试的时候发现请求脚本又没有gzip了,而且这次是连着访问好几次都没有压缩,这没法用我昨晚得到的结论去解释。让我非常郁闷-_-|||
冷静!
在系统日志中,我又看到了压缩目录无效的警告信息了,但这次,这个压缩目录是真没了。回想起那个时刻做的操作,当时运营的网站抛出未处理的异常,我修改完这个异常就把应用程序池给重启了。难道是说,当网站出现异常的时候,如果强制回收应用程序池的话,会导致静态压缩目录被删除?更奇怪的是,当我再次回收应用程序池,发现这个压缩目录又被重新创建了。
为了验证我的猜想,我又重复了这个过程,让网站抛出异常=>重启应用程序池=>再次请求脚本,问题重现;我又新建了一个一摸一样配置的测试网站,同样的测试流程,却发现压缩目录正常,没有出现上述的症状。
经过细致的对比,我发现运营的网站和我测试的网站环境还是不大一样的。运营的网站时时刻刻都有外部的请求,而且还很频繁。而测试网站不会有任何干扰的流量。
为了模拟真实的情况,我用iMacro写了一个宏,不间断的向测试网站请求该脚本。一开始,可以看到压缩目录确实是存在的,然后我在中途手动回收了应用程序池。发现压缩缓存目录确实被删除了。
经过再细致的测试,我最终得到一个结论:
在IIS7/7.5中,如果为应用程序池配置了administrator的账号权限,那么如果在应用程序池处理静态文件压缩的时候回收应用程序池,会导致压缩缓存目录被删除。直到下次应用程序池健康重启的时候,此压缩缓存目录才会被重新创建。
这个结论我还没有得到IIS团队的证实。但在我几台机器上的IIS7和7.5测试的结果确实都是这样的。联想之前的系统日志中,每隔1-3天就出现这样的问题,刚好是因为IIS7默认的回收间隔是1740分钟,也就是1天5小时。我计算了一下系统日志中的所有关于静态压缩目录警告出现的时间,和这个回收间隔的倍数非常吻合。
解决办法有两个:
- 如果没有必要,则不要给应用程序池配置administrator的账号,使用内置的账号不会出现这样的问题。
- 禁用固定时间间隔回收,改成在一个相对空闲的固定时刻回收,例如半夜4点。