解讀asp.net中的觀察者模式(2)_.Net教程
推薦:解讀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 |
看似這個方案不錯,我嘗試著將我的程序修改為這樣,但是我將上面的代碼編寫完,我發(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)向另一個頁面,但自從有了
- asp.net如何得到GRIDVIEW中某行某列值的方法
- .net SMTP發(fā)送Email實例(可帶附件)
- js實現(xiàn)廣告漂浮效果的小例子
- asp.net Repeater 數(shù)據(jù)綁定的具體實現(xiàn)
- Asp.Net 無刷新文件上傳并顯示進度條的實現(xiàn)方法及思路
- Asp.net獲取客戶端IP常見代碼存在的偽造IP問題探討
- VS2010 水晶報表的使用方法
- ASP.NET中操作SQL數(shù)據(jù)庫(連接字符串的配置及獲取)
- asp.net頁面?zhèn)髦禍y試實例代碼
- DataGridView - DataGridViewCheckBoxCell的使用介紹
- asp.net中javascript的引用(直接引入和間接引入)
- 三層+存儲過程實現(xiàn)分頁示例代碼
- 相關(guān)鏈接:
- 教程說明:
.Net教程-解讀asp.net中的觀察者模式(2)
。