licongjie的博客

专心、专注、专业
随笔 - 26, 文章 - 1, 评论 - 207, 引用 - 1
数据加载中……

实战小技巧系列(2):关于IIS6.0中应用程序池圆设置的问题

  关于应用程序池:这是微软的一个全新概念:应用程序池是将一个或多个应用程序链接到一个或多个工作进程集合的配置。因为应用程序池中的应用程序与其他应用程序被工作进程边界分隔,所以某个应用程序池中的应用程序不会受到其他应用程序池中应用程序所产生的问题的影响。这个功能是IIS6.0里新增的,它可以使得不同的WEB应用程序运行在同一个IIS时,不被互相干扰,从而当其中一个WEB应用发生异常时,而其它的WEB应用程序还能够正常运行。

  关于WEB圆:默认情况下,创建一个应用程序池的时候,WEB圆的最大工作进程数为1,也就是在任务管理器中只有一个w3wp进程。而只有一个进程,它所能提供的资源是有限的,可并发性访问量也只能维持在一定的程度上,所以我们有时候为了提高资源的利用率,提高并发访问量,可以自定义WEB圆的最大工作进程数,这时候,会在任务管理器中发现对应的w3wp进程数。

  其中涉及问题:

  1、应用程序池只有IIS6.0及以上版本才有,从而也出现一个问题。那就是应用程序池会自动释放资源,也就是根据应用程序池设定的资源回收时间或超过设定空闲时间,把一些已经初始化的资源回收。

  我们在开发项目的时候,其中有一个项目需要用到定时器,一天运行一次,在单元测试的时候,一切正常,可一旦集成测试发现该定时器根本就不起作用,经多次试验,才发现由于24小时运行一次间隔太长,而集成测试时往往是今天设定一个值,然后第二天才来看最后的效果怎么样,这中间可能没有其它的人为的或其它行为触发,导致定时器启动后一段时间后,就又回收了,所以才不会再执行相应的功能。

  解决方法:

  该问题涉及到了应用程序池的资源回收机制,当然我们可以设定应用程序池中的资源回收时间和空闲时间来临时性解决这个问题,但总不是一个好的解决方案。为此,我们也请教过微软的一些专家,而得到的回复是,如果定时器要运行在WEB应用程序中,暂时没有好的解决方法。他们的建议是,另外创建一个桌面程序或者windows服务,把定时功能放在此中,再去调用WEB应用中的一些需要定时实现的功能。

  2、WEB应用程序一些常用数据的提取。

  一般的WEB应用程序,都会有一些配制性或系统级的数据,这些数据经常会应用到各个模块中,非常频繁。所以,我们都是在程序启动的时候就初始化好这些数据,保存在静态变量中,如下示意:

public class Config
{
public static string SiteName;
public static bool AllowArticleComments;
...........

public static void Init()
{
//从数据表中读取数据,初始化SiteName和AllowArticleComments变量
}

}

  然后在Global.asax中程序启动时执行Init()方法,预先把数据实始化到静态变量中。

protected void Application_Start(object sender, EventArgs e)
{

  Config.Init();

}

  这样,在其它模块需要用到这些数据的时候,只要通过Config.SiteName类似这样取得数据。 

  但我们发现程序运行一段时间后,会出现Config.SiteName的值为null,而有些整形的数据也变成了默认数据了,经过多次的试验最终发现也是由于资源的回收机制在搞鬼,资源回收后进行重新分配,程序不会自动去数据表中读取数据初始化了,所以导致这些数据变成了其本身的默认值了。

  解决方法:

  由于资源的回收,我们不能阻止这些数据被重新分配初始默认值。所以我们就考虑当重新分配资源时,再去读数据表数据,重新赋值,这个可以用Cache缓存来实现,如下:

public static UnitInfoInfo GetUnitInfo()
{
if (!enableCaching)
{
return new UnitInfo().GetByPK(1);
}

string key = "GetUnitInfo";
UnitInfoInfo data = (UnitInfoInfo)HttpRuntime.Cache[key];

if (data == null)
{
data = new UnitInfo().GetByPK(1);
if (data != null)
{
HttpRuntime.Cache.Add(key, data, null, DateTime.Now.AddHours(unitInfoTimeOut), Cache.NoSlidingExpiration, CacheItemPriority.High, null);
}

}

return data;
}


