久久人人97超碰超碰窝窝_国产精品久久久久久搜索_AV在线网站无码不卡的_亚洲AV永久精品爱情岛论坛

rexian

咨詢(xún)電話(huà):023-6276-4481

熱門(mén)文章

聯(lián)系方式

電 話(huà):023-6276-4481

郵箱:broiling@qq.com

地址:重慶市南岸區(qū)亞太商谷6幢25-2

當(dāng)前位置:網(wǎng)站首頁(yè) > 技術(shù)文章 > ASP.net 緩存技術(shù):延遲操作高級(jí)用法

ASP.net 緩存技術(shù):延遲操作高級(jí)用法

編輯:T.T 發(fā)表時(shí)間:2017-09-15 12:44:36
T.T

 為了便于理解,我特意為大家準(zhǔn)備了一個(gè)示例。整個(gè)示例由三部分組成:一個(gè)頁(yè)面,一個(gè)JS文件,服務(wù)端代碼。先來(lái)看頁(yè)面代碼:

<body>
<p> 為了簡(jiǎn)單,示例頁(yè)面只處理一條記錄,且將記錄的RowGuid直接顯示出來(lái)。<br />
實(shí)際場(chǎng)景中,這個(gè)RowGuid應(yīng)該可以從一個(gè)表格的【當(dāng)前選擇行】中獲取到。
</p>
<p> 當(dāng)前選擇行的 RowGuid = <span id="spanRowGuid"><%= Guid.NewGuid().ToString() %></span><br />
當(dāng)前選擇行的 Sequence
= <span id="spanSequence">0</span>
</p>
<p><input type="button" id="btnMoveUp" value="上移" />
<input type="button" id="btnMoveDown" value="下移" />
</p>
</body>

 

  頁(yè)面的顯示效果如下:

  處理頁(yè)面中二個(gè)按鈕的JS代碼如下:

// 用戶(hù)輸入的調(diào)整記錄的原因
var g_reason 
= null;

$(
function(){
$(
"#btnMoveUp").click( function() { MoveRec(-1); } );
$(
"#btnMoveDown").click( function() { MoveRec(1); } );
});

function MoveRec(direction){
if( ~~($("#spanSequence").text()) + direction < 0 ){
alert(
"已經(jīng)不能上移了。");
return;
}
if( g_reason == null ){
g_reason 
= prompt("請(qǐng)輸入調(diào)整記錄順序的原因:""由于什么什么原因,我要調(diào)整...");
if( g_reason == null )
return;
}

$.ajax({
url: 
"/AjaxDelaySendMail/MoveRec.fish",
data: { RowGuid: $(
"#spanRowGuid").text(), 
Direction: direction,
Reason: g_reason
},
type: 
"POST", dataType: "text",
success: 
function(responseText){
$(
"#spanSequence").text(responseText);
}
});
}

  說(shuō)明:在服務(wù)端,我使用了我在【用Asp.net寫(xiě)自己的服務(wù)框架】那篇博客中提供的服務(wù)框架, 服務(wù)端的全部代碼是這個(gè)樣子的:(注意代碼中的注釋)

/// <summary>
/// 移動(dòng)記錄的相關(guān)信息。
/// </summary>
public class MoveRecInfo
{
public string RowGuid;
public int Direction;
public string Reason;
}


