Chapter21:State Management

HTTP是无状态的协议,所以它不会记住东西。许多现在的网页应用都需要维持状态,它们需要记住登录过的用户,购物车的内容还有其余的东西。
在网页流行之前,标准的cs结构意味着扁平的客户端和扁平的服务器端,也许你的桌面应用可以访问数据库。状态有关的信息要么保持在客户端的内存中,要么保持在服务器端的数据库中。通常,你不能指望只有少量内存和一块硬盘的客户端来管理状态,传统cs设计的一个最重要方面是客户端总是和服务器端连接。很容易忘记HTTP是无状态的协议。通常,一个连接被建立又被扯断,对应每一次request和response。HTTP 1.1包括了一个存活技术,在TCP层面上提供了优化。即使有这些优化,服务器端仍然没有办法决定接下来的连接是来自同一个客户端。
ASP.NET的会话管理支持提供了一个很简单的API在会话中存储信息。有难度的东西都由framework完成了,我们不需要担心。这一章会展示一些状态管理技术,你可以根据你需要的挑选适合你的一款。

YOUR SESSION STATE CHOICE

给出一个无状态的协议例如HTTP和服务区端的ASP.NET,你如何管理网页的状态?如果你要记住管理状态的一件事,记住,没有正确的回答。有一些回答比其余的更正确。但是存在许多管理状态的方法。想想你上次的工程。你花了多少时间决定在哪里管理状态?重要的是理解每个方法的优劣。
你需要理解请求的生命周期和在这个过程中给状态管理提供的机会。
1 一个网页发起了一个HTTP GET请求,http://myserver/myapp/mypage.aspx 这个客户端浏览器从来没有访问过你的网站。
2 IIS和你的ASP.NET应用将mypage.aspx渲染后的HTML作为响应。另外,mypage.aspx返回了一个cookie,内中有一个唯一的id来追踪这个浏览器。记住cookie实际上是一个抽象的概念。cookie是通过设定HTTP Set-Cookie头来返回给客户端的。客户端允诺在接下来的HTTP请求中都会包含这个cookie。在这个例子中,状态是客户端和服务器端来回发送cookie在请求响应中维持的。
3 返回的HTML可能包含隐藏的textboxes 例如

<input type="hidden" value="somestate"/>

这些textboxes和cookies类似,因为如果表单提交,它们也会返回到客户端。cookies是划分域的,隐藏的表单是划分网页的。
4 在接下来的请求中,之前设定的cookies将返回到服务器端,如果这个请求是以HTTP POST提交表单,表单中所有的fields都会返回,不管隐藏与否。
5这个之前设定的cookie可以作为唯一的标示符用在服务器端的状态管理机制中。
你会注意到的一个重复主题就是客户端和服务器端心照不宣的来回传输信息。这个信息可以是在URL中,也可以是在HTTP表头上,甚至是作为提交表单的一个域。
在服务器端,你可以有一些选择。你可以基于你具有的存储含量,你想要存储的数据量,和你访问数据的频率和速度来权衡这些选择。

UNDERSTANDING THE SESSION OBJECT IN ASP.NET

Sessions and the Event Model

HttpApplication对象在HTTP协议请求的生命中抛出一系列事件。这个部分提到了所有,但是只对那些和session有关的事件作详细说明。
HttpApplication对象可以用在Page对象子类的任何事件中。Page有一个公共的property为Session,会自动的从当前的HttpContext获取Seesion。尽管看起来这个session对象寄生在page中,它实际上存活在HttpContext中,网页的公共Sessionproperty实际上获取了session状态的引用。这个便利不但让经典ASP编程者更加舒服,而且节省了很多码字的时间。

Configuring Session State Management

在网页中对Session对象的引用代码都是使用字典风格的语法,但是HttpSessionState对象使用了provider pattern来提取session状态存储的可能选择。你可以在web.config文件中通过改变sessionState元素来选择providers。ASP.NET装载了三种存储providers。
In-Process Session State Store 将session存储在ASP.NET的缓存中
Out-Process Session State Store 将session存储在ASP.NET状态服务器服务中
Sql Session state Store 将sessions存储在微软的SQL Server 数据库中。
web.config文件中sessionState元素的格式如下

<configuration>
    <system.web>
        <sessionState mode="Off|InProc|StateServer|SQLServer|Custom"../>
    </system.web>
</configuration>

In-Process Session State

