日韩天天综合网_野战两个奶头被亲到高潮_亚洲日韩欧美精品综合_av女人天堂污污污_视频一区**字幕无弹窗_国产亚洲欧美小视频_国内性爱精品在线免费视频_国产一级电影在线播放_日韩欧美内地福利_亚洲一二三不卡片区

解讀asp.net中的觀察者模式(2)_.Net教程

編輯Tag賺U幣
教程Tag:暫無Tag,歡迎添加,賺取U幣!

推薦:解讀Asp.net教程:設(shè)計IP地址屏蔽功能
出于安全考慮,幾乎每個動態(tài)網(wǎng)站都具備IP地址屏蔽功能,而網(wǎng)上流傳的很多關(guān)于該功能的教程大都采用字符串保存和驗證IP地址,我認(rèn)為這是不太科學(xué)的,我試圖找到最佳的設(shè)計方案。 “I

第二個問題: 當(dāng)管理員頁面的ajax請求的時候,每兩個請求如何保存數(shù)據(jù)?呵呵,上面那個問題不是說了么,用單件,但是單件是全局存在的,我們的管理員是多個,每個管理員可以決定是否訂閱數(shù)據(jù),以及什么時候訂閱。想起來沒?除了全局?jǐn)?shù)據(jù)外我們還有Session

在管理頁面上我放置一個“開始監(jiān)視”的按鈕,這個按鈕使用ajax請求服務(wù)器端的一個HttpHandler,在Handler的ProcessRequest方法里這樣來做:

以下為引用的內(nèi)容:
[img]http://www.cnblogs.com/Images/OutliningIndicators/None.gif[/img]
Admin admin = context.Session["monitor_listener"] as Admin;
[img]http://www.cnblogs.com/Images/OutliningIndicators/None.gif[/img]
if(admin == null)
[img]http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif[/img]
[img]http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif[/img]
[img]http://www.cnblogs.com/Images/dot.gif[/img]{
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
admin = new Admin(Monitor.Current);
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
context.Session["monitor_listener"] = admin;
[img]http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif[/img]}
[img]http://www.cnblogs.com/Images/OutliningIndicators/None.gif[/img]

 

注意,由于這個Handler需要訪問Session,所以你需要讓這個Handler繼承IRequiresSessionState接口(為什么使用繼承而不用實現(xiàn)這個術(shù)語?實際上這個接口是一個標(biāo)記接口,沒有任何需要實現(xiàn)的成員,只是標(biāo)記這個Handler可以訪問Session,我不知道為什么MS不使用Attribute,是不是更合理些)

在管理頁面還有個一個SetInterval不斷的調(diào)用一個含有ajax的方法,去請求另外一個Handler,這個Handler將Admin收到的數(shù)據(jù)返回到web頁面,讓我們來看看這個Handler的部分實現(xiàn):

以下為引用的內(nèi)容:
img]http://www.cnblogs.com/Images/OutliningIndicators/None.gif[/img]
public void ProcessRequest(HttpContext context)
[img]http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif[/img]
[img]http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif[/img]
[img]http://www.cnblogs.com/Images/dot.gif[/img]{
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
context.Response.Buffer = true;
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
context.Response.ExpiresAbsolute = System.DateTime.Now.AddSeconds(-1);
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
context.Response.Expires = 0;
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
context.Response.CacheControl = "no-cache";
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
Admin admin = context.Session["monitor_listener"] as Admin;
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
if (admin == null || admin.MessageCollection == null ||
admin.MessageCollection.Count <= 0)
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
return;
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
string[] messages = new string[admin.MessageCollection.Count];
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
admin.MessageCollection.CopyTo(messages, 0);
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
StringBuilder sb = new StringBuilder();
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
for (int i = 0; i < messages.Length; i )
[img]http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif[/img]
[img]http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif[/img]
[img]http://www.cnblogs.com/Images/dot.gif[/img]{
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
sb.AppendFormat("<li>{0}</li>", messages);
[img]http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif[/img]
}
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
admin.MessageCollection.Clear();
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
context.Session["monitor_listener"] = admin;
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
context.Response.Write(sb);
[img]http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif[/img]
context.Response.Flush();
[img]http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif[/img]}
[img]http://www.cnblogs.com/Images/OutliningIndicators/None.gif[/img]

