案例分析:session丢失及appdomain回收
原文地址:http://blogs.msdn.com/tess/archive/2006/08/02/asp-net-case-study-lost-session-variables-and-appdomain-recycles.aspx
版权归原作者所有,转载请注明出处!
案例分析:session丢失及appdomain回收
在进入细节之前,先看两个问题
appdomain回收时发生了什么?
appdomain回收的原因是什么?
asp.net中每一个应用程序都运行于自己的appdomain中,举个例子,假设你有如下的站点结构:
WebSite root
/HrWeb
/EmployeeServices
/FinanceWeb
/SalesWeb
每一个子目录都配置为应用程序,那么asp.net进程中会有如下的appdomain:
System Domain
Shared Domain
Default Domain
Root
HrWeb
EmployeeServices
FinanceWeb
SalesWeb
除了前三个比较特殊之外,其他的每个都包含应用程序相关的数据,包含的具体内容对本文主题来说没什么价值
所有应用程序相关的装配件
一个HttpRuntime对象
一个Cache对象
当appdomain卸载时所有上面所说的都将丢失,这意味着下一次请求到来时所有的装配件都将被重新加载,代码重新JIT,cache以及任何inproc模式的session等都被清空。这会带来很大的性能冲击,可以想象,保证appdomain不频繁回收非常重要.
appdomain为什么会回收
以下任意情况发生,appdomain都会卸载:
1.Machine.Config, Web.Config or Global.asax被修改
2.bin目录或里面的内容被修改
重编译次数(aspx, ascx or asax)达到了配置文件中(machine.config or web.config中的<compilation 3.numRecompilesBeforeAppRestart=/>设置)指定的次数(默认值为15)
4.虚礼目录的路径被修改
5.CAS(Code Access Security:代码访问安全)策略被修改
6.web服务重启
7.应用程序子目录被删除(仅.net2.0),参见Todd的博客: http://blogs.msdn.com/toddca/archive/2006/07/17/668412.aspx
可能漏掉了一些2.0方面的,但我希望上面覆盖了绝大部分场景
我会更多的关注其中的一些问题,尤其是看起来比较普遍的
未知的config或者bin目录更改
你发誓没有人动过这些, 但在开始剖析时(稍后会展示),导致回收的原因正是配置改变
基本上,Dr. Watson.. 等其他的程序动过它们,其他如病毒扫描或者备份软件或索引服务也是常见原因.他们并没有改动文件的内容,但他们会改动文件的属性,足以触发文件改动监视器发出通知。
如果是病毒扫描造成的,你也许应该考虑将文件夹排除在实时扫描范围外,在仔细确认没人可以访问之后可以安装任何杀毒软件到这些目录
中等到重负载时站点更新
场景:有一个10个装配件的应用,需要更新一些项目下的装配件,有一个机制可以让你在运行时更新装配件,在高负载的情况下做更新的话,再想想?
更新10个文件中的7个,假定需要大概7秒,7秒钟重之内有3个请求,那么你会见到如下的情况:
第1秒:a,b装配件更新到新版本- appdomain开始卸载(任何未决的请求将在完全卸载前完成)
第2秒:1号请求到达,加载一个有2个更新文件的appdomain
第3秒:c被更新 --appdomain开始卸载(任何未决的请求必须在卸载之前完成)
第4秒:d被更新
第5秒:2号请求到达,加载一个新的appdomain,包含4个更新的文件以及三个未更新的
第6秒:e,f更新 ----appdomain开始卸载(任何未决的请求必须在卸载之前完成)
第7秒:f更新
第8秒:3号请求到达,一个包含7个更新过文件的的appdomain加载
看到了没,发生了很多不好的事情
首先,总共发生了三次appdomain重启而不是你认为的一次,因为asp.net不知道什么时候会完成更新.
其次,1号请求和2号请求执行的是部分更新的dll,如果dll依赖于其他也需要被更新的dll,就可能会生成一系列错误,你应该已经想到了这种情况
再次:可能会收到"无法访问装配件,因为文件正在被其他进程使用",因为在映射拷贝时dll被锁定了(参见: http://support.microsoft.com/kb/810281)
换言之:在运行状态下不要进行批量更新
那么,难道这个功能没有用么?不,如果希望更新一个dll,上面的任何一种情况都不会发生,如果在低负载或者没有负载的情况批量更新下很可能不会碰到这些问题,但是如果你希望批量更新dll,应该先关闭应用程序
有一个办法可以避免上面的问题,如果你确定一定要在负载下更新,上面提到的KB略叙了这个办法
.net 1.1 引入了两个新的设置项:<httpRuntime waitChangeNotification= /> 、 <httpRuntime maxWaitChangeNotification= />.
waitChangeNotification用于指定在下一个请求触发appdoamin重启时应该等待多少秒,也就是说,第一秒更新了dll,新请求在第三秒到来,waitChangeNotification设置为5,那么需要等到第八秒(首先是1+5=6秒,之后变为3+5)在新请求获得新的appdomain之前,在第二秒的请求会继续在旧的appdomain中被处理(这个时间是滑动的,所以它总是在最后一次更新后等待5秒)
maxWaitChangeNotification用于指定从第一次请求到来之后等待的最大时间.如果我们将其设置为10,在上面描述的情况中,我们在第一秒和第三秒进行了更新,在第8秒waitChangeNotification超时,将仍然会使用一个新的appdomain来处理请求.如果我们设置为6,那么在第七秒就已经加载了新的appdomain,因为此时maxWaitChangeNotification已经超时.所以这是一个绝对过期而不是滑动过期...并且maxWaitChangeNotification and waitChangeNotification中最小的那个来决定是否回收.
在本文中提到的场景,可以设置waitChangeNotification为3,maxWaitChangeNotification=10来避免多次回收
我知道这样解释或许有点混乱,但希望你明白大致意思
在捣鼓这些设置的时候,还有一些重要的事项
如果不设置那么默认为0
maxWaitChangeNotification必须大于等于waitChangeNotification
如果设置值均大于0,在达到超时时间之前,将看不到任何变化,看起来就像设置和dll被缓存了
重编译
一个常见的场景:有一组aspx页面(包含一些显示新闻的项或者其他),后台有一个内容编辑器周期性的更新这些页面.每一次你更新一个aspx页面都必须重新编译,同样是因为asp.net仅仅知道有人更新了这些文件,没办法知道是代码更新还是简单的静态文本更新。
如果你读过我之前的文章你就会知道,在appdomain卸载之前,装配件不会被卸载.并且每次重编译将产生一个新的装配件,有个设置可以限制重新编译的次数,来避免产生过多的装配件(也是为了限制在这上面的内存使用).默认值为15.
如果页面内容需要持续更新,,建议动态从数据库或文件获取内容而不是更新aspx页面文件.或者在HTML页面中用frame作为替代.
如何确认应用被回收?
如果遭遇chahe或者session丢失,基本上赌对了,但为了确认,可以查看ASP.NET v…/Application Restarts计数器.
如何确认appdomain重启的原因?
asp.net 2.0可以用内置的Health Monitoring Events来记录应用程序重启的原因.修改C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/CONFIG目录下的主web.config文件,加入下面的节点:
<add name="Application Lifetime Events Default" eventName="Application Lifetime Events"
provider="EventLogProvider" profile="Default" minInstances="1"
maxLimit="Infinite" minInterval="00:01:00" custom="" />
web.config的更改会产生如下的事件:
Event Type: Information
Event Source: ASP.NET 2.0.50727.0
Event Category: Web Event
Event ID: 1305
Date: 2006-08-02
Time: 13:33:19
User: N/A
Computer: PRATHER
Description:
Event code: 1002
Event message: Application is shutting down. Reason: Configuration changed.
Event time: 2006-08-02 13:33:19
Event time (UTC): 2006-08-02 11:33:19
Event ID: 6fc2b84de5b74b5ba65b21804d18b7bf
Event sequence: 8
Event occurrence: 1
Event detail code: 50004
Application information:
Application domain: /LM/w3svc/1/ROOT/DebuggerSamples-9-127989919076505325
Trust level: Full
Application Virtual Path: /DebuggerSamples
Application Path: c:/inetpub/wwwroot/DebuggerSamples/
Machine name: PRATHER
Process information:
Process ID: 4876
Process name: w3wp.exe
Account name: NT AUTHORITY/NETWORK SERVICE
Custom event details:
For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.
可以捕获很多事件,甚至可以撰写自己的event和provider.想得到更多信息以及更多可用的事件,查看下面的文章:http://msdn2.microsoft.com/en-us/library/ms228103.aspx
asp.net 1.1可以使用反射来获得关闭信息(在2.0一样可以实现)
如果对细节不感兴趣仅仅想记录日志,查看scottgu的博客
如果希望知道所有的细节,下面告诉你怎么做
正如之前提到的:每个域拥有一个 HttpRuntime 对象
0:014> !do 0x04f6f324
Name: System.Web.HttpRuntime
MethodTable 0x00e39df4
EEClass 0x0b608028
Size 116(0x74) bytes
GC Generation: 2
mdToken: 0x02000078 (c:/winnt/assembly/gac/system.web/1.0.5000.0__b03f5f7f11d50a3a/system.web.dll)
FieldDesc*: 0x00e3955c
MT Field Offset Type Attr Value Name
0x00e39df4 0x4000680 0x4 CLASS instance 0x00000000 _namedPermissionSet
0x00e39df4 0x4000681 0x8 CLASS instance 0x01031904 _fcm
0x00e39df4 0x4000682 0xc CLASS instance 0x01031b64 _cache
0x00e39df4 0x4000683 0x54 System.Boolean instance 0 _isOnUNCShare
0x00e39df4 0x4000684 0x10 CLASS instance 0x01033c88 _profiler
0x00e39df4 0x4000685 0x14 CLASS instance 0x01033ca4 _timeoutManager
0x00e39df4 0x4000686 0x18 CLASS instance 0x0104ded4 _requestQueue
0x00e39df4 0x4000687 0x55 System.Boolean instance 0 _apartmentThreading
0x00e39df4 0x4000688 0x56 System.Boolean instance 0 _beforeFirstRequest
0x00e39df4 0x4000689 0x60 VALUETYPE instance start at 0x010318c8 _firstRequestStartTime
0x00e39df4 0x400068a 0x57 System.Boolean instance 1 _firstRequestCompleted
0x00e39df4 0x400068b 0x58 System.Boolean instance 0 _userForcedShutdown
0x00e39df4 0x400068c 0x59 System.Boolean instance 1 _configInited
0x00e39df4 0x400068d 0x50 System.Int32 instance 0 _activeRequestCount
0x00e39df4 0x400068e 0x5a System.Boolean instance 0 _someBatchCompilationStarted
0x00e39df4 0x400068f 0x5b System.Boolean instance 0 _shutdownInProgress
0x00e39df4 0x4000690 0x1c CLASS instance 0x00000000 _shutDownStack
0x00e39df4 0x4000691 0x20 CLASS instance 0x00000000 _shutDownMessage
0x00e39df4 0x4000692 0x68 VALUETYPE instance start at 0x010318d0 _lastShutdownAttemptTime
0x00e39df4 0x4000693 0x5c System.Boolean instance 1 _enableHeaderChecking
0x00e39df4 0x4000694 0x24 CLASS instance 0x01033e44 _handlerCompletionCallback
0x00e39df4 0x4000695 0x28 CLASS instance 0x01033e60 _asyncEndOfSendCallback
0x00e39df4 0x4000696 0x2c CLASS instance 0x01033e7c _appDomainUnloadallback
0x00e39df4 0x4000697 0x30 CLASS instance 0x00000000 _initializationError
0x00e39df4 0x4000698 0x34 CLASS instance 0x00000000 _appDomainShutdownTimer
0x00e39df4 0x4000699 0x38 CLASS instance 0x0104dc60 _codegenDir
0x00e39df4 0x400069a 0x3c CLASS instance 0x00fc3c48 _appDomainAppId
0x00e39df4 0x400069b 0x40 CLASS instance 0x00fc3ca4 _appDomainAppPath
0x00e39df4 0x400069c 0x44 CLASS instance 0x00fc3d8c _appDomainAppVPath
0x00e39df4 0x400069d 0x48 CLASS instance 0x00fc3d04 _appDomainId
0x00e39df4 0x400069e 0x4c CLASS instance 0x00000000 _resourceManager
0x00e39df4 0x400069f 0x5d System.Boolean instance 0 _debuggingEnabled
0x00e39df4 0x40006a0 0x5e System.Boolean instance 0 _vsDebugAttach
0x00e39df4 0x400067b 0 CLASS shared static _theRuntime
>> Domain:Value 0x0014af68:NotInit 0x0017cd60:0x04f6f324 0x002165d0:0x04fcb660 <<
0x00e39df4 0x400067c 0x4 CLASS shared static s_autogenKeys
>> Domain:Value 0x0014af68:NotInit 0x0017cd60:0x04f6ef28 0x002165d0:0x04fcb474 <<
0x00e39df4 0x400067d 0xc System.Boolean shared static s_initialized
>> Domain:Value 0x0014af68:NotInit 0x0017cd60:1 0x002165d0:1 <<
0x00e39df4 0x400067e 0x8 CLASS shared static s_installDirectory
>> Domain:Value 0x0014af68:NotInit 0x0017cd60:0x04f6f19c 0x002165d0:0x04fcb4d8 <<
0x00e39df4 0x400067f 0x10 System.Boolean shared static s_isapiLoaded
>> Domain:Value 0x0014af68:NotInit 0x0017cd60:1 0x002165d0:1 <<
HttpRuntime对象是静态的,要获得domain内某特定的HttpRuntime对象,可以dump任意HttpRuntime对象然后查看其静态成员_theRuntime...静态成员变量有一点特殊,当使用!do命令时,将得到一个列表而不是直接得到对象地址:
0x00e39df4 0x400067b 0 CLASS shared static _theRuntime
>> Domain:Value 0x0014af68:NotInit 0x0017cd60:0x04f6f324 0x002165d0:0x04fcb660 <<
列表说明0x0014af68的appdomain还没有初始化,0x0017cd60 的domain地址位于0x04f6f324,0x002165d0位于0x04fcb660
使用!dumpdomain命令可以获得domain的地址:
Domain 3: 0x2165d0
LowFrequencyHeap: 0x00216634
HighFrequencyHeap: 0x0021668c
StubHeap: 0x002166e4
Name: /LM/W3SVC/1/HrWeb-127976921852307107
从前面的列表可以得到一些信息:_debugginEnabled,可以查看debug是否为ture;可以得到cache对象的地址,有意思的是,这个案例的HttpRuntime对象包含了两个成员变量:_shutDownStack和_shutDownMessage在记录事件的时候可以加以利用
在 global.asax 的Application_End方法中,可以加入以下的代码以获得domain内的_theRuntime对象
HttpRuntime runtime = (HttpRuntime) typeof(System.Web.HttpRuntime).InvokeMember("_theRuntime", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetField, null, null, null);
接着获得shutDownMessage and shutDownStack的内容:
string shutDownMessage = (string) runtime.GetType().InvokeMember("_shutDownMessage", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField, null, runtime, null);
string shutDownStack = (string) runtime.GetType().InvokeMember("_shutDownStack", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField, null, runtime, null);
剩下的问题就是写日志和等待回收.
结束之前还有一些事情:如果有配置更改的通知,但你不确定谁动了这些文件,建议运行filemon来记录文件访问,这个工具在许多安全性和文件相关的问题上非常有用
案例分析:session丢失及appdomain回收相关推荐
- 内存回收导致关键业务抖动案例分析-论云原生OS内存QoS保障
蒋彪,腾讯云高级工程师,10+年专注于操作系统相关技术,Linux内核资深发烧友.目前负责腾讯云原生OS的研发,以及OS/虚拟化的性能优化工作. ## 导语 云原生场景,相比于传统的IDC场景,业务更 ...
- 计算机两个硬盘无法重启,双硬盘电脑非系统盘硬盘盘符重启丢失的案例分析
原标题:双硬盘电脑非系统盘硬盘盘符重启丢失的案例分析 昨晚,小编在工作了一段时间之后,重新启动计算机,突然发现两个数据盘分区没了,从计算机的磁盘管理里面看,硬盘分区还在,盘符没了.如下图所示: 我的计 ...
- 关于session丢失原因的分析
关于session丢失原因的分析 http://www.weiw.com 2003-3-29 伟网动力 很多session丢失的原因是因为错误的程序或者是错误的虚拟目录结构. SessionID ...
- 读书笔记之 大型网站技术架构(核心原理与案例分析)
前言 坚持看了十几天的书,终于完成了毕业后第一次静下心来,利用业务时间看书并做笔记的成就了.废话不多说,这回看的是一直很膜拜的李智慧大神写的大型网站技术架构-核心原理与案例分析. 简短的读后感 极其推 ...
- 45.JVM调优策略、常见问题:内存泄漏(年老代堆空间被占满、持久代被占满、堆栈溢出、线程堆栈满、系统内存被占满)优化方法:优化目标、优化GC步骤、优化总结;案例分析(公司系统参数、网上给的配置参数)
45.JVM调优策略 45.1.常见问题 45.1.1.内存泄漏 45.1.1.1.年老代堆空间被占满 45.1.1.2.持久代被占满 45.1.1.3.堆栈溢出 45.1.1.4.线程堆栈满 45. ...
- 《深入理解JVM.2nd》笔记(五):调优案例分析与实战
文章目录 概念 案例分析 高性能硬件上的程序部署策略 情景再现1 问题分析1 关于Full GC 使用64位JDK来管理大内存可能遇到问题 建立逻辑集群 使用逻辑集群可能遇到的问题 最后解决方案 集群 ...
- 【系统分析师之路】2016年系统分析师下午案例分析真题
[系统分析师之路]2016年系统分析师下午案例分析真题 系统分析师下午案例分析真题 [系统分析师之路]2016年系统分析师下午案例分析真题 2016年系统分析师下午案例分析第一题(系统规划) 我的解答 ...
- 【软考系统架构设计师】2017下系统架构师案例分析历年真题
[软考系统架构设计师]2017下系统架构师案例分析历年真题 2017下系统架构师案例分析历年真题 [软考系统架构设计师]2017下系统架构师案例分析历年真题 2017下系统架构师案例分析试题一(系统架 ...
- 软件工程个人作业-软件案例分析
软件工程个人作业-软件案例分析 目录 软件工程个人作业-软件案例分析 1. CSDN APP 1.1. 调研,评测 1.1.1. 软件简介 1.1.2. 优缺点分析 1.1.3. 软件 bug a. ...
最新文章
- CentOS 6.5升级Python后yum不可用的解决方案
- 元宇宙大比拼:英伟达Nvidia,Facebook,iwemeta
- Redis 常用命令学习三:哈希类型命令
- 【struts2+hibernate+spring项目实战】用户登录校验(struts拦截器)
- 读书笔记《单核工作法》1
- 基于HBASE的并行计算架构之rowkey设计篇
- python3.7.2怎么使用win7_Win7同时安装Python2和Python3的配置
- C语言位于30到100之间的一个奇数,《帮你度过C语言新手阶段》系列之三
- 外星人台式电脑_谈谈4万人民币的外星人R9台式机值不值得购买
- 深入浅出学Hive:Hive体系结构
- python flask上传文件_Python-Flask-文件上传
- 深度学习之图像识别基础篇——神经元与感知机
- html登录页面的校验控件,HTML5一款有趣智能的密码输入界面控件
- Leetcode每日一题:168.excel-sheet-column-title(Excel表名称)
- python 类self作用_self在Python中有什么用途?
- mac remix导入本地项目
- iphone长截图哪个软件好_iPhone上最好的长截图工具!
- 计算机C盘空间减少,为何我的C盘空间突然减少好几个G?
- 向量的加减(运算符重载)
- PHP 生成带logo二维码并修改背景颜色,利用画布布局二维样式