当配置设为InProc时,session数据存储在HttpRuntime的内部缓存中。session state key是120比特的字符串来索引。当session state在in process中,对象作为活的引用存储。这个机制是难以置信的快,因为没有序列化存在,也没有对象离开process space。当然,如果你的对象在In-Process session对象里,它们不会被垃圾箱回收,因为还存在引用。
另外,因为这些对象存储在内存中,它们使用内存直到session时效截止。如果一个用户访问你的网站点击了一个网页,他可能在in-process session中存储50MB的xmldocument。如果这个用户再也不回来了,你在接下来的20分钟或者你配置的session时间里都消耗着这些内存。

InProc Gotchas

尽管这个InProc session model是最快的,也是默认的和最常见的,它的限制很显著。如果应用回收,所有的session数据都丢失了。除此之外,ASP.NET应用可能因为许多原因重启,例如
你改变了web.config或者Global.asax。

  • 你已经修改了\bin和\App_Code目录下的文件。
  • 在web.config或者machine.config文件中的processModel元素指示应- 用应该重启。内存有限或者请求队列有限都会造成应用重启。
  • 反病毒软件修改了任何上面提到的文件。
    这就是说,in-process session state对于只需要一个网页服务器的应用或者IP负载均衡正在将每一个用户返回到其原有的服务器情形都特别适用。
    现在想象一下,一个用户已经有了一个session key,但是返回到一个机器,这个机器不是创建其session的机器。在这种情况下,这个机器不知道这个session key,因此一个新的session创建了。这个session的基础是用户提供的session ID。尽管这个session key可能一样,这个目标机器不认识这个session因此没有和这个session key有关的数据。这个新的session是空的,无法预料的结果可能发生,有一个解决的办法,如果regenerateExpiredSessionId设为true,一个新的session ID产生了并且分配给了用户。

Web Gardening

Web gardening是一个应用于多处理系统的技术。处理器亲和力意味着,一个ASP.NET工作进程和一个特别的cpu有亲和力。这个技术通常只在很大的网页农场被启用。
不要忘记in-process session state只是in-process。即使你的网页应用只包含一个网页服务区,所有的IP交通都路由到这个服务器,你也不能保证接下来的请求会由同一个处理器服务。如果你在一个多处理器系统上使用web gardening,你禁止使用in-process session state否则你会失去session。

Storing Data in the Session Object

在下面这个简单的例子中,在一个button点击事件中,textbox的内容被加在Session对象中。用户然后点击到另一个网页,从Session对象中获取的数据被呈现在浏览器中。

Making Sessions Transparent

在Session对象中存储和获取数据必须使用类型投掷语法,因为存储的都是对象。

Advanced Techniques for Optimizing Session Performance

默认的情况下,所有的pages都有对Session的写入权利。因为有可能多余一个网页在同一时间发出请求。一个网页有一个读写锁在session上。一个网页如果有一个写的锁,所有其他的请求必须等到第一个请求结束。说白了,session的锁是对应session ID的,这些锁对于不同的session没有影响。
为了让你使用session的网页有最好的性能,ASP.NET允许你声明你的网页对Session对象的要求,这是通过EnableSessionState @Page属性的设定完成。

  • EnableSessionState=”True” 这个网页需要有对session的读写权利。在每个请求中。
  • EnableSessionState=”False” 这个网页不需要对session有任何权利。如果代码使用了Session对象,会抛出异常。
  • EnableSessionState=”ReadOnly” 这个网页对session只需要读的权利。通过设定SessionState为ReadOnly,同一时间可以有多个请求访问session。
    性能建议 如果你的page不需要session,将EnableSessionState设定为False。ASP.NET会在需要Session的网页前调度这个网页。如果你的应用根本不使用Session,在你的web.config文件中将Mode设定为off,这回减少整个应用的开销

Out-of-Process Session State

Out-of-process session state保持在一个叫aspnet_state.exe的进程中,该进程是作为Windows Service存在的。你可以开启ASP.NET state service,使用下面的命令行
net start aspnet_state
默认情况下,这个state service运行在TCP port为42424 但是这个port可以改变。 将web.config文件中的设置从InProc改为StateServer,如下面的代码所示。另外,你必须包括stateConnectionString属性,IP地址和端口号即session state service运行的地址和端口号。在一个网页农场(网页服务器群),你可以将session state service运行在任何一个服务器上或者一台完全不同的机器上。在这个例子中,这个state server运行在当前的机器上,这个ip地址是127.0.0.1,如果你将session state service运行在另一台机器上,确保那个端口号是开启的。
<configuration>
    <system.web mode="StateServer" stateConnectionString="tcpid=127.0.0.1:42424">
    </system.web>