[MyService]
public class AjaxDelaySendMail
{
[MyServiceMethod]
public int MoveRec(MoveRecInfo info)
{
// 這里就不驗(yàn)證從客戶(hù)端傳入的參數(shù)了。實(shí)際開(kāi)發(fā)中這個(gè)是必須的。

// 先來(lái)調(diào)整記錄的順序,示例程序沒(méi)有數(shù)據(jù)庫(kù),就用Cache來(lái)代替。
int sequence = 0;
int.TryParse(HttpRuntime.Cache[info.RowGuid] as string, out sequence);
// 簡(jiǎn)單地示例一下調(diào)整順序。
sequence 
+= info.Direction;
HttpRuntime.Cache[info.RowGuid] 
= sequence.ToString();


string key = info.RowGuid +"_DelaySendMail";
// 這里我不直接發(fā)郵件,而是把這個(gè)信息放入Cache中,并設(shè)置2秒的滑過(guò)過(guò)期時(shí)間,并指定移除通知委托
// 將操作信息放在緩存,并且以覆蓋形式放入,這樣便可以實(shí)現(xiàn)保存最后狀態(tài)。
// 注意:這里我用Insert方法。
HttpRuntime.Cache.Insert(key, info, 
null, Cache.NoAbsoluteExpiration,
TimeSpan.FromMinutes(
2.0), CacheItemPriority.NotRemovable, MoveRecInfoRemovedCallback);

return sequence;


private void MoveRecInfoRemovedCallback(string key, object value, CacheItemRemovedReason reason)
{
if( reason == CacheItemRemovedReason.Removed )
return; 
// 忽略后續(xù)調(diào)用HttpRuntime.Cache.Insert()所觸發(fā)的操作

// 能運(yùn)行到這里,就表示是肯定是緩存過(guò)期了。
// 換句話(huà)說(shuō)就是:用戶(hù)2分鐘再也沒(méi)操作過(guò)了。

// 從參數(shù)value取回操作信息
MoveRecInfo info 
= (MoveRecInfo)value;
// 這里可以對(duì)info做其它的處理。

// 最后發(fā)一次郵件。整個(gè)延遲發(fā)郵件的過(guò)程就處理完了。
MailSender.SendMail(info);
}
}

 

  為了能讓JavaScript能直接調(diào)用C#中的方法,還需要在web.config中加入如下配置:

  

<httpHandlers>
<add path="*.fish" verb="*" validate="false" type="MySimpleServiceFramework.AjaxServiceHandler"/>
</httpHandlers>

  

  好了,示例代碼就是這些。如果您有興趣,可以在本文的結(jié)尾處下載這些示例代碼,自己親自感受一下利用Cache實(shí)現(xiàn)的【延遲處理】的功能。

  其實(shí)這種【延遲處理】的功能是很有用的,比如還有一種適用場(chǎng)景:有些數(shù)據(jù)記錄可能需要頻繁更新,如果每次更新都去寫(xiě)數(shù)據(jù)庫(kù),肯定會(huì)對(duì)數(shù)據(jù)庫(kù)造成一定的壓力, 但由于這些數(shù)據(jù)也不是特別重要,因此,我們可以利用這種【延遲處理】來(lái)將寫(xiě)數(shù)據(jù)庫(kù)的時(shí)機(jī)進(jìn)行合并處理, 最終我們可以實(shí)現(xiàn):將多次的寫(xiě)入變成一次或者少量的寫(xiě)入操作,我稱(chēng)這樣效果為:延遲合并寫(xiě)入