[OK,一個在asp.net環(huán)境中實現(xiàn)的觀察者模式基本上就算完成了,不過上面只有怎樣訂閱,那什么時候取消訂閱了,可以在Session_End事件里面取消訂閱

還查看了一些關(guān)于長連接的文章,發(fā)現(xiàn)這個不錯,準(zhǔn)備改進一下。

完整的代碼稍后提供,希望這塊轉(zhuǎn)頭能引來一些玉

寫完這個Post后本來想把完整代碼實現(xiàn)傳上來,后來看到不少園友提出異議,看了大家的留言后我也一直在思索:我為什么這樣做?當(dāng)初我是怎樣想到這個解決方案的?我在幾個解決方案之間做了取舍了么?我這樣做是不是矯枉過正了?經(jīng)過這些思考有了現(xiàn)在的這個Post。

首先我進一步談一下需求:

這是一個Web Application,有很多客戶端向服務(wù)器端提交數(shù)據(jù)(客戶端是C 的,以http-post方式向服務(wù)器端提交二進制數(shù)據(jù),服務(wù)器端解析這個二進制包,數(shù)據(jù)提交很頻繁),管理員可以進入監(jiān)視頁面瀏覽這些數(shù)據(jù),數(shù)據(jù)要即時的,客戶端發(fā)來一條,管理員屏幕上要馬上可以看到,允許多個管理員同時監(jiān)視即時數(shù)據(jù),所有管理員看到的數(shù)據(jù)都是一樣的(目前是這樣的,也許以后對管理員要分角色,各角色管理員看到的信息將不同)。

由于數(shù)據(jù)提交非常頻繁,客戶要求不允許頻繁的數(shù)據(jù)庫操作,所以我將數(shù)據(jù)保存在一個IList的緩存里面,當(dāng)這個IList的大小超過了我在配置文件里定義的大小的時候就將數(shù)據(jù)批量插入到數(shù)據(jù)庫。

下面我將以我當(dāng)初思考的思路為主線描述:

第一個版本:

以下為引用的內(nèi)容:
//在程序里我寫了一個靜態(tài)類,這個靜態(tài)類保存整個程序中共享的一些數(shù)據(jù),
相當(dāng)于原來的//Application對象,但是靜態(tài)成員是編譯期類型檢查的
public static ApplicationData
{
//這個隊列用來保存客戶端傳遞過來的數(shù)據(jù),當(dāng)隊列達到一定長度的時候同步到數(shù)據(jù)庫
public static Queue<DataHead> OperateDataList = new Queue<DataHead>();
//這個List也是保存客戶端傳遞過來的數(shù)據(jù)的,但它是為監(jiān)視準(zhǔn)備數(shù)據(jù)的,
//當(dāng)一個監(jiān)視頁面的請求到來的時候?qū)⑦@個List的數(shù)據(jù)Response過去,然后Clear這個//List
public static IList<DataHead> MonitorDataList = new List<DataHead>();
}
public class ReciveDataHandler : IHttpHandler
{
//……
Public void ProcessRequest(HttpContext context)
{
//解析從客戶端傳遞過來的數(shù)據(jù)
DataHead data = GetData(context);
OperateDataList.Add(data);
If(OperateDataList.Count > BufferSize)
{
//將數(shù)據(jù)寫入到數(shù)據(jù)庫
AddToBase();
}
MonitorDataList.Add(data);
}
}
//監(jiān)視頁面從這里獲取數(shù)據(jù)
public class MonitorHandler : IHttpHandler
{
//……
Public void ProcessRequest(HttpContext context)
{
If(MonitorDataList.Count > 0)
{
//將MonitorDataList里的數(shù)據(jù)Response出去
OutPut();
MonitorDataList.Clear();
}
}
}

說實話,我當(dāng)初做出這個的時候覺得一點問題都沒有,開始的時候客戶測試也沒有發(fā)現(xiàn)任何問題,終于有一天客戶和我同時測試部署在同一IIS的時候,問題出現(xiàn)了:只有一個監(jiān)視頁面有數(shù)據(jù)�?吹竭@個后我還百思不得其解,順著程序的執(zhí)行流程一步一步走下去,沒有找出任何錯誤。后來做了下日志,原來MonitorDataList是一個全局共享的,一個在監(jiān)視把數(shù)據(jù)Clear了后別人就無法獲取數(shù)據(jù)了。不知道有沒有人這樣做過:有時候忘記了自己正在做一個web程序,而web程序是一個并發(fā)的,對一些共享資源的訪問有著微妙的問題,如果沒有記住這點,按照程序流程的執(zhí)行步驟是找不出任何問題的。

怎么辦?再一看這不是事件訂閱所描述的場景么?所以就有了上一篇Post的Solution。不過那個方案受到不少人質(zhì)疑,其中金色海洋提出這樣的方法:

以下為引用的內(nèi)容:
Public class ReciveData : IHttpHandler
{
//……….
//將客戶端傳遞過來的數(shù)據(jù)存入數(shù)據(jù)庫
}

 

Public class MonitorHandler : IHttpHandler
{
//………
//為null的時候說明該管理員第一次監(jiān)視
If(Session[“id”] == null)
{
//根據(jù)時間從服務(wù)器取出數(shù)據(jù)
//并將取出數(shù)據(jù)的最后一個id保存在session中
Session[“id”] = id;
}
//不為null則說明該管理員已經(jīng)開始監(jiān)視了
Else
{
//根據(jù)session里保存的最后一個id,取出大于那個id的數(shù)據(jù)
Session[“id”] = currentId;
}
}

看似這個方案不錯,我嘗試著將我的程序修改為這樣,但是我將上面的代碼編寫完,我發(fā)現(xiàn)我不可以再進行下去了:上面的方案滿足不了我的需求,客戶明確要求了客戶端提交的數(shù)據(jù)要先緩存然后緩存超過配置大小(這個大小還需要可以在配置文件里面配置,以便可以經(jīng)過測試找出一個最合理的值),而這種Session記錄的方案是依靠數(shù)據(jù)庫來保存數(shù)據(jù),這個Session[“id”]就相當(dāng)于一個游標(biāo),這個游標(biāo)指向的是數(shù)據(jù)庫,那好,我們將Session[“id”]指向緩存數(shù)據(jù),但是請注意緩存隨時可能超過設(shè)置大小而被同步到數(shù)據(jù)庫并被清空。

分享:.Net教程之HTTP狀態(tài)碼200,301,302
跳轉(zhuǎn)非常常用,在哪里都一樣,這里的一些說明和用法也如此,不止適用于asp.net,其他語言也會用得到。跳轉(zhuǎn)的目的本來很簡單,就是當(dāng)用戶或系統(tǒng)需要時從一個頁面轉(zhuǎn)向另一個頁面,但自從有了

來源:模板無憂//所屬分類:.Net教程/更新時間:2009-08-01
相關(guān).Net教程