</configuration>
因为你的应用代码运行在ASP.NET worker process(aspnet_wp.exe或者w3wp.exe),你的state service运行在另一个aspnet_state.exe进程中。存储在session中的对象不能使用引用存储。你的对象必须通过二进制序列化离开worker process。

SQL-Backed Session State

ASP.NET sessions也可以存储在一个SQL Server数据库中。InProc提供了速度,state server提供了一个平衡。将sessions存储在SQL Server中提供了恢复。 SQL-backed session state是通过aspnet_regsql.exe来配置的。这个工具增加和删除AN的许多特色,例如缓存依赖,session支持。当你从命令行运行aspnet_regsql.exe且不待任何选项时,它弹出一个GUI。

Extending Session State with Other Providers

AN session state建立在一个可扩展,provider-based的存储模型。

Cookieless Session State

在前面的例子中,AN的session state ID存储在cookie中,有一些设备不支持cookies,或许用户在浏览器中关闭了cookie。cookies的方便在于其值在请求响应之间传来传去。这意味着每一个HttpRequest包含了cookie的值,每一个HttpResponse包含着cookie的值。还有什么是在每一个请求响应之间传来传去的?这个答案是URL。 如果你在web.config文件中添加cookieless=”UseUrl”。AN不会将cookie作为session ID传来传去。它会修改每一个URL,将session ID包含在请求网页的前面。
<sessionState mode="SQLServer" cookieless="UseUrl" sqlConnectionString="data source=127.0.0.1;user id=sa;password=Wrox" />
注意这个session ID出现在URL中好像它是一个目录。session ID是一个字符串,仅仅包含了ASCII字符,

Choosing the Correct Way to Maintain State

现在你已经熟悉了AN中维持状态的不同选,下面是一些从真正的系统中得到的建议。进程中的session提供是最快的方法,当然,所有的东西都在内存中。这个provider在HttpApplication的缓存中,如果应用重启,都丢失了。 在作者的经验中,out-of-process的state service通常要比in-process provider慢15%,这是由序列化引起的开销。SQL session state通常比in-process慢25%。不要让这些数字太困扰你。

值得说的是 我们建议所有的开发者在开发过程中使用out-of-process session state,即使这不会你的应用部署的时候采取的方法。强迫自己使用进程外的provider允许你捕获那些没有Serializable属性的对象。如果你使用进程中的provider来设计你的整个网站,后来发现你需要转换为进程外或者SQL,你不能确保你的网站会正确运行。将进程外session state provider作为一个保险政策,而该政策不会向你索取任何代价。

THE APPLICATION OBJECT

Application对象类似于你的AN应用中包裹许多全局变量的大袋子。全局变量在其他编程环境中被认为是有害的,AN也不例外。你需要对你想要放进Application对象的东西和原因做一点思考。通常,这个更灵活的Cache对象会更有用。 这个Application对象对这个machine来说不是全局的。它对于HttpApplication来说是全局的。如果你是在网页农场的环境下运行,每一个网页服务器上的每一个AN应用都有其自己的Application对象。因为AN应用是多线程的,所以对于Application对象的管理需要通过Application.Lock和Application.Unlock方法完成。如果你的代码不直接调用Unlock方法,这个锁在HttpRequest的末尾会移除。 这个简单的例子展示了你如何锁住Application对象,然后插入对象。其他线程如果试图写入Application将要等到其解锁。这个例子假设已经有一个证书存在GlobalCount中。
Application.Lock();
Application["GlobalCount"]=(int)Application["GlobalCount"]+1;
Application.Unlock();
对象的引用可以存储在Application中,就像存储在Session中。但是它们不许被投掷回它们的类型。

QUERYSTRING

URL或者QueryString,是一个存储导航而不是用户数据的理想场所。QueryString是网站上最容易侵袭的元素,就像水能载舟亦能覆舟的道理一样。例如,如果你的导航主题使用你的网页ID(/mysite/mypage.aspx?id=54)不要想当然的将id投掷为int类型,如果你这样做了,你需要有个备份计划,如果投掷出问题了怎么办。一个好的想法是返回Response.StatusCode=404。 记住URL是你的用户首先看到的东西,甚至在你的HTML之前看到URL。Hackable URLs使得你的网站更容易访问。

COOKIES