  這里我就對(duì)數(shù)據(jù)庫(kù)的延遲合并寫(xiě)入提供一個(gè)思路:將需要寫(xiě)入的數(shù)據(jù)記錄放入Cache,調(diào)用Insert方法并提供slidingExpiration和onRemoveCallback參數(shù), 然后在CacheItemRemovedCallback回調(diào)委托中,模仿我前面的示例代碼,將多次變成一次。不過(guò),這樣可能會(huì)有一個(gè)問(wèn)題:如果數(shù)據(jù)是一直在修改,那么就一直不會(huì)寫(xiě)入數(shù)據(jù)庫(kù)。 最后如果網(wǎng)站重啟了,數(shù)據(jù)可能會(huì)丟失。如果擔(dān)心這個(gè)問(wèn)題,那么,可以在回調(diào)委托中,遇到CacheItemRemovedReason.Removed時(shí),使用計(jì)數(shù)累加的方式,當(dāng)?shù)竭_(dá)一定數(shù)量后, 再寫(xiě)入數(shù)據(jù)庫(kù)。比如:遇到10次CacheItemRemovedReason.Removed我就寫(xiě)一次數(shù)據(jù)庫(kù),這樣就會(huì)將原來(lái)需要寫(xiě)10次的數(shù)據(jù)庫(kù)操作變成一次了。 當(dāng)然了,如果是其它移除原因,寫(xiě)數(shù)據(jù)庫(kù)總是必要的。注意:對(duì)于金額這類(lèi)敏感的數(shù)據(jù),絕對(duì)不要使用這種方法。

  再補(bǔ)充二點(diǎn):

  1. 當(dāng)CacheItemRemovedCallback回調(diào)委托被調(diào)用時(shí),緩存項(xiàng)已經(jīng)不在Cache中了。

  2. 在CacheItemRemovedCallback回調(diào)委托中,我們還可以將緩存項(xiàng)重新放入緩存。

  有沒(méi)有想過(guò):這種設(shè)計(jì)可以構(gòu)成一個(gè)循環(huán)?如果再結(jié)合參數(shù)slidingExpiration便可實(shí)現(xiàn)一個(gè)定時(shí)器的效果。

  關(guān)于緩存的失效時(shí)間,我要再提醒一點(diǎn):通過(guò)absoluteExpiration, slidingExpiration參數(shù)所傳入的時(shí)間,當(dāng)緩存時(shí)間生效時(shí),緩存對(duì)象并不會(huì)立即移除, ASP.NET Cache大約以20秒的頻率去檢查這些已過(guò)時(shí)的緩存項(xiàng)。

 

  巧用緩存項(xiàng)的移除通知 實(shí)現(xiàn)【自動(dòng)加載配置文件】

  在本文的前部分的【文件依賴(lài)】小節(jié)中,有一個(gè)示例演示了:當(dāng)配置文件更新后,頁(yè)面可以顯示最新的修改結(jié)果。 在那個(gè)示例中,為了簡(jiǎn)單,我直接將配置參數(shù)放在Cache中,每次使用時(shí)再?gòu)腃ache中獲取。 如果配置參數(shù)較多,這種做法或許也會(huì)影響性能,畢竟配置參數(shù)并不會(huì)經(jīng)常修改,如果能直接訪(fǎng)問(wèn)一個(gè)靜態(tài)變量就能獲取到,應(yīng)該會(huì)更快。 通常,我們可能會(huì)這樣做: 『點(diǎn)擊此處展開(kāi)』

  但是,這種做法有一缺點(diǎn)就是:不能在配置文件更新后,自動(dòng)加載最新的配置結(jié)果。

  為了解決這個(gè)問(wèn)題,我們可以使用Cache提供的文件依賴(lài)以及移除通知功能。 前面的示例演示了移除后通知功能,這里我再演示一下移除前通知功能。

  說(shuō)明:事實(shí)上,完成這個(gè)功能,可以仍然使用移除后通知,只是移除前通知我還沒(méi)有演示,然而,這里使用移除前通知并沒(méi)有顯示它的獨(dú)有的功能。

  下面的代碼演示了在配置文件修改后,自動(dòng)更新運(yùn)行參數(shù)的實(shí)現(xiàn)方式:(注意代碼中的注釋) 『點(diǎn)擊此處展開(kāi)』

  改動(dòng)很小,只是LoadRunOptions方法做了修改了而已,但是效果卻很酷。

  還記得我在上篇博客【在.net中讀寫(xiě)config文件的各種方法】的結(jié)尾處留下來(lái)的問(wèn)題嗎? 這個(gè)示例就是我的解決方案。

 

  文件監(jiān)視技術(shù)的選擇

  對(duì)于文件監(jiān)視,我想有人或許會(huì)想到FileSystemWatcher。正好我就來(lái)說(shuō)說(shuō)關(guān)于【文件監(jiān)視技術(shù)】的選擇問(wèn)題。

  說(shuō)明,本文所有結(jié)論均為我個(gè)人的觀點(diǎn),僅供參考。

  這個(gè)組件,早在做WinForm開(kāi)發(fā)時(shí)就用過(guò)了,對(duì)它也是印象比較深的。

  它有一個(gè)包裝不好的地方是:事件會(huì)重復(fù)發(fā)出。比如:一次文件的保存操作,它卻引發(fā)了二次事件。

  什么,你不信? 正好,我還準(zhǔn)備了一個(gè)示例程序。

  說(shuō)明:圖片中顯示了發(fā)生過(guò)二次事件,但我只是在修改了文件后,做了一次保存操作而已。 本文的結(jié)尾處有我的示例程序,您可以自己去試一下。這里為了方便,還是貼出相關(guān)代碼:

