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

时间:2021-11-13 20:32:03

  关于应用程序池:这是微软的一个全新概念:应用程序池是将一个或多个应用程序链接到一个或多个工作进程集合的配置。因为应用程序池中的应用程序与其他应用程序被工作进程边界分隔,所以某个应用程序池中的应用程序不会受到其他应用程序池中应用程序所产生的问题的影响。这个功能是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 : IIS6.0, WEB圆, 应用程序池