记住当cookies刚引入的时候,许多用户都不知道cookie是什么,但是他们都被灌输了cookies是邪恶的理念,而且存储了他们的个人信息。那时候,个人信息的确有可能存储在cookie中。永远也不要存储敏感信息,例如用户ID或者密码在cookie中。cookies应当只用于存储非敏感的信息。cookies不应当被信任,他们的内容应该能够被验证。 当你存储信息在cookies(Response.Cookies,读使用Request.Cookies)中,记住这不同于在Session对象中存储数据。
  • Cookies在每一个请求响应中都是传来传去的。这意味着你为你的每一个HTTP GET和HTTP POST付上cookies的代价。
  • Cookies会被偷取,侦测和假冒。如果你的代码依赖于cookie的值,在你的代码中需要有相应的措施。
  • 如果cookie没有出现,你的应用会如何表现?如果它是4096字节呢?你的应用应该在没有cookie或者cookie太大的情况下自我愈合。
  • 在Base64编码任何大数据存储在cookie之前好好想想。如果你的设计依赖于这个技术,重新考虑使用session或者另外的存储手段。

POSTBACKS AND CROSS-PAGE POSTBACKS

AN使用postback的概念,通过服务器端的事件来预警开发者客户端的一个动作。如果用户点击了浏览器中的一个button,这个表单postback到服务器端。
然而,这个postback到同一个page的技术是违反直觉的。这一章涉及状态管理。Postbacks在AN1.x中引入,为网页开发提供了事件子系统。利用cross-page postback,数据可以传递到一个不同的网页。
AN2.0以上在Button控件中包含了PostBackUrl,当一个含有PostBackUrl性质的button被点击时,网页post到PostBackUrl指向的网页。当一个cross-page请求发生时,当前Page的PreviousPage性质包含了造成postback的page引用。为了从PreviousPage中获取一个控件的引用,使用controls的性质,或者FindControl()方法。

HIDDEN FIELDS VIEWSTATE AND CONTROLSTATE

Hidden input fields例如

<input type="hidden" name="myName">

以name/value对的形式POST,除了不渲染。将它们视为隐藏的textboxes。
ViewState,将其字节暴露为key/value对的集合,类似Session对象,但是将其自己渲染为一个隐藏的域

<input type="hidden" name="__VIEWSTATE" value="fafaga"/>

放在ViewState中的对象必须标记为Serializable。ViewState使用LosFormatter来序列化对象。
ViewState和hidden fields都不适合敏感数据。

USING HTTPCONTEXT.CURRENT.ITEMS FOR VERY SHORT-TERM STORAGE

HttpContext的Items集合是AN保护的最好的一个秘密。它是一个key/value的IDICTIONARY集合,里面包含了在单个HttpRequest生命中共享的对象。为什么你想要在这么短的时间内存储状态?考虑下面的原因

  • 当你在IHttpModules和IHttpHandlers之间分享内容时。
  • 当你在同一个网页同一个UserControl的两个实例间交流时 想象一下你写了一个UserControl来安置广告。这个UserControl的两个实例可以从HttpContext.Items中选择它们的广告,防止同一个网页出现相同的广告。
  • 当你存储有价值的结果 如果你有多个UserControl,每一个显示来自昂贵的数据库读取都过来的数据的一部分。这些UserControl可以从HttpContext.Items中获取所需要的数据。数据库只需要访问一次。
  • 如果一个HttpRequest中的不同单元需要对同样的数据或者相似的数据进行操作 如果你的饿数据的生命周期只是一个请求,考虑使用HttpContext.Items作为短期的缓存。

Items集合包含了对象,和本章其余集合一样,当你获取这些对象时,你需要将这些对象投掷为他们的原来类型。

public static MyData GetExpensiveData(int ID) {
string key = "data" + ID.ToString();
MyData d = (MyData) HttpContext.Current.Items[key]; if (d == null)
{
d = new Data();
//Go to the Database, do whatever... HttpContext.Current.Items[key] = d;
}
return d;

SUMMARY

这一章探索了在你的AN应用中管理状态的许多方法。Session对象和Providers提供了很多选择。每一个都有其优劣。服务器端的session state data可以有其独一标示key存储在cookie中,这个key也可以存储在URL中。Cookies也可以用来独立的存储少量数据。Hidden Fields,View State ,ControlState,postback和cross-postback提供了管理少量状态信息的可能。HttpContext.Current.Items为存储瞬态,只存活于单个HttpRequest的数据提供了完美的解决方案。QueryStrings存储适合导航的信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值