private void Form1_Shown(object sender, EventArgs e)
{
this.fileSystemWatcher1.Path 
= Environment.CurrentDirectory;
this.fileSystemWatcher1.Filter 
= "RunOptions.xml";
this.fileSystemWatcher1.NotifyFilter 
= System.IO.NotifyFilters.LastWrite;
this.fileSystemWatcher1.EnableRaisingEvents 
= true
}

private void fileSystemWatcher1_Changed(object sender, System.IO.FileSystemEventArgs e)
{
string message = string.Format("{0} {1}.", e.Name, e.ChangeType);
this.listBox1.Items.Add(message);
}

 

  對(duì)于這個(gè)類(lèi)的使用,只想說(shuō)一點(diǎn):會(huì)引發(fā)的事件很多,因此一定要注意過(guò)濾。以下引用MSDN的一段說(shuō)明:

  Windows 操作系統(tǒng)在 FileSystemWatcher 創(chuàng)建的緩沖區(qū)中通知組件文件發(fā)生更改。如果短時(shí)間內(nèi)有很多更改,則緩沖區(qū)可能會(huì)溢出。這將導(dǎo)致組件失去對(duì)目錄更改的跟蹤,并且它將只提供一般性通知。使用 InternalBufferSize 屬性來(lái)增加緩沖區(qū)大小的開(kāi)銷(xiāo)較大,因?yàn)樗鼇?lái)自無(wú)法換出到磁盤(pán)的非頁(yè)面內(nèi)存,所以應(yīng)確保緩沖區(qū)大小適中(盡量小,但也要有足夠大小以便不會(huì)丟失任何文件更改事件)。若要避免緩沖區(qū)溢出,請(qǐng)使用 NotifyFilter 和 IncludeSubdirectories 屬性,以便可以篩選掉不想要的更改通知。

  幸運(yùn)的是,ASP.NET Cache并沒(méi)有使用這個(gè)組件,我們不用擔(dān)心文件依賴(lài)發(fā)引用的重復(fù)操作問(wèn)題。 它直接依賴(lài)于webengine.dll所提供的API,因此,建議在ASP.NET應(yīng)用程序中,優(yōu)先使用Cache所提供的文件依賴(lài)功能。

 

  各種緩存方案的共存

  ASP.NET Cache是一種緩存技術(shù),然而,我們?cè)贏SP.NET程序中還可以使用其它的緩存技術(shù), 這些不同的緩存也各有各自的長(zhǎng)處。由于ASP.NET Cache不能提供對(duì)外訪(fǎng)問(wèn)能力,因此,它不可能取代以memcached為代表的分布式緩存技術(shù), 但它由于是不需要跨進(jìn)程訪(fǎng)問(wèn),效率也比分布式緩存的速度更快。如果將ASP.NET Cache設(shè)計(jì)成【一級(jí)緩存】, 分布式緩存設(shè)計(jì)成【二級(jí)緩存】,就像CPU的緩存那樣,那么將能同時(shí)利用二者的所有的優(yōu)點(diǎn),實(shí)現(xiàn)更完美的功能以及速度。

  其實(shí)緩存是沒(méi)有一個(gè)明確定義的技術(shù),一個(gè)static變量也是一個(gè)緩存,一個(gè)static集合就是一個(gè)緩存容器了。 這種緩存與ASP.NET Cache相比起來(lái),顯然static變量的訪(fǎng)問(wèn)速度會(huì)更快,如果static集合不是設(shè)計(jì)得很差的話(huà), 并發(fā)的沖突也可能會(huì)比ASP.NET Cache小,也正是因?yàn)檫@一點(diǎn),static集合也有著廣泛的使用。 然而,ASP.NET Cache的一些高級(jí)功能,如:過(guò)期時(shí)間,緩存依賴(lài)(包含文件依賴(lài)),移除通知,也是static集合不具備的。 因此,合理地同時(shí)使用它們,會(huì)讓程序有著最好的性能,也同時(shí)擁有更強(qiáng)大的功能。