- 1.CROSS APPLY :
可以理解成INNER JOIN
适用:
* 表A 存储的是客户信息,每个客户ID一条记录;表B存储的是发布记录,每个客户ID对应多条记录,
需要取出每个客户最新的一条的发布信息记录
* 表A存储多客户发布的多条信息,想取得每个客户最新发布的单条信息
*
代码:
select
distinct info.UserID,id.DisplayDate
from view_InformationSimple as info
CROSS apply(select top(1)DisplayDate,UserId from view_Information as i where i.UserId=info.UserId order by DisplayDate DESC)as idCROSS APPLY :可以理解成INNER JOIN 例如:select distinct info.UserID,id.DisplayDate from view_InformationSimple as info
CROSS apply(select top(1)DisplayDate,UserId from view_Information as i where i.UserId=info.UserId order by DisplayDate DESC)as id 取得唯一用户的最新发布的信息
[Delphi]Win2003下提示Can't load package:dclite70.bpl
转载:http://hi.baidu.com/delphi%B1%E0%B3%CC/blog/item/93da634c204c2cfad72afc0e.html
win2003server SP1 + delphi7 ,在启动DELPHI时会提示Can't load package:dclite70.bpl,进入后打开Options时出现异常。
解决方法:
右击“我的电脑”。单击“属性”。
在“系统属性”中单击“高级”。
在“性能”中单击“设置”。
在“性能选项”中单击“数据执行保护”。
单击“添加”。选择Delphi7安装目录,然后到Bin目录下选择Delphi32.exe。
应用重启DELPHI即可。
全是Window2003的Data Execution Prevention(DEF数据执行保护)造成的。
也就是说,这个DEF数据执行保护是MS为了防范其系统中不知哪处的漏洞而被攻击者利用来制造数据缓冲区溢出而达到攻击入侵的目的的。而默认是执行保护的,所以给Delphi7造成这个错误。
[Memcached]Discuz!NT中集成Memcached分布式缓存
大约在两年前我写过一篇关于Discuz!NT缓存架构的文章,在那篇文章的结尾介绍了在IIS中如果开启多个
应用程序池会造成多个缓存实例之间数据同步的问题。虽然给出了一个解决方案,但无形中却把压力转移到了
磁盘I/O上(多个进程并发访问cache.config文件)。其实从那时起我就开始关注有什么更好的方案,当然今
天本文中所说的Memcached,以及Velocity等这类的分布式缓存方案之前都考虑过,但一直未能决定该使用那
个。起码Velocity要在.net 4.0之后才会提供,虽然是原生态,但有些远水解不了近火。
我想真正等到Velocity能堪当重任还要等上一段时间。于是我就开始将注意力转移到了Memcached,必定
有Facebook这只“超级小白鼠”使用它并且反响还不错。所以就开始尝试动手在产品中集成Memcached。
其实在之前的那篇关于Discuz!NT缓存架构的文章中已提到过,使用了设计模式中的“策略模式”来构造。
所以为了与以往使用缓存的代码格式相兼容,所以这里采用新添加MemCachedStrategy(MemCached策略)
来构造一个缓存策略类以便于当管理后台开启“MemCached”时以“MemCached策略模式”来做为当前系统默认
的策略模式。
其代码段如下(Discuz.Cache/MemCached.cs):
/// MemCache缓存策略类
/// </summary>
public class MemCachedStrategy : Discuz.Cache.ICacheStrategy
{
/// <summary>
/// 添加指定ID的对象
/// </summary>
/// <param name="objId"></param>
/// <param name="o"></param>
public void AddObject(string objId, object o)
{
RemoveObject(objId);
if (TimeOut > 0)
{
MemCachedManager.CacheClient.Set(objId, o, System.DateTime.Now.AddMinutes(TimeOut));
}
else
{
MemCachedManager.CacheClient.Set(objId, o);
}
}
/// <summary>
/// 添加指定ID的对象(关联指定文件组)
/// </summary>
/// <param name="objId"></param>
/// <param name="o"></param>
/// <param name="files"></param>
public void AddObjectWithFileChange(string objId, object o, string[] files)
{
;
}
/// <summary>
/// 添加指定ID的对象(关联指定键值组)
/// </summary>
/// <param name="objId"></param>
/// <param name="o"></param>
/// <param name="dependKey"></param>
public void AddObjectWithDepend(string objId, object o, string[] dependKey)
{
;
}
/// <summary>
/// 移除指定ID的对象
/// </summary>
/// <param name="objId"></param>
public void RemoveObject(string objId)
{
if (MemCachedManager.CacheClient.KeyExists(objId))
MemCachedManager.CacheClient.Delete(objId);
}
/// <summary>
/// 返回指定ID的对象
/// </summary>
/// <param name="objId"></param>
/// <returns></returns>
public object RetrieveObject(string objId)
{
return MemCachedManager.CacheClient.Get(objId);
}
/// <summary>
/// 到期时间
/// </summary>
public int TimeOut { set; get; }
}
上面类实现的接口Discuz.Cache.ICacheStrategy定义如下:
/// 公共缓存策略接口
/// </summary>
public interface ICacheStrategy
{
/// <summary>
/// 添加指定ID的对象
/// </summary>
/// <param name="objId"></param>
/// <param name="o"></param>
void AddObject(string objId, object o);
/// <summary>
/// 添加指定ID的对象(关联指定文件组)
/// </summary>
/// <param name="objId"></param>
/// <param name="o"></param>
/// <param name="files"></param>
void AddObjectWithFileChange(string objId, object o, string[] files);
/// <summary>
/// 添加指定ID的对象(关联指定键值组)
/// </summary>
/// <param name="objId"></param>
/// <param name="o"></param>
/// <param name="dependKey"></param>
void AddObjectWithDepend(string objId, object o, string[] dependKey);
/// <summary>
/// 移除指定ID的对象
/// </summary>
/// <param name="objId"></param>
void RemoveObject(string objId);
/// <summary>
/// 返回指定ID的对象
/// </summary>
/// <param name="objId"></param>
/// <returns></returns>
object RetrieveObject(string objId);
/// <summary>
/// 到期时间
/// </summary>
int TimeOut { set;get;}
}
当然在MemCachedStrategy类中还有一个对象要加以说明,就是MemCachedManager,该类主要是对
Memcached一些常操作和相关初始化实例调用的“封装”,下面是是其变量定义和初始化构造方法的代码:
/// MemCache管理操作类
/// </summary>
public sealed class MemCachedManager
{
#region 静态方法和属性
private static MemcachedClient mc = null;
private static SockIOPool pool = null;
private static MemCachedConfigInfo memCachedConfigInfo = MemCachedConfigs.GetConfig();
private static string [] serverList = null;
static MemCachedManager()
{
CreateManager();
}
private static void CreateManager()
{
serverList = Utils.SplitString(memCachedConfigInfo.ServerList, ""r"n");
pool = SockIOPool.GetInstance(memCachedConfigInfo.PoolName);
pool.SetServers(serverList);
pool.InitConnections = memCachedConfigInfo.IntConnections;//初始化链接数
pool.MinConnections = memCachedConfigInfo.MinConnections;//最少链接数
pool.MaxConnections = memCachedConfigInfo.MaxConnections;//最大连接数
pool.SocketConnectTimeout = memCachedConfigInfo.SocketConnectTimeout;//Socket链接超时时间
pool.SocketTimeout = memCachedConfigInfo.SocketTimeout;// Socket超时时间
pool.MaintenanceSleep = memCachedConfigInfo.MaintenanceSleep;//维护线程休息时间
pool.Failover = memCachedConfigInfo.FailOver; //失效转移(一种备份操作模式)
pool.Nagle = memCachedConfigInfo.Nagle;//是否用nagle算法启动socket
pool.HashingAlgorithm = HashingAlgorithm.NewCompatibleHash;
pool.Initialize();
mc = new MemcachedClient();
mc.PoolName = memCachedConfigInfo.PoolName;
mc.EnableCompression = false;
}
/// <summary>
/// 缓存服务器地址列表
/// </summary>
public static string[] ServerList
{
set
{
if (value != null)
serverList = value;
}
get { return serverList; }
}
/// <summary>
/// 客户端缓存操作对象
/// </summary>
public static MemcachedClient CacheClient
{
get
{
if (mc == null)
CreateManager();
return mc;
}
}
public static void Dispose()
{
if (pool != null)
pool.Shutdown();
}


上面代码中构造方法会初始化一个池来管理执行Socket链接,并提供静态属性CacheClient以便MemCachedStrategy
来调用。
当然我还在这个管理操作类中添加了几个方法分别用于检测当前有效的分布式缓存服务器的列表,向指定(或全部)
缓存服务器发送特定stats命令来获取当前缓存服务器上的数据信息和内存分配信息等,相应的方法如下(详情见注释):
/// 获取当前缓存键值所存储在的服务器
/// </summary>
/// <param name="key">当前缓存键</param>
/// <returns>当前缓存键值所存储在的服务器</returns>
public static string GetSocketHost(string key)
{
string hostName = "";
SockIO sock = null;
try
{
sock = SockIOPool.GetInstance(memCachedConfigInfo.PoolName).GetSock(key);
if (sock != null)
{
hostName = sock.Host;
}
}
finally
{
if (sock != null)
sock.Close();
}
return hostName;
}
/// <summary>
/// 获取有效的服务器地址
/// </summary>
/// <returns>有效的服务器地</returns>
public static string[] GetConnectedSocketHost()
{
SockIO sock = null;
string connectedHost = null;
foreach (string hostName in serverList)
{
if (!Discuz.Common.Utils.StrIsNullOrEmpty(hostName))
{
try
{
sock = SockIOPool.GetInstance(memCachedConfigInfo.PoolName).GetConnection(hostName);
if (sock != null)
{
connectedHost = Discuz.Common.Utils.MergeString(hostName, connectedHost);
}
}
finally
{
if (sock != null)
sock.Close();
}
}
}
return Discuz.Common.Utils.SplitString(connectedHost, ",");
}
/// <summary>
/// 获取服务器端缓存的数据信息
/// </summary>
/// <returns>返回信息</returns>
public static ArrayList GetStats()
{
ArrayList arrayList = new ArrayList();
foreach (string server in serverList)
{
arrayList.Add(server);
}
return GetStats(arrayList, Stats.Default, null);
}
/// <summary>
/// 获取服务器端缓存的数据信息
/// </summary>
/// <param name="serverArrayList">要访问的服务列表</param>
/// <returns>返回信息</returns>
public static ArrayList GetStats(ArrayList serverArrayList, Stats statsCommand, string param)
{
ArrayList statsArray = new ArrayList();
param = Utils.StrIsNullOrEmpty(param) ? "" : param.Trim().ToLower();
string commandstr = "stats";
//转换stats命令参数
switch (statsCommand)
{
case Stats.Reset: { commandstr = "stats reset"; break; }
case Stats.Malloc: { commandstr = "stats malloc"; break; }
case Stats.Maps: { commandstr = "stats maps"; break; }
case Stats.Sizes: { commandstr = "stats sizes"; break; }
case Stats.Slabs: { commandstr = "stats slabs"; break; }
case Stats.Items: { commandstr = "stats"; break; }
case Stats.CachedDump:
{
string[] statsparams = Utils.SplitString(param, " ");
if(statsparams.Length == 2)
if(Utils.IsNumericArray(statsparams))
commandstr = "stats cachedump " + param;
break;
}
case Stats.Detail:
{
if(string.Equals(param, "on") || string.Equals(param, "off") || string.Equals(param, "dump"))
commandstr = "stats detail " + param.Trim();
break;
}
default: { commandstr = "stats"; break; }
}
//加载返回值
Hashtable stats = MemCachedManager.CacheClient.Stats(serverArrayList, commandstr);
foreach (string key in stats.Keys)
{
statsArray.Add(key);
Hashtable values = (Hashtable)stats[key];
foreach (string key2 in values.Keys)
{
statsArray.Add(key2 + ":" + values[key2]);
}
}
return statsArray;
}
/// <summary>
/// Stats命令行参数
/// </summary>
public enum Stats
{
/// <summary>
/// stats : 显示服务器信息, 统计数据等
/// </summary>
Default = 0,
/// <summary>
/// stats reset : 清空统计数据
/// </summary>
Reset = 1,
/// <summary>
/// stats malloc : 显示内存分配数据
/// </summary>
Malloc = 2,
/// <summary>
/// stats maps : 显示"/proc/self/maps"数据
/// </summary>
Maps =3,
/// <summary>
/// stats sizes
/// </summary>
Sizes = 4,
/// <summary>
/// stats slabs : 显示各个slab的信息,包括chunk的大小,数目,使用情况等
/// </summary>
Slabs = 5,
/// <summary>
/// stats items : 显示各个slab中item的数目和最老item的年龄(最后一次访问距离现在的秒数)
/// </summary>
Items = 6,
/// <summary>
/// stats cachedump slab_id limit_num : 显示某个slab中的前 limit_num 个 key 列表
/// </summary>
CachedDump =7,
/// <summary>
/// stats detail [on|off|dump] : 设置或者显示详细操作记录 on:打开详细操作记录 off:关闭详细操作记录 dump: 显示详细操作记录(每一个键值get,set,hit,del的次数)
/// </summary>
Detail = 8
}
当然在配置初始化缓存链接池时使用了配置文件方式(memcached.config)来管理相关参数,其info信息
类说明如下(Discuz.Config/MemCachedConfigInfo.cs):
/// MemCached配置信息类文件
/// </summary>
public class MemCachedConfigInfo : IConfigInfo
{
private bool _applyMemCached;
/// <summary>
/// 是否应用MemCached
/// </summary>
public bool ApplyMemCached
{
get
{
return _applyMemCached;
}
set
{
_applyMemCached = value;
}
}
private string _serverList;
/// <summary>
/// 链接地址
/// </summary>
public string ServerList
{
get
{
return _serverList;
}
set
{
_serverList = value;
}
}
private string _poolName;
/// <summary>
/// 链接池名称
/// </summary>
public string PoolName
{
get
{
return Utils.StrIsNullOrEmpty(_poolName) ? "DiscuzNT_MemCache" : _poolName;
}
set
{
_poolName = value;
}
}
private int _intConnections;
/// <summary>
/// 初始化链接数
/// </summary>
public int IntConnections
{
get
{
return _intConnections > 0 ? _intConnections : 3;
}
set
{
_intConnections = value;
}
}
private int _minConnections;
/// <summary>
/// 最少链接数
/// </summary>
public int MinConnections
{
get
{
return _minConnections > 0 ? _minConnections : 3;
}
set
{
_minConnections = value;
}
}
private int _maxConnections;
/// <summary>
/// 最大连接数
/// </summary>
public int MaxConnections
{
get
{
return _maxConnections > 0 ?_maxConnections : 5;
}
set
{
_maxConnections = value;
}
}
private int _socketConnectTimeout;
/// <summary>
/// Socket链接超时时间
/// </summary>
public int SocketConnectTimeout
{
get
{
return _socketConnectTimeout > 1000 ? _socketConnectTimeout : 1000;
}
set
{
_socketConnectTimeout = value;
}
}
private int _socketTimeout;
/// <summary>
/// socket超时时间
/// </summary>
public int SocketTimeout
{
get
{
return _socketTimeout > 1000 ? _maintenanceSleep : 3000;
}
set
{
_socketTimeout = value;
}
}
private int _maintenanceSleep;
/// <summary>
/// 维护线程休息时间
/// </summary>
public int MaintenanceSleep
{
get
{
return _maintenanceSleep > 0 ? _maintenanceSleep : 30;
}
set
{
_maintenanceSleep = value;
}
}
private bool _failOver;
/// <summary>
/// 链接失败后是否重启,详情参见http://baike.baidu.com/view/1084309.htm
/// </summary>
public bool FailOver
{
get
{
return _failOver;
}
set
{
_failOver = value;
}
}
private bool _nagle;
/// <summary>
/// 是否用nagle算法启动socket
/// </summary>
public bool Nagle
{
get
{
return _nagle;
}
set
{
_nagle = value;
}
}
}
这些参数我们通过注释应该有一些了解,可以说memcached的主要性能都是通过这些参数来决定的,大家
应根据自己公司产品和应用的实际情况配置相应的数值。
当然,做完这一步之后就是对调用“缓存策略”的主体类进行修改来,使其根据对管理后台的设计来决定
加载什么样的缓存策略,如下:
/// Discuz!NT缓存类
/// 对Discuz!NT论坛缓存进行全局控制管理
/// </summary>
public class DNTCache
{

.//通过该变量决定是否启用MemCached
private static bool applyMemCached = MemCachedConfigs.GetConfig().ApplyMemCached;
/// <summary>
/// 构造函数
/// </summary>
private DNTCache()
{
if (applyMemCached)
cs = new MemCachedStrategy();
else
{
cs = new DefaultCacheStrategy();
objectXmlMap = rootXml.CreateElement("Cache");
//建立内部XML文档.
rootXml.AppendChild(objectXmlMap);
//LogVisitor clv = new CacheLogVisitor();
//cs.Accept(clv);
cacheConfigTimer.AutoReset = true;
cacheConfigTimer.Enabled = true;
cacheConfigTimer.Elapsed += new System.Timers.ElapsedEventHandler(Timer_Elapsed);
cacheConfigTimer.Start();
}
}


到这里,主要的开发和修改基本上就告一段落了。下面开始介绍一下如果使用Stats命令来查看缓存的
分配和使用等情况。之前在枚举类型Stats中看到该命令有几个主要的参数,分别是:
stats reset
stats malloc
stats maps
stats sizes
stats slabs
stats items
stats cachedump slab_id limit_num
stats detail [on|off|dump]
而JAVAEYE的 robbin 写过一篇文章:贴一段遍历memcached缓存对象的小脚本,来介绍如何使用其中的
“stats cachedump”来获取信息。受这篇文章的启发,我将MemCachedClient.cs文件中的Stats方法加以修
改,添加了一个command参数(字符串型),这样就可以向缓存服务器发送上面所说的那几种类型的命令了。
测试代码如下:
{
ArrayList arrayList = new ArrayList();
arrayList.Add("10.0.1.52:11211");//缓存服务器的地址
StateResult.DataSource = MemCachedManager.GetStats(arrayList, (MemCachedManager.Stats)
Utils.StrToInt(StatsParam.SelectedValue, 0), Param.Text);
StateResult.DataBind();
}
页面代码如下:


我这样做的目的有两个,一个是避免每次都使用telnet协议远程登陆缓存服务器并输入相应的命令行
参数(我记忆力不好,参数多了之后就爱忘)。二是将来会把这个页面功能内置到管理后台上,以便后台
管理员可以动态监测每台缓存服务器上的数据。
好了,到这里今天的内容就差不多了。在本文中我们看到了使用设计模式的好处,通过它我们可以让
自己写的代码支持“变化”。这里不妨再多说几句,大家看到了velocity在使用上也是很方便,如果可以
的话,未来可以也会将velocity做成一个“缓存策略”,这样站长或管理员就可以根据自己公司的实际情
况来加以灵活配置了。
相关资料:
memcached 全面剖析.pdf
memcached 深度分析
原文链接:http://www.cnblogs.com/daizhj/archive/2009/02/09/1386652.html
作者: daizhj, 代震军
Tags: memcached,discuz!nt,缓存,设计模式,策略模式,strategy pattern
网址: http://daizhj.cnblogs.com/
[Memcached].NET下实现分布式缓存系统Memcached
转载:http://tech.it168.com/a2009/0907/675/000000675239.shtml
【IT168 技术文档】在Web应用程序中,数据通常保存在RDBMS中,应用服务器从数据库中读取数据并在浏览器中显示。但随着数据量的增大、访问的集中,就会出现RDBMS的负载加重、数据库响应变慢、网站显示延迟等重大影响。为了缓解数据库的压力,提升Web应用程序的响应速度,人们提出了Web缓存的概念,这里缓存的概念不同于计算机硬盘控制器上的一块内存芯片。Web缓存位于Web服务器(1 个或多个,内容源服务器)和客户端之间(1个或多个),缓存会根据进来的请求保存输出内容的副本,例如html页面, 图片,文件(统称为副本),然后,当下一个请求来到的时候,如果是相同的URL,缓存直接使用副本响应访问请求,而不是向内容源服务器再次发送请求。使用 缓存可以减少相应延迟,因为请求在缓存服务器(离客户端更近)而不是源服务器被相应,这个过程耗时更少,让web服务器看上去相应更快;当副本被重用时还 可以减少网络带宽消耗。
缓存的工作方式
缓存的工作方式如下图所示:

上图中的缓存服务器维护一个集中缓存并在其中存放经常被请求的对象,任何 Web 浏览器客户端都可以访问该缓存。来自内存/磁盘缓存的对象所需的处理资源比来自其他网络的对象所需的处理资源要少得多。因此,这可以提高客户端浏览器性 能、缩短用户响应时间并减少Internet 连接所消耗的带宽。
上图1到6标示了当用户请求对象时,缓存服务器是如何响应的,主要经过下列步骤:
第一个用户(客户端 1)请求 Web 对象。
缓存服务器检查该对象是否存在于缓存中。由于该对象不存在于缓存服务器缓存中,因此,缓存服务器从Internet中的服务器请求该对象。
Internet上的服务器将该对象返回给缓存服务器计算机。
缓存服务器将该对象的一个副本保留在其缓存中,并将对象返回给客户端 1。
客户端 2请求相同的对象。
缓存服务器从其缓存中返回该对象,而不是从Internet中获取该对象。
一般在组织的Web服务器前面部署缓存服务器。Web服务器是主持商业 Web 业务或可由业务合作伙伴访问的服务器。使用传入的Web请求,缓存服务器可以充当外部环境的 Web 服务器,并通过其缓存来完成客户端对Web内容的请求。只有在缓存无法处理请求时,缓存服务器才将请求转发到Web服务器。
目前实现缓存的机制有几种,Memcached就是其中的一种。本文介绍了Memcached的概念,基本原理以及工作机制,并介绍了如何在ASP.NET中使用Memcache进行开发Web应用程序以提升Web应用程序的性能。
Memcached介绍
Memcached是一个高性能的分布式的内存对象缓存系统。Memcached是为了加快http://www.livejournal.com/访 问速度而诞生的一个项目,由Danga Interactive开发的,它通过在内存里维护一个统一的巨大的hash表来存储各 种格式的数据,包括图像、视 频、文件以及数据库检索的结果等。虽然最初为了加速 LiveJournal 访问速度而开发的,但是后来被很多大型的网站采用。它的缓存是一种分布式的,也就是可以允许不同主机上的多个用户同时访问这个缓存系统,这种方法不仅解决 了共享内存只能是单机的弊端,同时也解决了数据库检索的压力,最大的优点是提高了访问获取数据的速度,Memcached用于在动态应用中减少数据库负 载,提升访问速度,但是用来加速Web应用、降低数据库负载时比较多。Memcached也完全可以用到其他地方,比如分布式数据库,分布式计算等领域。
Memcache的特点
Memcached作为高速运行的分布式缓存服务器,具有以下的特点。
协议简单。Memcached的服务器客户端通信并不使用复杂的XML等格式, 而使用简单的基于文本行的协议。因此,通过telnet也能在memcached上保存数据、取得数据。
基于libevent的事件处理,libevent是一个事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue等系统调用管理事件机制。而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。
内置内存存储方式。为了提高性能,memcached中保存的数据都存储在memcached内置的内存存储空间中。 由于数据仅存在于内存中,因此重启memcached、重启操作系统会 导致全部数据消失。 另外,内容容量达到指定值之后,就基于LRU(Least Recently Used)算法自动删除不使用的缓存。 memcached本身是为缓存而设计的服务器,因此并没有过多考虑数据的永久性问题。memcached默认情况下采用了名为Slab Allocator的机制分配、管理内存。 在该机制出现以前,内存的分配是通过对所有记录简单地进行malloc和free来进行的。 但是,这种方式会导致内存碎片,加重操作系统内存管理器的负担,最坏的情况下,会导致操作系统比memcached进程本身还慢。
memcached不互相通信的分布式。memcached尽管是“分布式”缓存服务器,但服务器端并没有分布式功能。各个memcached不会互相通 信以共享信息。那么,怎样进行分布式呢?这完全取决于客户端的实现。Cache::Memcached的分布式方法简单来说,就是“根据服务器台数的余数 进行分散”。 求得键的整数哈希值,再除以服务器台数,根据其余数来选择服务器。
Memcached的缓存是一种分布式的,可以让不同主机上的多个用户同时访问, 因此解决了共享内存只能单机应用的局限,更不会出现使用数据库做类似事情的时候,磁盘开销和阻塞的发生。
Memcached使用了libevent(如果可以的话,在linux下使用epoll)来均衡任何数量的打开链接,使用非阻塞的网络I/O, 对内部对象实现引用计数(因此,针对多样的客户端,对象可以处在多样的状态), 使用自己的页块分配器和哈希表, 因此虚拟内存不会产生碎片并且虚拟内存分配的时间复杂度可以保证为O(1)。
许多语言都实现了连接memcached的客户端,其中以Perl、PHP为主。 仅仅memcached网站上列出的语言就有Perl、PHP、Python、Ruby、C#、C/C++等等。
Memcached的工作机制
Memcached通过在内存中开辟一块区域来维持一个大的hash表来加快页面访问速度,虽然和数据库是独立的,但是目前主要用来缓存数据库的数据。 允许多个server通过网络形成一个大的hash,用户不必关心数据存放在哪,只调用相关接口就可。存放在内存的数据通过LRU算法进行淘汰出内存。同 时可以通过删除和设置失效时间来淘汰存放在内存的数据。
Memcached在.NET中的应用
一.Memcached服务器端的安装(此处将其作为系统服务安装)
下载文件:memcached 1.2.1 for Win32 binaries (Dec 23, 2006)
下载地址:http://jehiah.cz/projects/memcached-win32/files/memcached-1.2.1-win32.zip
1.解压缩文件到c:\memcached
2.命令行输入 c:\memcached\memcached.exe -d install'
3.命令行输入 c:\memcached\memcached.exe -d start ,该命令启动 Memcached ,默认监听端口为 11211
通过 memcached.exe -h 可以查看其帮助,查看memcache状态,telnet 192.168.0.98 11211。输入stats查询状态
stats
STAT pid 8601
STAT uptime 696
STAT time 1245832689
STAT version 1.2.0
STAT pointer_size 64
STAT rusage_user 0.007998
STAT rusage_system 0.030995
STAT curr_items 1
STAT total_items 1
STAT bytes 76
STAT curr_connections 2
STAT total_connections 4
STAT connection_structures 3
STAT cmd_get 1
STAT cmd_set 1
STAT get_hits 1//命中次数
STAT get_misses 0 //失效次数
STAT bytes_read 97
STAT bytes_written 620
STAT limit_maxbytes 134217728
END
-d选项是启动一个守护进程
-m是分配给Memcache使用的内存数量,单位是MB,我这里是10MB
-u是运行Memcache的用户
-l是监听的服务器IP地址,如果有多个地址的话,我这里假定指定了服务器的IP地址为本机ip地址
-p是设置Memcache监听的端口,我这里设置了12000,最好是1024以上的端口
-c选项是最大运行的并发连接数,默认是1024,我这里设置了256,按照你服务器的负载量来设定
-P是设置保存Memcache的pid文件
二..NET memcached client library(memcached的.NET客户端类库)
下载memcached的.NET客户端类库,下载地址:https://sourceforge.net/projects/memcacheddotnet/里面有.net1.1 和 .net2.0的两种版本,里面还有.NET应用memcached的例子。
三.应用
1.新建ASP.NET站点,将Commons.dll,ICSharpCode.SharpZipLib.dll,log4net.dll,Memcached.ClientLibrary.dll添加到web引用。
2.为了进行后续的测试,我们创建两个aspx页面,memcache.aspx和nomemcache.aspx,memcache.aspx是使用 MemcacheClient类加入了缓存机制的页面。nomemcache.aspx是没有加入缓存机制的页面,直接连接的数据库。一会我们通过观察数 据库事件和进行压力测试来测试在压力测试的情况下应用程序的性能。
3.memcache.aspx.cs中添加对Memcached.ClientLibrary.dll的引用,即:using Memcached.ClientLibrary;Page_Load()中加入如下代码。
protected void Page_Load(object sender, EventArgs e)
{
string[] serverlist = new string[] { "127.0.0.1:11211" };
string poolName = "MemcacheIOPool";
SockIOPool pool = SockIOPool.GetInstance(poolName);
//设置连接池的初始容量,最小容量,最大容量,Socket 读取超时时间,Socket连接超时时间
pool.SetServers(serverlist);
pool.InitConnections = 1;
pool.MinConnections = 1;
pool.MaxConnections = 500;
pool.SocketConnectTimeout = 1000;
pool.SocketTimeout = 3000;
pool.MaintenanceSleep = 30;
pool.Failover = true;
pool.Nagle = false;
pool.Initialize();//容器初始化
//实例化一个客户端
MemcachedClient mc = new MemcachedClient();
mc.PoolName = poolName;
mc.EnableCompression = false;
string key = "user_info";//key值
object obj = new object();
if (mc.KeyExists(key)) //测试缓存中是否存在key的值
{
obj = mc.Get(key);
User user2 = (User)obj;
Response.Write("
" + user2.Name + "," + user2.Pwd + "
");
}
else {
string conStr = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
SQLConnection conn = new SQLConnection(conStr);
conn.Open();
string sql = "Select * From T_User Where id=1";
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataReader dr = cmd.ExecuteReader();
User user = new User();
while (dr.Read())
{
user.Name = dr["name"].ToString();
user.Pwd = dr["pwd"].ToString();
}
dr.Close();
conn.Close();
mc.Set(key, user, System.DateTime.Now.AddMinutes(2)); //存储数据到缓存服务器,这里将user这个对象缓存,key 是"user_info1"
Response.Write("
姓名:" + user.Name + ",密码:" + user.Pwd + "
");
}
}
4.nomemcache.aspx是没有加缓存机制的直接连接数据库的页面。nomemcache.aspx.cs中的代码:
protected void Page_Load(object sender, EventArgs e)
{
string conStr = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
SqlConnection conn = new SqlConnection(conStr);
conn.Open();
string sql = "Select * From T_User Where id=2";
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataReader dr = cmd.ExecuteReader();
User user = new User();
while (dr.Read())
{
user.Name = dr["name"].ToString();
user.Pwd = dr["pwd"].ToString();
}
dr.Close();
conn.Close();
Response.Write("
姓名:" + user.Name + ",密码:" + user.Pwd + "
");
}
}
5.测试
测试memcache.aspx页面,该测试的主要目的是访问memcache.aspx页面,看是否看该页面走的是memcached而非访问的数据 库(第一次是访问数据库)。首先,我们运行memcache.aspx页面,通过Sql Server Profiler来查看运行memcache.aspx页面对数据库的操作情况。第一次运行memcache.aspx的时候,Sql Server Profiler里面显示memcache.aspx对数据库的操作,即执行了Select * From T_User Where id=1。

memcache.aspx页面上第一次运行的时候显示如下信息:
通过读取数据库得到的数据:
姓名:lucy,密码:lucy。

接着我们刷新memcache.aspx页面,页面上还显示上述信息:
通过读取缓存得到的数据:
姓名:lucy,密码:lucy
但从Sql Server Profiler观察,memcache.aspx页面没有对数据库进行任何操作。说明我们第二次访问该页面是读取的缓存,接着不停的刷新页面,都显示的是通过读取缓存得到的数据。直到到达缓存失效时间。
测试nomemcache.aspx,我们运行nomemcache.aspx页面,每刷新一次(包括第一次),Sql Server Profiler都记录了,nomemcache.aspx页面对数据库进行的操作,即之行了Select * From T_User Where id=2语句。说明每访问一次该页面,都要进行一次数据库的访问操作。
这里我们使用Microsoft Web Application Stress Tool对web进行压力测试,Microsoft Web Application Stress Tool 是由微软的网站测试人员所开发,专门用来进行实际网站压力测试的一套工具。透过这套功能强大的压力测试工具,您可以使用少量的客户端计算机仿真大量用户上 线对网站服务所可能造成的影响,在网站实际上线之前先对您所设计的网站进行如同真实环境下的测试,以找出系统潜在的问题,对系统进行进一步的调整、设置工 作。就是因为这些特性,才使它具备了DOS轰炸的功能。
1、工具简单设置
打开Web Application Stress Tool,很简洁的一个页面,上面是工具栏,左下方是功能选项,右下方是详细设置选项。在对目标Web服务器进行压力测试之前,先对它进行一些必要的设置。
(1).在“settings”的功能设置中(如下图),一个是Stress level (threads)这里是指定程序在后台用多少线程进行请求,也就是相当于模拟多少个客户机的连接,更加形象的就是说设置多少轰炸的线程数。一般填写 500~1000,因为这个线程数是根据本机的承受力来设置的,如果你对自己的机器配置有足够信心的话,那么设置的越高,轰炸的效果越好。

(2).在“Test Run Time”中来指定一次压力测试需要持续的时间,分为天、小时、分、秒几个单位级别,你根据实际情况来设置吧!这里面设置测试时间为1分钟。
(3).其余的选项大家可以根据自己的情况设置。
2、压力测试
步骤1:在工具中点右键,选择Add命令,增加了一个新的测试项目:memcache,对它进行设置,在主选项中的server中填写要测试的服务器的 IP地址,这里我们是在本机上进行测试,所以填写localhost。在下方选择测试的Web连接方式,这里的方式Verb选择 get,path选择要测试的Web页面路径,这里填写/WebMemCache/memcache.aspx,即加入缓存的memcache.aspx 页面(如下图)。

步骤2:在“Settings”的功能设置中将Stress level (threads)线程数设置为500。完毕后,点工具中的灰色三角按钮即可进行测试(如下图)。

同理,我们在建一个nomemcach的项目用来测试nomemcache.aspx页面。Memcach和nomemcach测试完毕后,点击工具栏上的Reports按钮查看测试报告:

3.性能分析
Memcache.aspx的测试报告:
Overview
================================================================================
Report name: 2009-7-20 10:52:00
Run on: 2009-7-20 10:52:00
Run length: 00:01:12
Web Application Stress Tool Version:1.1.293.1
Number of test clients: 1
Number of hits: 2696
Requests per Second: 44.93
Socket Statistics
——————————————————————————–
Socket Connects: 3169
Total Bytes Sent (in KB): 646.80
Bytes Sent Rate (in KB/s): 10.78
Total Bytes Recv (in KB): 2019.37
Bytes Recv Rate (in KB/s): 33.65
Socket Errors
——————————————————————————–
Connect: 0
Send: 0
Recv: 0
Timeouts: 0
RDS Results
——————————————————————————–
Successful Queries: 0
Page Summary
Page Hits TTFB Avg TTLB Avg Auth Query
================================================================================
GET /WebMemCache/memcache.aspx 2696 1.94 1.95 No No
Nomemcache.aspx的测试报告:
Overview
================================================================================
Report name: 2009-7-20 10:54:01
Run on: 2009-7-20 10:54:01
Run length: 00:01:12
Web Application Stress Tool Version:1.1.293.1
Number of test clients: 1
Number of hits: 2577
Requests per Second: 42.95
Socket Statistics
——————————————————————————–
Socket Connects: 2860
Total Bytes Sent (in KB): 589.32
Bytes Sent Rate (in KB/s): 9.82
Total Bytes Recv (in KB): 1932.75
Bytes Recv Rate (in KB/s): 32.21
Socket Errors
——————————————————————————–
Connect: 0
Send: 0
Recv: 0
Timeouts: 0
RDS Results
——————————————————————————–
Successful Queries: 0
Page Summary
Page Hits TTFB Avg TTLB Avg Auth Query
================================================================================
GET /WebMemCache/nomemcache.aspx 2577 4.75 4.79 No No
从测试报告上看出memcache.aspx页面在一分钟内的Hits(命中次数)2696,平均TTFB是(Total Time to First Byte)1.94,平均TTLB(Total Time to Last Byte)是1.95。这些参数都低于nomemcache.aspx。另外memcache.aspx的Requests per Second(每秒请求的次数)是 44.93高于nomemcache.aspx页面的42.95.这些参数都说明memcache.aspx页面的执行性能要高于 nomemcache.aspx页面。缓存起到了提高性能的作用。当然我这里面进行的测试只是模拟500个用户在1分钟内的访问对Web服务器性能的影 响。
总结
本文简单介绍了Memcached的基本原理,特点以及工作方式,接下来介绍了 Windows下Memcached服务器端程序的安装方法、在.NET应用程序中使用.NET memcached client library。接下来通过运行分析程序来了解memcached的工作原理机制,最后通过压力测试工具对没有加入Memcached机制的页面和加入 Memcached页面进行了压力测试,对比加入Memcached机制前后Web应用程序的性能。了解Memcached内部构造, 就能知道如何在应用程序中使用memcached才能使Web应用的速度更上一层楼。提升web应用程序的性能和访问速度。
[WEB]提高网站性能之 —— 减少图片HTTP请求的方案
转载:http://www.cnblogs.com/xugang/archive/2009/10/26/1589934.html
当一个页面的图片在没有使用任何方案的情况下,五个图片超链接就只能使用五个图标按钮。
代码如下:
<center> <a href="JavaScript:alert('Home')" title="Home"><img src="http_request_img/home.gif" border="0"></a>
<a href="JavaScript:alert('Gift')" title="Gift"><img src="http_request_img/gift.gif" border="0"></a>
<a href="javascript:alert('Cart')" title="Cart"><img src="http_request_img/cart.gif" border="0"></a>
<a href="javascript:alert('Settings')" title="Settings"><img src="http_request_img/settings.gif" border="0"></a>
<a href="javascript:alert('Help')" title="Help"><img src="http_request_img/help.gif" border="0"></a>
</center>
</div>
效果如下:
注意:这五个按钮分别使用了五张图片
那么,五张图片就意味着你的该页面又多了五个HTTP请求,它将大大降低页面的呈现效率。增加的HTTP请求是导致页面性能下降的最大刽子手,所以我们应该尽量减少HTTP请求,而图片是增加HTTP请求的最大可能者,搞定它,事不宜迟!呵呵^_^
下面列出了三种解决方案:
方案一:图片地图(图像热点):它将用户的点击映射到一个操作,而无需向后端Web服务器发送任何请求。
代码如下:
<img usemap="#mymap" src="http_request_img/imagemap.gif" border="0" />
<map name="mymap"> <!– img使用的map –>
<area shape="rect" coords="0,0,31,31" href="javascript:alert('Home');"/>
<area shape="rect" coords="36,0,66,31" href="javascript:alert('Gifts');"/>
<area shape="rect" coords="71,0,101,31" href="javascript:alert('Cart')"/>
<area shape="rect" coords="106,0,136,31" href="javascript:alert('Set')"/>
<area shape="rect" coords="141,0,171,31" href="javascript:alert('Help')"/>
</map></div>
效果如下:
注意:这五个按钮其实是在一张图片中
方案二:CSS Sprites:使用这种方式也可以将五张图片合并为一张图片,并且更为灵活。
代码如下:
/* 对div中所有span的样式设置 */
#navbar span {
/* 定义<span>标签将截取的图片宽度和高度 */
width:30px; height:31px;
/* 添加包含了所有图标的图片 */
background-image:url(http_request_img/imagemap.gif);
display:inline; float:left;
}
/* background-position:指定图标在CSS Sprites图片的偏移量
margin-left 和 margin-right 则用来定位图标位置
*/
.home { background-position:0 0; margin-left:2px;}
.gifts { background-position:-35px 0; margin-left:50px;}
.cart { background-position:-70px 0; margin-left:120px;}
</style>
<div id="navbar" style="border: 2px ridge rgb(51, 51, 51); padding: 4px 0pt; background-color:#ff0; width: 300px; height: 32px;">
<a href="javascript:alert('Home')" title="Home">
<span class="home"></span></a> <!– 注意:该span标签用来加载图片 –>
<a href="javascript:alert('Gifts')" title="Gifts">
<span class="gifts"></span></a> <!– 注意:该span标签用来加载图片 –>
<a href="javascript:alert('Cart')" title="Cart">
<span class="cart"></span></a> <!– 注意:该span标签用来加载图片 –>
</div>
效果如下:
注意:在这个“CSS Sprites”中用到的图片就是“图片地图”中的同一张图片。(并特地只显示其中的三个图标)
CSS Sprites的原理:只是定位到图片其中的一块区域,并显示在某个位置而已。
CSS Sprites的优点:通过只使用一个图片减少了HTTP请求,并且比“图片地图”更灵活。
很多人会误认为:合并的图片要比分离的图片的总和要大,因为合并的图片中包含有附加的空白区域。
实际上,合并的图片要比分离的图片的总和要小,这是因为它降低了图片自身的开销(颜色表、格式信息,等等)。
方案三:内联图片:通过使用data: URL模式在Web页面中包含图片,且无需任何额外的HTTP请求。(IE目前不支持)
1、内联图片的原理:允许将小块数据内联为‘立即数(immediate data)’,数据就包含在其URL自身之中。
2、内联图片的格式:data:[<mediatype>][;base64],<data>
举例:一个内联图片可以定义为
<img src="data:image/gif;base64,asdfghjklaasdvdddddddaaaaaddddd++ddd======ssswdvdv33" >
其中“asdfghjklaasdvdddddddaaaaaddddd++ddd======ssswdvdv33”就是该图片的数据。
data: URL模式-> 基本用于内联图片,可以用在任何需要指定URL的地方,如:script和a标签中。
3、内联图片的缺陷:
a> 不受IE的支持(IE7也如此);
b> 保存数据的大小上受限制(Firefox 1.5支持高达100KB的数据);
c> Base64编码会增加其图片的大小;
d> 在跨越不同页面时不会被缓存
(可以将内联图片作为背景图片保存在外部CSS样式表中来解决此问题,但会增加一个HTTP请求以换得数据被缓存的结果。);
在PHP技术中,使用函数file_get_contents从磁盘读取图片并插入到页面中来创建内联图片:
在其HTML中,使用style标签的href属性指定外部样式表文件时,直接指定到了一个PHP模版的.php文件(注意:不是.css文件),该PHP模版使用file_get_contents函数生成其样式表:
.home{ background-image:url(data:image/gif;base64,<?php echo base64_encode(file_get_contents("../img/home.gif")) ?>);}
其最终数据将是base64编码格式的图片data数据。
当然,使用ASP.NET技术生成其base64编码格式的图片data数据也不会很难。大家可以自行实验。
本人推荐:根据具体情况,使用方案一:图片地图(图像热点)或方案二:CSS Sprites。
附加知识:URL中的相关协议:http:、data:、file:、ftp:、mailto:、smtp:、pop:、dns:、whois:、finger:、daytime:、news:、urn: 等。这其中有一些是官方注册的,还有一些是由于广泛使用而被接受。
[SQL]Select 语句查询原理
我并非专业DBA,但做为B/S架构的 开发人员,总是离不开数据库,一般开发员只会应用SQL的四条经典语句:select ,insert,delete,update。但是我从来没有研究过它们的工作原理,这篇我想说一说select在数据库中的工作原理。B/S架构中最经 典的话题无非于三层架构,可以大概分为数据层,业务逻辑层和表示层,而数据层的作用一般都是和数据库交互,例如查询记录。
我们经常是写好查询SQL,然后调用程序执行SQL。但是它内部的工作流程是怎样的呢?先做哪一步,然后做哪一步等,我想还有大部分朋友和我一样都不一定清楚。
第一步:应用程序把查询SQL语句发给服务器端执行。
我们在数据层执行SQL语句时,应用程序会连接到相应的数据库服务器,把SQL语句发送给服务器处理。
第二步:服务器解析请求的SQL语句。
1:SQL计划缓存,经常用查询分析器的朋友大概都知道这样一个事实,往往一个查询语句在第一次运行的时候需要执行特别长的时间,但是如果你马上或者在一定时间内运行同样的语句,会在很短的时间内返回查询结果。
原因:
1):服务器在接收到查询请求后,并不会马上去数据库查询,而是在数据库中的计划缓存中找是否有相对应的执行计划,如果存在,就直接调用已经编译好的执行计划,节省了执行计划的编译时间。
2):如果所查询的行已经存在于数据缓冲存储区中,就不用查询物理文件了,而是从缓存中取数据,这样从内存中取数据就会比从硬盘上读取数据快很多,提高了查询效率.数据缓冲存储区会在后面提到。
2:如果在SQL计划缓存中没有对应的执行计划,服务器首先会对用户请求的SQL语句进行语法效验,如果有语法错误,服务器会结束查询操作,并用返回相应的错误信息给调用它的应用程序。
注意:此时返回的错误信息中,只会包含基本的语法错误信息,例如select 写成selec等,错误信息中如果包含一列表中本没有的列,此时服务器是不会检查出来的,因为只是语法验证,语义是否正确放在下一步进行。
3:语法符合后,就开始验证它的语义是否正确,例如,表名,列名,存储过程等等数据库对象是否真正存在,如果发现有不存在的,就会报错给应用程序,同时结束查询。
4:接下来就是获得对象的解析锁,我们在查询一个表时,首先服务器会对这个对象加锁,这是为了保证数据的统一性,如果不加锁,此时有数据插入,但因为没有加锁的原因,查询已经将这条记录读入,而有的插入会因为事务的失败会回滚,就会形成脏读的现象。
5:接下来就是对数据库用户权限的验证,SQL 语句语法,语义都正确,此时并不一定能够得到查询结果,如果数据库用户没有相应的访问权限,服务器会报出权限不足的错误给应用程序,在稍大的项目中,往往 一个项目里面会包含好几个数据库连接串,这些数据库用户具有不同的权限,有的是只读权限,有的是只写权限,有的是可读可写,根据不同的操作选取不同的用户 来执行,稍微不注意,无论你的SQL语句写的多么完善,完美无缺都没用。
6:解析的最后一步,就是确定最终的执行计划。当语法,语义,权限都验证后,服务器并不会马上给你返回结果,而是会针对你的SQL进行优化,选择不同的查询算法以 最高效的形式返回给应用程序。例如在做表联合查询时,服务器会根据开销成本来最终决定采用hash join,merge join ,还是loop join,采用哪一个索引会更高效等等,不过它的自动化优化是有限的,要想写出高效的查询SQL还是要优化自己的SQL查询语句。
当确定好执行计划后,就会把这个执行计划保存到SQL计划缓存中,下次在有相同的执行请求时,就直接从计划缓存中取,避免重新编译执行计划。
第三步:语句执行。
服务器对SQL语句解析完成后,服务器才会知道这条语句到底表态了什么意思,接下来才会真正的执行SQL语句。
些时分两种情况:
1):如果查询语句所包含的数据行已经读取到数据缓冲存储区的话,服务器会直接从数据缓冲存储区中读取数据返回给应用程序,避免了从物理文件中读取,提高查询速度。
2):如果数据行没有在数据缓冲存储区中,则会从物理文件中读取记录返回给应用程序,同时把数据行写入数据缓冲存储区中,供下次使用。
说明:SQL缓存分好几种,这里有兴趣的朋友可以去搜索一下,有时因为缓存的存在,使得我们很难马上看出优化的结果,因为第二次执行因为有缓存的存在,会特别快速,所以一般都是先消除缓存,然后比较优化前后的性能表现,这里有几个常用的方法:
DBCC DropCLEANBUFFERS
从缓冲池中删除所有清除缓冲区。
DBCC FREEPROCCACHE
从过程缓存中删除所有元素。
DBCC FREESYSTEMCACHE
从所有缓存中释放所有未使用的缓存条目。SQL Server 2005 数据库引擎会事先在后台清理未使用的缓存条目,以使内存可用于当前条目。但是,可以使用此命令从所有缓存中手动删除未使用的条目。
这只能基本消除SQL缓存的影响,目前好像没有完全消除缓存的方案,如果大家有,请指教。
结论:只有知道了服务执行应用程序提交的SQL的操作流程才能很好的调试我们的应用程序。
1:确保SQL语法正确;
2:确保SQL语义上的正确性,即对象是否存在;
3:数据库用户是否具有相应的访问权限。 注:
本文引用:
http://database.ctocio.com.cn/tips/210/7791210.shtml
http://tech.it168.com/a2008/0805/199/000000199573.shtml
[ORM]实现自己的O/R Mapping组件[二]-高效缓存的思考一
转载:http://www.cnblogs.com/william_fire/articles/126646.html
缓存在O/R Mapping组件中是非常重要的,一个比较好的缓存方案,可以大大地提高对象的使用效率,避免内存空间上的浪费。目前需要在构思架构部分,但缓存的实现 方式突然吸引了我,所以就对缓存进行一些思考,并研究了Gentle.NET与NHibernate的实现原理。 [这里的研究基于NHibernate 0.7.0]
NHibernate的实现方案
在NHibernate上,缓存的机制实现充分考虑了性能与扩展性,它与Gentle.NET在实现不同的是全部是基于Hashtable而 非HybridDictionary。一开始,我看见这样的实现的时候,还误以为,NHibenate过于模仿Java,而没有充分利用.NET的特性, 然而,在仔细分析之后,发现直接使用Hashtable是一个非常好的思路。
因为在HybridDictionary中,当插入一个新的对象到集合中时,该类会进行一个判断,如果集合中的元素多于9个时,则切换为 Hashtable模式,这看起来非常有意思。但实际上,在O/R Mapping中,集合的数量往往都是比较多的,尤其是在企业应用中,由于网络传输数据的延迟,缓存的实现是一个非常珍贵的资源,它大大的减轻了数据库服 务端的压力。就这个层次上来说,实际上实现ListDictionary并没有太大的意义。
NHibernate的缓存机制,比起Gentle.NET来说,要复杂一些。NHibernate中,缓存对象的创建实际上通过 CacheFactory产生的。这样做的原因是,在NHibernate中,缓存包括ReadOnlyCache、ReadWriteCache、 NonstrictReadWriteCache这三种缓存管理类,它们都继承自ICacheConcurrencyStrategy接口,可以非常方便 地添加应用了新策略的缓存管理类。
另外,在NHibernate中,缓存对象的类型也并不是不可变的,在现有的设计上,使用了 HashtableCacheProvider与HashtableCache两个类来描述Provider对象和Cache对象,分别继承于 ICacheProvider与ICache,如下代码所示:
可以看出,这样的实现是一个非常优秀的设计,意味着非常强大的扩展性与可维护性。
在NHibernate的缓存方案中,使用了锁模式进行管理,它的实现是模仿操作系统中的“原语”进行的,是一个基于线程安全的实现的方案,具体的管理方式类似PV操作的形式。
[Flash]Flash摄像头游戏课件设计与开发
转载:http://space.flash8.net/space/?246908/viewspace-452337.html
【摘 要】摄像头游戏是随着技术的进步产生出来的一种新的游戏形式,它摆脱了鼠标与键盘的限制,给游戏者以全新的互动体验。Flash 8提供的位图处理功能为Flash摄像头游戏的实现提供了可能。将摄像头游戏技术引入到Flash课件的开发,可以充分发挥其互动性和趣味性,与传统 Flash课件相比,具有其不可替代的优势。该文提出设计Flash摄像头游戏课件的基本原则,并以《水果乐园》课件为例,详细介绍了开发的过程和主要实 现方法,为摄像头游戏类课件制作提供了参考。
【关键词】Flash;摄像头游戏;课件
【中图分类号】G434【文献标识码】B【论文编号】1009—8097 (2008) 01—0107—05
一 引言
摄像头游戏是随着摄像头的普及和图形图像技术的不断发展而出现的。它摆脱了鼠标键盘的限制,给游戏者带来全新的游戏体验,能够实现许多传统游戏无法实现 的游戏效果。它通过摄像头将玩家投影到游戏中,由玩家自己做动作与游戏进行交互。画面中登场的敌人或道具都将对玩家的动作即刻做出反应,让玩家体验到前所 未有的新鲜感。
Flash课件是用Flash的形式表现教学内容一种课件形式。Flash课件充分利用了Flash的直观性、互动性以及 娱乐性,能够有效的调动学生的学习积极性,激发学生的学习乐趣,在当前的教学过程中发挥着越来越大的作用。Flash摄像头游戏课件整合了摄像头游戏和 Flash课件的优点,充分体现了“寓教于乐”的教学思想。它带给学生全新的游戏感受,能够极大的激发学生的学习兴趣。它能够激发学生的多种感观刺激,建 立强烈的真实感,通过游戏过程中学生与游戏的互动,还能够促进学生手、耳、眼以及肢体的相互协调,达到较好的教学效果。
二 Flash摄像头游戏课件的设计原则
Flash摄像头游戏课件具有较好参与性、体验性,能够有效增强学习效果,提高学习效率,特别适合于学生动作类技能的训练。在设计的过程中,应遵循如下原则,体现其独特的优势。
(1)教学性原则
这是所有课件,应用于教学的最基本的要求,该类课件也不例外,在设计的过程中,必须针对一定的教学目标,遵循认知过程的一般规律,组织教学内容和教学活动。紧密围绕“以学生学习为中心”的设计思路。
(2)易用性原则
摄像头游戏课件的交互,计算机对于学习者的动作回应,都是建立在动作检测,捕捉学习者动作影像的基础上。设计过程中应考虑让学习者的操作尽量简便,易于上手使用。做好课件使用的帮助或者使用手册。
(3)科学性原则
课件的内容,不能有科学性的错误,这也是设计中基本要求,必须对课件所有呈示的内容,进行严格仔细的审查,保证学生看到的所有知识点,都必须科学、准确,一般由课件开发小组中学科专家把好关。
(4)艺术性原则
如果一个课件的展示不但取得良好的教学效果,而日‘使人赏心悦目,使人获得美的享受,则说课件具有较高的艺术性。这样的课件是好的内容与美的形式的统一,美的形式能激发学生的兴趣,史好地表现内容。其表现有:展示的对象结构对称,色彩柔和,搭配合理,有审美性。
(5)体验性原则
摄像头游戏课件因其交互的多维化,全方位,检测学习者动作,使得学习者的沉浸感很强,要求学习者的个人形象和课件环境融合的过程中,能让学习能有身临其 境的感觉,所以课件在设计的过程中,注意课件环境,界面的元素,声音的效果,都必然能激发学习者的投入感。该原则是摄像头游戏课件设计过程中最重要的原 则,也是整个课件制作的关键,成败所在。
三 Flash摄像头游戏课件的设计与实现
在儿童英语教学中,引入Flash摄像头游戏课件,使得儿童摆脱键盘和鼠标的单一化的交互方式,而进行身体动作与计算机的交互,增强儿童学习的体验感,能较好的辅助英语的学习。以下是设计与开发的“水果乐园”课件的实现步骤和主要功能代码。
1 摄像头图像的捕获
使用Camera类实现对摄像头图像的捕获。Camera.get()方法返回对用于捕获视频的 Camera 对象的引用。当 SWF 文件尝试访问 Camera.get() 返回的摄像头时,Flash Player 显示“拒绝”对话框,用户可从中选择是允许还是拒绝对摄像头的访问,如图1所示。
setMode()方法将摄像头的捕获模式设置为最符合指定要求的本机模式。
//新建一个Camera对象,实例名为my_cam,并设置对象属性。
var my_cam:Camera = Camera.get();
my_cam.setMode(160,120,30,true);
2 摄像头图像的显示
1)显示图像
若要实际开始捕获视频,必须将 Camera 对象附加到 Video 对象。attachVideo(source:Object) : Void指定将在舞台上的 Video 对象的边界内显示的视频流 (source)。
//新建一个Video对象,将Camera对象附加到Video上。
var my_video:Video;
my_video.attachVideo(my_cam);
2)水平翻转图像
由于摄像头中看到的图像与游戏者本人的方向相反。为了方便游戏者操作游戏,在加载图像时应该把图像左右翻转。这一操作非常重要,将直接影响游戏者的游戏体验。
Video._rotation 属性可以实现对Video图像的旋转,但是无法实现左右翻转的效果。
Video._xscale 属性指示从 Video 对象注册点开始应用的 Video 对象的水平缩放比例 。当x轴的水平缩放比例为-100时,则可以实现对Video对象的水平翻转。默认注册点为 (0,0)。缩放本地坐标系统将影响 _x 和 _y 属性设置,这些设置是以整像素定义的。由于翻转时是以左上角主测点为中心,因此在水平翻转之后,Video对象的坐标虽然未发生改变,但是整体位置向左平 移了Video. _width个像素。因此翻转之后我们需要设置Video的新坐标为Video._x = Video._x+Video._width。
3 获取图像信息
要对获取的图像进行分析,必须将从摄像头获取的图像信息提 取出来。在Flash8中, Bitmap对象存储了图像中各点的RGB通道信息以及Alpha通道信息,可以使用new方法建立一个Bitmap对象。然后使用draw()方法提取 某一时刻video对象中的像素信息。
draw(source:Object,[matrix:Matrix],[colorTransform.:ColorTransform], [blendMode:Object], [clipRect:Rectangle], [smooth:Boolean]) : Void使用 Flash Player 矢量呈现器在目标图像上绘制源图像。使用 Matrix、ColorTransform、BlendMode 对象以及目标 Rectangle 对象来控制呈现的执行方式。或者也可以指定缩放时是否应对位图进行平滑处理。这只适用于当源对象是 BitmapData 对象时的情况。
import Flash.display.BitmapData;
//创建BitmapData对象
Var snapshot:BitmapData=new
BitmapData(output_vid._width,output_vid._height);
//从my_video获取当前图像
now.draw(my_video);
4 运动检测的实现
该部分是整个摄像头游戏实现的核心。主要利用了Flash 8的位图处理功能,即BitmapData类。
1)基本思路
我们可以使用getPixel(x,y)获取前一张图片上每个像素点的像素值,然后对比后一张图片中的每一点的像素值,当像素的亮度差值变化达到一定程度时,认为该点发生了运动变化。通过这种方式,得到前后两张图片的负片效果图。
//阀值
tolerance=10;
//获取当前图像now某一点的RGB值
nc=now.getPixel(x,y);
//红色通道
nr=nc>>16&0xff;
//绿色通道
ng=nc>>8&0xff;
//蓝色通道
nb=nc&0xff;
//计算该点亮度值
nl=Math.sqrt(nr*nr + ng*ng + nb*nb)
//获取前一快照before同一点的RGB值
bc=before.getPixel(x,y);
//红色通道
br=bc>>16&0xff;
//绿色通道
bg=bc>>8&0xff;
//蓝色通道
bb=bc&0xff;
//计算该点亮度值
bl=Math.sqrt(br*br + bg*bg + bb*bb);
//计算亮度值的变化
d=Math.round(Math.abs(bl-nl));
if(d>tolerance)
{
//该点发生了变化
}
但是这种方法存在计算效率问题。按图像大小为180*160像素,每秒30帧计算,每计算一副图片需要的计算次数为180*160*30。图像的像素越 大,需要的计算次数就越多。采用隔点检测的方法可以在一定程度上缓解计算压力,即每隔n个像素检测一次,这样电脑的计算次数减少为原来的1/n。
2)改进思路
Flash8提供的图像混合模式可以解决上面遇到的效率问题。Flash8提供了11种图像混合模式。每一种混合模式可以得到不同的混合效果。 Different 混合模式是基于两张图片之间的亮度差值进行计算,从而得到图片的负片效果。利用该模式,便可以得到移动像素的检测图像。
//将前一张快照before的图像绘制到当前快照now上,使用different混合模式
now.draw(before, new Matrix(), null, "difference");
图像中黑色表示没有发生移动,其他颜色表示发生了移动。由于Flash8提供的混合模式是采用C++编码实现的,因此运行效率要比Action Script编码更加高效。
5 动态显示检测图像
1)处理检测图像
通过以上两种方式得到的图像中色彩范围较广,这增加了统计上面的困难。通过使用 threshold() 方法,可以隔离和替换图像中的颜色范围,并对图像像素执行其它逻辑操作。threshold()函数根据指定的阈值测试图像中的像素值,并将通过测试的像 素设置为新的颜色值。这样,便得到了清晰醒目的检测图像。
//将大于阀值0xFF111111的像素替换为绿色。
myBitmap.threshold(myBitmap, myBitmap.rectangle, myBitmap.rectangle.topLeft, ">", 0xFF111111, 0xFF00FF00, 0x00FFFFFF, false);
2)检测图像的显示
Bitmap对象无法直接在舞台上显示,必须附着在MovieClip上面才能显示。因此可以使用MovieClip类的attachBitmap()方法,将获取的图像信息显示出来。
//创建一个影片剪辑来显示当前图像
this.createEmptyMovieClip(“当前”,this.getNextHighest Depth());
//将摄像头获取的图像显示在影片剪辑内
bitmap_mc.attachBitmap(now,1);
3)检测图像的动态显示
要实现检测图像的动态显示,必须不断的更新当前图像now和历史图像before,并进行混合处理。可以把检测图像的功能写成方法snapshot(),每隔100毫秒调用一次。
伪代码如下:
function snapshot() {
//获取当前图像
//获取检测图像
//将检测图像绘制到
//将检测图像中RGB超过阀值0xFF111111的部分替换成绿色
//显示检测图像
//本次检测完成之后,当前图像便成为了历史图像。为下一次检测做好准备。
preBitmap = nowBitmap.clone();
}
在该方法中,关键在于每次检测完成之后当前图像和历史图像的更新。
6 检测特定区域内的运动状态
经过上面几步操作,已经得到了检测图像。检测图像是对整幅图像的运动情况的反映。在游戏中,经常需要检测的是某一特定区域的运动状态。
1)检测某点的运动状态
因为已经到了检测图像,所以在检测某一点运动状态时,只需要判断检测图像上该点的RGB值是否大于阀值。
伪代码如下:
pix = myBitmap.getPixel(x, y);
if (pix大于阀值) {
//该点发生了运动
}
2)检测某区域的运动状态
由于灯 光因素、摄像头图像噪点等干扰因素的存在,每次只检测一个点容易造成检测结果的不稳定。因此,大多采用区域检测的方式。即在检测某点运动状态时,检测的不 仅仅是这个点,而是以该点开始的n*n个像素的区域(n的取值根据实际情况确定,在检测点数量较多时,n的值不宜取太大。)。如果检测区域内的像素点变化 数量超过一定阀值,如60%,则认为该区域发生了运动。
需要注意的一点是,由于看到的图像是经过水平翻转的,但原有的图像内部坐标系并没有发生变化,因此,检测时的取点位置也要水平翻转。
//以(rectx,recty)为顶点的rectw*recth的矩形区域的运动情况
function ismove(a, rectx, recty, rectw, recth) {
var i, j;
var sum = 0;
var pix;
for (i=1; i<=rectw; i++) {
for (j=1; j<=recth; j++) {
//图像水平翻转后取点位置相对变化
pix = a.getPixel(160-i-rectx, j+recty);
if (pix>132361) {
sum++;
}
}
}
//trace("sum="+sum);
if (sum>(recth*rectw/2)) {
return (sum);
//该区域发生了运动
} else {
return 0;
}
}
在该方法中,检测图像、检测区域顶点坐标、检测区域大小都为作为变量输入。采用这种方法,提高了程序的重用性,还可以实现对运动物体的检测。
3)检测点的设置
所谓检测点,实际是一个影片剪辑。在进行区域检测时,以该影片剪辑的坐标(x,y)确定检测区域的坐标位置。检测点可以是一个不可见的辅助点,也可以是 舞台中运动的物体。当把运动物体做为检测点时,随着物体的移动,检测区域也随之移动,因此可以实现对运动物体的检测。检测点的作用:一是可以起到辅助点的 作用,简化了检测区域定位的繁琐工作,使定位操作可视化。二是实现了代码的重用,起到了简化程序的作用。
7 实现摄像头运动检测的控制接口
游戏中检测点接口的作用就是返回舞台中被触碰的检测点编号。在接口函数中,调用了以上几个功能函数。返回值为检测点编号。
function istouch() {
var max:Number = 0;
var min:Number;
var num:Number;
for (var i = 1; i<=9; i++) {
//检测第i个检测点是否被触碰
min = ismove(myBitmap, this["point"+i]._x-xpoint, this["point"+i]._y-ypoint, 10, 10);
//每次只能激发一个点,选择9个点中移动最显著的一个
if (max
this._x = this._x-30*Math.cos(rot*Math.PI/180);
this._y = this._y-30*Math.sin(rot*Math.PI/180);
当子弹出界时,要使用this.unloadMovie()方法将该子弹实例销毁,释放内存。
2)气球系统的实现
该部分主要是实现气球的碰撞检测,判断气球是否被子弹击中需使用hitTest()函数。该函数有两种用法:
用法 1:根据 shapeFlag 设置,将 x 和 y 坐标与指定实例的形状或边框进行比较。如果 shapeFlag 设置为 true,则只计算在舞台上的实例实际占据的区域,并且如果 x 和 y 在任意一点重叠,则返回 true 值。
用法 2:计算 target 和指定实例的边框,如果它们在任意一点上重叠或交叉,则返回 true。
3)主控制系统的实现
主控制系统是实现互动功能的核心部分。主要工作就是把游戏中所有的功能模块集成起来,对各功能模块进行调度和显示。一方面,主控制系统要接收从摄像头功能接口传递的信息;一方面根据接收的信息执行相应的功能代码。
四 结束语
Flash摄像头游戏课件能够实现很多传统Flash课件难以实现的效果,特别是对操作技能的培养。我们开发的《水果乐园》课件提供给一些小学,进行了 英语学习实践,效果还不错。但是,对于Flash摄像头游戏课件应用于学生高级思维策略的训练,还没有进行深入的研究,特别是角色扮演型、问题探究型等学 习模式等实施,有待进一步的的探索和实践。
[SQL]SQL2005/2008中的CTE应用--递归查询
微软从SQL2005起引入了CTE(Common Table Expression)以强化T-SQL。这是一个类似于非持久视图的好东东。
按照MSDN介绍
1、公用表表达式 (CTE) 可以认为是在单个 Select、Insert、Update、Delete 或 Create VIEW 语句的执行范围内定义的临时结果集。CTE 与派生表类似,具体表现在不存储为对象,并且只在查询期间有效。与派生表的不同之处在于,CTE 可自引用,还可在同一查询中引用多次。
CTE 可用于:
- 创建递归查询。有关详细信息,请参阅使用公用表表达式的递归查询。
- 在不需要常规使用视图时替换视图,也就是说,不必将定义存储在元数据中。
- 启用按从标量嵌套 select 语句派生的列进行分组,或者按不确定性函数或有外部访问的函数进行分组。
- 在同一语句中多次引用生成的表。
使用 CTE 可以获得提高可读性和轻松维护复杂查询的优点。查询可以分为单独块、简单块、逻辑生成块。之后,这些简单块可用于生成更复杂的临时 CTE,直到生成最终结果集。可以在用户定义的例程(如函数、存储过程、触发器或视图)中定义 CTE。
2、公用表表达式 (CTE) 具有一个重要的优点,那就是能够引用其自身,从而创建递归 CTE。递归 CTE 是一个重复执行初始 CTE 以返回数据子集直到获取完整结果集的公用表表达式。当某个查询引用递归 CTE 时,它即被称为递归查询。递归查询通常用于返回分层数据,例如:显示某个组织图中的雇员或物料清单方案(其中父级产品有一个或多个组件,而那些组件可能还有子组件,或者是其他父级产品的组件)中的数据。
递 归 CTE 可以极大地简化在 Select、Insert、Update、Delete 或 Create VIEW 语句中运行递归查询所需的代码。在 SQL Server 的早期版本中,递归查询通常需要使用临时表、游标和逻辑来控制递归步骤流。有关公用表表达式的详细信息,请参阅使用公用表表达式。
这里举例说明如下:
为了描述方便,邀月特地列举了一个常见的自关联Table
表结构如下:
再插入一些测试数据
一个典型的应用场景是:在这个自关联的表中,查询以PKID为2的分类包含所有子分类。也许很多情况下,我们不得不用临时表\表变量\游标等。现在我们有了CTE,就简单多了
查询结果如下:
C_Name C_ParentName C_ParentCode
分类5 分类2 2
分类8 分类5 2/5
分类12 分类8 2/5/8
分类13 分类8 2/5/8
感觉怎么样?如果我只想查询第二层,而不是默认的无限查询下去,
可以在上面的SQL后加一个选项 Option(MAXRECURSION 5),注意5表示到第5层就不往下找了。如果只想找第二层,但实际结果有三层,此时会出错,
Msg 530, Level 16, State 1, Line 1
The statement terminated. The maximum recursion 1 has been exhausted before statement completion.
此时可以通过where条件来解决,而保证不出错,看如下SQL语句:
查询结果:
C_Name C_ParentName C_ParentCode
分类5 分类2 2
分类8 分类5 2/5
当然,我们不是说CTE就是万能的。通过好的表设计也可以某种程度上解决特定的问题。下面用常规的SQL实现上面这个需求。
注意:上面表中有一个字段很重要,就是C_Code,编码 ,格式如"1/2",“2/5/8"表示该分类的上级分类是1/2,2/5/8
这样,我们查询就简单多,查询以PKID为2的分类包含所有子分类:
from CategorySelf c where C_Code like '2/%'查询以PKID为2的分类包含所有子分类,且级别不大于3
from CategorySelf c where C_Code like '2/%' and C_Level<=3查询结果同上,略去。这里我们看出,有时候,好的表结构设计相当重要。
邀月于2009.10.23 1:36 完成分享。
有人很关心性能问题。目前没有测试过。稍后会附上百万级测试报告。不过,有两点理解邀月忘了补充:
一、CTE其实是面向对象的,运行的基础是CLR。一个很好的说明是With查询语句中是区分字段的大小写的。即"C_Code"和"c_Code"是不一样的,后者会报错。这与普通的SQL语句不同。
二、 这个应用示例重在简化业务逻辑,即便是性能不佳,但对临时表\表变量\游标等传统处理方式是一种业务层次上的简化或者说是优化。
助人等于自助! 3w@live.cn
[IIS]应用程序池运行状况
应用程序池运行状况
要点 只有在工作进程隔离模式下运行时才能使用该 IIS 6.0 功能。
在工作进程隔离模式中,可以配置应用程序池以监视其工作进程的运行状况,还可以监视整个应用程序池的运行状况。监视工作进程的运行状况包括检测工作进程不能为请求提供服务并进行适当的操作。例如,如果工作进程没有响应万维网发布服务(WWW 服务)发出的 Ping 请求,那么工作进程可能没有可用线程处理传入的请求。发生这种情况时,WWW 服务会终止该工作进程,或者释放工作进程并使其保持运行状态,然后启动一个新的工作进程替代它。管理员可以预先配置释放未正常运行的工作进程时所进行的操作,例如将工作进行附加到调试程序。
除了监视工作进程的运行状况,WWW 服务还可以检测整个应用程序池中当前的问题。例如,如果工作进行每几秒钟就异常终止一次,则 WWW 服务可以确定工作进程运行不正常并将其停止,从而防止运行不正常的应用程序影响其他应用程序池中的应用程序。
下列情况将导致应用程序池启动或停止:
- 启用快速失败保护会导致应用程序池停止。
- 达到时间限制的作业对象会导致应用程序池停止,随后会在时间窗口过期后启动。
- 由于试图使用不存在的标识而引起的配置错误会导致应用程序池停止。
- Windows 管理员在应用程序池中执行请求停止或启动。
监视应用程序池运行状况
工作进程 ping 使 WWW 服务能够检测到工作进程无法响应请求(即工作进程不能正常运行)。Ping 是在 WWW 服务与工作进程之间发送的消息。如果 Ping 成功,则 WWW 服务假设应用程序池运行正常。如果 Ping 失败(未收到工作进程的响应),WWW 服务则假设工作进程存在问题。如果存在问题,则 WWW 服务会终止工作进程或将其释放,然后在需要为新请求提供服务时启动新的工作进程。如果工作进程被释放后仍然处于运行状态,则 WWW 服务会运行由管理员配置的操作。
WWW 服务 Ping 功能的设置和 Ping 的频率由配置数据库属性
ISAPI 扩展能够声明自己运行不正常
可以生成 ISAPI 扩展应用程序以编程方式通知 IIS 它需要进行回收。此操作可以通过新的服务器支持函数 HSE_REQ_REPORT_UNHEALTHY 来完成。请参阅 MSDN Online 上 ISAPI 扩展参考中的 ServerSupportFunction。
要有效地使用此函数,运行应用程序的 IIS 服务器必须启用了工作进程 Ping(请参阅配置工作进程运行状况监视),因为正是在 Ping 操作期间,WWW 服务能够检测到 ISAPI 未正常运行以及工作进程应该回收。ISAPI 需要能够确定其未正常运行状况的内部机制,例如监视内部线程池的状态。您应该考虑到此类的编程会关闭 ISAPI 扩展在其间运行的工作进程。因此,在该工作进程中运行的所有应用程序都需要重新启动。
由于 ASP ISAPI 扩展能够监视其内部线程池的状态,因此可以实现使用此功能的逻辑。如果进入锁定状态的线程过多,它将会发出回收的信号。
运行状况检测的限制
运行状况检测不能用于未导致工作进程损坏的应用程序失败,也不能阻止工作进程中的可用线程。例如,返回无效响应代码(如 HTTP 500)但能够正常运行的应用程序仍然可以对 WWW 服务的 Ping 做出响应,除非应用程序是实现了特定代码指示其未正常运行状态的自定义 ISAPI 扩展。
启用调试操作
如果确定某个工作进程无法正常运行,您可能会希望在启动新的工作进程为请求提供服务之前,WWW 服务保持原工作进程处于运行状态以便进行调试,而不是终止该进程。通过启用应用程序池中的调试功能,可以通知 WWW 服务不要终止工作进程,而使其从为应用程序池服务中释放出来,并保持运行状态。
除了以释放状态运行运行不正常的工作进程,还可以将 WWW 服务配置为启动可执行应用程序或脚本(例如,向管理员发送电子邮件以便通知他们失败消息可以配置为启用调试功能的应用程序)。被认为是运行不正常的工作进程的“进程 id”是发送给可执行程序或脚本的第一个参数。
注意 已被释放并保持运行状态的工作进程可能仍然会终止。WWW 服务会使其处于运行状态。如果工作进程从不正常运行状态中恢复,则会检测到自己与 WWW 服务无关系,并自终止。因此,可能找到声明工作进程被释放的日志项,但是无法找到工作进程运行的证据。
由于工作进程不会由 IIS 从内存中删除,因此如果启用运行要被释放的未正常运行的工作进程,请考虑到可能需要处理被阻止的工作进程。如果管理员未正确处理用于调试的工作进程,那么您的计算机上会运行大量运行不正常的工作进程。并且这些工作进程可能会占用其他进程所需的资源。为了释放这些资源,可能需要快速终止这些工作进程。
快速失败保护
如果在指定时间段内发现指派给应用程序池的太多工作进程运行不正常,快速失败保护将停止该应用程序池。
应用程序池停止后,HTTP.sys 将返回停止服务消息(503:服务不可用),或者基于应用程序池
注意 如果一台计算机上主控了多个应用程序,则应该小心配置负载平衡器,或者将硬件切换为仅重新路由对于失败的应用程序池的通信。不要将请求路由出能够正常运行的应用程序池,因为它们仍然可以接收和处理请求。
由于请求不进入用户模式的处理,因此快速失败保护可以减少处理出现问题的应用程序的开销。因此,其他应用程序池受到保护,防止它们受到运行不正常的应用程序池的影响。
可以使用以下两种方式设置快速失败保护:
- 根据给定时间段(以分钟计)内工作进程失败数,配置 IIS 来放置处于快速失败状态的应用程序。
- 手动设置应用程序处于快速失败状态。
监视应用程序池运行状况的方案
监视应用程序池运行状况并进行修正操作至少需要完成下列步骤:
- 通过启用 WWW 服务 Ping 工作进程,允许 WWW 服务检测未正常运行的应用程序。请参阅配置工作进程运行状况监视。
- 指派给应用程序池的工作进程在指定时间段内损坏很多次时,将快速失败保护配置为允许禁用应用程序池。请参阅配置快速失败保护。
相关主题
- 有关配置 IIS 用于应用程序池运行状况监视的详细信息,请参阅配置工作进程运行状况监视。
- 有关设置快速失败保护的详细信息,请参阅配置快速失败保护。
Mikel