public static string siteName
{
get
{
UnitInfoInfo unitInfo = GetUnitInfo();
return unitInfo.SiteName;
}
}

  这样同样可以通过Config.SiteName来访问这些数据,当资源回收时,缓存里的数据就为null,则会再次去读数据库,解决了由于资源回收导致的初始值丢失的问题。

  3、由于2中所述的这些数据,我们一般都可以在管理后台中维护,当这些数据更改时,我们也希望这些缓存中的数据相应在的改变。这时我们或许可以通过以下方法来实现:

  HttpRuntime.Cache.Remove( "GetUnitInfo");

  当数据修改时,调用该方法,把缓存中的数据移掉,这样当重新调用Conifg.SiteName时,会再次去到数据库中读取数据。从而保持数据一致性。

  很庆幸,程序能够正常运行。但当我们把WEB圆的最大工作进程数改成大于1时,问题来了。当我们在后台修改SiteName数据后。发现,前台显示有时候是修改后的数据,有时候还是原来的数据。这是因当WEB圆的最大工作进程数大于1,如为2时,则该应用程序池会生成二个w3wp进程,这二个进程是相互独立的,访问网站时,资源的读取是从这二个w3wp中轮循访问的,从而可以达到一定的负载均衡。正由于这二个进程的独立性,当执行HttpRuntime.Cache.Remove( "GetUnitInfo");语句后,或许当时访问的进程是其中一个w3wp进程,我们暂定为w3wp(1),所以在w3wp(1)进程中该缓存是被移走了,当再次访问时,显示的数据就为修改后正确的数据。而w3wp(2)进程中的缓存还是没有改变,所以当轮循到该进程时,显示的数据为修改前非正确的数据。

  解决方法:

  所以当WEB圆的最大工作进程数大于1时,2中所提供的解决方法,有点不够用了。那用什么方法可以移除所有进程中的对应缓存呢?幸亏.net2.0中还提供了一种缓存,就是数据库依赖缓存。当数据表中某些数据变动后,会促使对应的缓存失效。为此,我们把2中的解决方法稍微做一修改:

public static UnitInfoInfo GetUnitInfo()
{
if (!enableCaching)
{
return new UnitInfo().GetByPK(1);
}

string key = "GetUnitInfo";
UnitInfoInfo data = (UnitInfoInfo)HttpRuntime.Cache[key];

if (data == null)
{
data = new UnitInfo().GetByPK(1);
AggregateCacheDependency cd = DependencyFacade.GetUnitInfoDependency();
if (data != null)
{
HttpRuntime.Cache.Add(key, data, cd, DateTime.Now.AddHours(unitInfoTimeOut), Cache.NoSlidingExpiration, CacheItemPriority.High, null);
}

}

return data;
}

  其中红色部分为新增语句或修改过的地方。

  4、关于WEB圆中最大工作进程数大于1时,Session值如何在各进程之间的共享问题,这个微软到已经提供了解决方案。

  解决方法:  

  在Web.Config中,有这样一行配制信息:

  <sessionState mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes" cookieless="false" stateNetworkTimeout="20"/>

  这里的mode属性值有InPorc和StateServer二种值,如果是InProc那么,Session的值是保存在进程中的,则无法在各个进程间共享了,我们可以设定值为StateServer,当然前提我们还必须到Windows服务中开启ASP.NET 状态服务,这样就可以解决进程间Session值共享问题了。

  可能大家都有自己的解决方法来解决以上碰到的问题,欢迎在此讨论,以便我们可以在现有的基础上做进一步的改进。

Technorati : , ,

posted on 2008-03-30 13:33 李.net 阅读(2295) 评论(9)  编辑 收藏

评论

#1楼    回复  引用  查看    

