咨詢電話:023-6276-4481
熱門文章
電 話:023-6276-4481
郵箱:broiling@qq.com
地址:重慶市南岸區(qū)亞太商谷6幢25-2
為了便于理解,我特意為大家準備了一個示例。整個示例由三部分組成:一個頁面,一個JS文件,服務端代碼。先來看頁面代碼:
<body>
<p> 為了簡單,示例頁面只處理一條記錄,且將記錄的RowGuid直接顯示出來。<br />
實際場景中,這個RowGuid應該可以從一個表格的【當前選擇行】中獲取到。
</p>
<p> 當前選擇行的 RowGuid = <span id="spanRowGuid"><%= Guid.NewGuid().ToString() %></span><br />
當前選擇行的 Sequence= <span id="spanSequence">0</span>
</p>
<p><input type="button" id="btnMoveUp" value="上移" />
<input type="button" id="btnMoveDown" value="下移" />
</p>
</body>
頁面的顯示效果如下:
處理頁面中二個按鈕的JS代碼如下:
// 用戶輸入的調(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("請輸入調(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);
}
});
}
說明:在服務端,我使用了我在【用Asp.net寫自己的服務框架】那篇博客中提供的服務框架, 服務端的全部代碼是這個樣子的:(注意代碼中的注釋)
/// <summary>
/// 移動記錄的相關信息。
/// </summary>
public class MoveRecInfo
{
public string RowGuid;
public int Direction;
public string Reason;
}
[MyService]
public class AjaxDelaySendMail
{
[MyServiceMethod]
public int MoveRec(MoveRecInfo info)
{
// 這里就不驗證從客戶端傳入的參數(shù)了。實際開發(fā)中這個是必須的。
// 先來調(diào)整記錄的順序,示例程序沒有數(shù)據(jù)庫,就用Cache來代替。
int sequence = 0;
int.TryParse(HttpRuntime.Cache[info.RowGuid] as string, out sequence);
// 簡單地示例一下調(diào)整順序。
sequence += info.Direction;
HttpRuntime.Cache[info.RowGuid] = sequence.ToString();
string key = info.RowGuid +"_DelaySendMail";
// 這里我不直接發(fā)郵件,而是把這個信息放入Cache中,并設置2秒的滑過過期時間,并指定移除通知委托
// 將操作信息放在緩存,并且以覆蓋形式放入,這樣便可以實現(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ā)的操作
// 能運行到這里,就表示是肯定是緩存過期了。
// 換句話說就是:用戶2分鐘再也沒操作過了。
// 從參數(shù)value取回操作信息
MoveRecInfo info = (MoveRecInfo)value;
// 這里可以對info做其它的處理。
// 最后發(fā)一次郵件。整個延遲發(fā)郵件的過程就處理完了。
MailSender.SendMail(info);
}
}
為了能讓JavaScript能直接調(diào)用C#中的方法,還需要在web.config中加入如下配置:
<httpHandlers>
<add path="*.fish" verb="*" validate="false" type="MySimpleServiceFramework.AjaxServiceHandler"/>
</httpHandlers>
好了,示例代碼就是這些。如果您有興趣,可以在本文的結(jié)尾處下載這些示例代碼,自己親自感受一下利用Cache實現(xiàn)的【延遲處理】的功能。
其實這種【延遲處理】的功能是很有用的,比如還有一種適用場景:有些數(shù)據(jù)記錄可能需要頻繁更新,如果每次更新都去寫數(shù)據(jù)庫,肯定會對數(shù)據(jù)庫造成一定的壓力, 但由于這些數(shù)據(jù)也不是特別重要,因此,我們可以利用這種【延遲處理】來將寫數(shù)據(jù)庫的時機進行合并處理, 最終我們可以實現(xiàn):將多次的寫入變成一次或者少量的寫入操作,我稱這樣效果為:延遲合并寫入
這里我就對數(shù)據(jù)庫的延遲合并寫入提供一個思路:將需要寫入的數(shù)據(jù)記錄放入Cache,調(diào)用Insert方法并提供slidingExpiration和onRemoveCallback參數(shù), 然后在CacheItemRemovedCallback回調(diào)委托中,模仿我前面的示例代碼,將多次變成一次。不過,這樣可能會有一個問題:如果數(shù)據(jù)是一直在修改,那么就一直不會寫入數(shù)據(jù)庫。 最后如果網(wǎng)站重啟了,數(shù)據(jù)可能會丟失。如果擔心這個問題,那么,可以在回調(diào)委托中,遇到CacheItemRemovedReason.Removed時,使用計數(shù)累加的方式,當?shù)竭_一定數(shù)量后, 再寫入數(shù)據(jù)庫。比如:遇到10次CacheItemRemovedReason.Removed我就寫一次數(shù)據(jù)庫,這樣就會將原來需要寫10次的數(shù)據(jù)庫操作變成一次了。 當然了,如果是其它移除原因,寫數(shù)據(jù)庫總是必要的。注意:對于金額這類敏感的數(shù)據(jù),絕對不要使用這種方法。
再補充二點:
1. 當CacheItemRemovedCallback回調(diào)委托被調(diào)用時,緩存項已經(jīng)不在Cache中了。
2. 在CacheItemRemovedCallback回調(diào)委托中,我們還可以將緩存項重新放入緩存。
有沒有想過:這種設計可以構(gòu)成一個循環(huán)?如果再結(jié)合參數(shù)slidingExpiration便可實現(xiàn)一個定時器的效果。
關于緩存的失效時間,我要再提醒一點:通過absoluteExpiration, slidingExpiration參數(shù)所傳入的時間,當緩存時間生效時,緩存對象并不會立即移除, ASP.NET Cache大約以20秒的頻率去檢查這些已過時的緩存項。
巧用緩存項的移除通知 實現(xiàn)【自動加載配置文件】
在本文的前部分的【文件依賴】小節(jié)中,有一個示例演示了:當配置文件更新后,頁面可以顯示最新的修改結(jié)果。 在那個示例中,為了簡單,我直接將配置參數(shù)放在Cache中,每次使用時再從Cache中獲取。 如果配置參數(shù)較多,這種做法或許也會影響性能,畢竟配置參數(shù)并不會經(jīng)常修改,如果能直接訪問一個靜態(tài)變量就能獲取到,應該會更快。 通常,我們可能會這樣做: 『點擊此處展開』
但是,這種做法有一缺點就是:不能在配置文件更新后,自動加載最新的配置結(jié)果。
為了解決這個問題,我們可以使用Cache提供的文件依賴以及移除通知功能。 前面的示例演示了移除后通知功能,這里我再演示一下移除前通知功能。
說明:事實上,完成這個功能,可以仍然使用移除后通知,只是移除前通知我還沒有演示,然而,這里使用移除前通知并沒有顯示它的獨有的功能。
下面的代碼演示了在配置文件修改后,自動更新運行參數(shù)的實現(xiàn)方式:(注意代碼中的注釋) 『點擊此處展開』
改動很小,只是LoadRunOptions方法做了修改了而已,但是效果卻很酷。
還記得我在上篇博客【在.net中讀寫config文件的各種方法】的結(jié)尾處留下來的問題嗎? 這個示例就是我的解決方案。
文件監(jiān)視技術(shù)的選擇
對于文件監(jiān)視,我想有人或許會想到FileSystemWatcher。正好我就來說說關于【文件監(jiān)視技術(shù)】的選擇問題。
說明,本文所有結(jié)論均為我個人的觀點,僅供參考。
這個組件,早在做WinForm開發(fā)時就用過了,對它也是印象比較深的。
它有一個包裝不好的地方是:事件會重復發(fā)出。比如:一次文件的保存操作,它卻引發(fā)了二次事件。
什么,你不信? 正好,我還準備了一個示例程序。
說明:圖片中顯示了發(fā)生過二次事件,但我只是在修改了文件后,做了一次保存操作而已。 本文的結(jié)尾處有我的示例程序,您可以自己去試一下。這里為了方便,還是貼出相關代碼:
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);
}
對于這個類的使用,只想說一點:會引發(fā)的事件很多,因此一定要注意過濾。以下引用MSDN的一段說明:
Windows 操作系統(tǒng)在 FileSystemWatcher 創(chuàng)建的緩沖區(qū)中通知組件文件發(fā)生更改。如果短時間內(nèi)有很多更改,則緩沖區(qū)可能會溢出。這將導致組件失去對目錄更改的跟蹤,并且它將只提供一般性通知。使用 InternalBufferSize 屬性來增加緩沖區(qū)大小的開銷較大,因為它來自無法換出到磁盤的非頁面內(nèi)存,所以應確保緩沖區(qū)大小適中(盡量小,但也要有足夠大小以便不會丟失任何文件更改事件)。若要避免緩沖區(qū)溢出,請使用 NotifyFilter 和 IncludeSubdirectories 屬性,以便可以篩選掉不想要的更改通知。
幸運的是,ASP.NET Cache并沒有使用這個組件,我們不用擔心文件依賴發(fā)引用的重復操作問題。 它直接依賴于webengine.dll所提供的API,因此,建議在ASP.NET應用程序中,優(yōu)先使用Cache所提供的文件依賴功能。
各種緩存方案的共存
ASP.NET Cache是一種緩存技術(shù),然而,我們在ASP.NET程序中還可以使用其它的緩存技術(shù), 這些不同的緩存也各有各自的長處。由于ASP.NET Cache不能提供對外訪問能力,因此,它不可能取代以memcached為代表的分布式緩存技術(shù), 但它由于是不需要跨進程訪問,效率也比分布式緩存的速度更快。如果將ASP.NET Cache設計成【一級緩存】, 分布式緩存設計成【二級緩存】,就像CPU的緩存那樣,那么將能同時利用二者的所有的優(yōu)點,實現(xiàn)更完美的功能以及速度。
其實緩存是沒有一個明確定義的技術(shù),一個static變量也是一個緩存,一個static集合就是一個緩存容器了。 這種緩存與ASP.NET Cache相比起來,顯然static變量的訪問速度會更快,如果static集合不是設計得很差的話, 并發(fā)的沖突也可能會比ASP.NET Cache小,也正是因為這一點,static集合也有著廣泛的使用。 然而,ASP.NET Cache的一些高級功能,如:過期時間,緩存依賴(包含文件依賴),移除通知,也是static集合不具備的。 因此,合理地同時使用它們,會讓程序有著最好的性能,也同時擁有更強大的功能。