讲了这么多,真正关键的地方却给漏掉了,DependencyFacade.GetUnitInfoDependency();

如何生成这个缓存依赖项
2008-03-30 13:54 | Argo      

#2楼    回复  引用  查看    

拜读,谢过!
所谓WEB花园也好,WEB农场也好,都是为分担大量用户访问的处理机制。WEB花园是把大量访问处理分配到同一机器的多个进程,WEB农场就是把大量访问处理分配到不同的机器。
编写这样的WEB应用程序,必须把公共的数据处理部分独立于进程或机器来设计。想必楼主经过这次DEBUG,一定比我们这些纸上谈兵的混混儿更了解其中的原理了。
2008-03-30 13:58 | 李战      

#3楼 [楼主]   回复  引用  查看    

@Argo
关于如何生成这个缓存依赖项,我觉得没有必要在这里详细说明了。毕竟这些特性,早已有前辈高手叙述过了,一查就能查到一大片内容。况且还有petshop的开源代码可供参考。
2008-03-30 14:02 | 李.net      

#4楼    回复  引用  查看    

首先感谢LZ分享实战经验。
1,对于静态变量,属性reset,楼主有没有试过,到底是资源回收,还是应用程序重启?
2,对于配置信息等的保存,建议用缓存,而不是在静态构造函数初始化,因为静态构造函数只执行一次,除非应用程序重启。
3,绝对正真要针对web场的大应用,分布式应用,缓存方面还是需要用分布式缓存。
2008-03-30 17:08 | 5ypan [未注册用户]

#5楼    回复  引用  查看    

谢谢分享
2008-03-30 18:47 | 明明 [未注册用户]

#6楼    回复  引用  查看    

非常精辟!

其实应该是应用程序池自动回收w3wp进程,使得一些静态对象的数据回归到初始值。我向来不在Global或静态函数构造函数中初始化这些配置信息。
我的做法如下:
class Config
{
private static Config _Instance;
private static Object lockobj = new Object();
public static Config Instance
{
get
{
if(_Instance!=null)return _Instance;
lock(lockobj)
{
if(_Instance!=null)return _Instance;
_Instance=new Config();
//to do
//加载配置
}
}
}
}

无论从哪方面来说,我都觉得这种做法是最好的
2008-03-30 18:54 | 大石头      

#7楼    回复  引用  查看    

好处:
1,需要时才加载,节省程序启动时间以及内存资源。其实这也是.net程序的核心思想之一。
2,使用者无需为初始化而费心,一切工作在Config内完成。
3,专用的锁对象lockobj,Double Lock的判断,解决了多线程时的同步问题,并且能保证最好的性能。
2008-03-30 18:58 | 大石头      

#8楼    回复  引用  查看    

我是不推荐使用web园的,总会发生各种各样的问题。
为了提高性能,我更愿意从其它地方下手。
2008-03-30 19:06 | 大石头      

#9楼    回复  引用  查看    

看的出是作者的经验之谈。我也针对作者所说的几点谈谈自己的经验。

首先来谈状态保存
因为WEB是无状态的,因此我们不能指望(一定不能指望)在一个网站的应用程序池中长期数据,在一定时间段不在执行或应用程序池内存使用超过多少(这个我已经暂时无从考验,还是几年前看到的文章)都是引起回收和重启。至于你所提到通过windows服务或桌面程序时时调用WEB,不能从根本上解决这个问题。

第二个就是多个应用程序池之间共享数据的问题。
通过数据库缓存依赖只能解决简单的select之类的问题。我不清楚MS为什么会推出这会鸡肋的功能。而且在存储过程中你不能使用if ,else等逻辑语句。
好的建议就是写一个windows服务,从内存中开一块共享内存,这样所有的应用程序池可以共享同一个数据。
我就厚着脸破帖一个以前的帖子(不用给我回复):http://www.cnblogs.com/cnzc/category/59569.html

第三个关于session共享
我一直对这性能持怀疑。从来没有做过。不做评论了
2008-03-30 23:20 | 小春      

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-03-30 14:09 编辑过
 
向地震灾区捐赠爱心