|
我所負(fù)責(zé)的XXX.CN平臺(tái)前期由于網(wǎng)站整體運(yùn)行效率低因此采用了文件緩存的方式(文件緩存就是當(dāng)某個(gè)頁(yè)面第一次接受用戶訪問(wèn)時(shí)將數(shù)據(jù)庫(kù)中獲取到的內(nèi)容轉(zhuǎn)化成xml文件的形式,并且存儲(chǔ)在服務(wù)器硬盤(pán)當(dāng)中,當(dāng)后面的人再來(lái)訪問(wèn)時(shí)就只需要直接讀取xml緩存文件即可,減少了讀取數(shù)據(jù)庫(kù)的次數(shù),從而達(dá)到提高網(wǎng)站運(yùn)行效率的目的),但是使用了這種方式其中有一個(gè)更新數(shù)據(jù)的問(wèn)題,在更新和讀取是就產(chǎn)生了并發(fā)的問(wèn)題,說(shuō)白了就是讀取和寫(xiě)入的矛盾,當(dāng)你的網(wǎng)站的訪問(wèn)量達(dá)到一定程度后,產(chǎn)生這種矛盾的概率越來(lái)越大,已至于訪問(wèn)者無(wú)法訪問(wèn)網(wǎng)站,流失了客戶,對(duì)于我們酒店預(yù)訂平臺(tái)來(lái)說(shuō),流失的不是客戶,而是白花花的金子,這個(gè)問(wèn)題已經(jīng)困撓了我許久,直到前幾天,我在坐公交時(shí)回想起前一天給面試的人打印試卷的事,突然腦子里出現(xiàn)了“復(fù)制”這個(gè)詞,并且把它擴(kuò)展開(kāi)想到了我們網(wǎng)站文件緩存的占用問(wèn)題上,且已經(jīng)用程序的方式成功運(yùn)用到我們的網(wǎng)站中,下面就講一下運(yùn)行過(guò)程。
首先,我們網(wǎng)站整站的緩存方式都是依靠的DataSet的ReadXml和WriteXml的方式實(shí)現(xiàn)的,這種方式在訪問(wèn)量不是很大的網(wǎng)站中是一點(diǎn)問(wèn)題都沒(méi)有的(最大可承受的日IP估計(jì)在8000-15000左右),但是當(dāng)你的網(wǎng)站日IP訪問(wèn)量到達(dá)20000時(shí),他就完全崩潰了,出現(xiàn)xml的并發(fā)占用問(wèn)題日趨嚴(yán)重,于是我們就采用了文件流的形式去操作,具體代碼如下:
寫(xiě)入:
Stream s = null;
s = File.Open(FileName, FileMode.Create, FileAccess.ReadWrite,FileShare.ReadWrite);
BinaryFormatter b = new BinaryFormatter();
b.Serialize(s, ds);
s.Close();
讀取:
Stream s = null;
s = File.Open(FileName, FileMode.Open, FileAccess.Read);
BinaryFormatter b = new BinaryFormatter();
ds = (DataSet)b.Deserialize(s);
s.Close();
這種方式在一定程度上解決了直接使用DataSet的ReadXml和WriteXml的方式帶來(lái)的問(wèn)題,但是當(dāng)網(wǎng)站的日訪問(wèn)量達(dá)到40000或更高時(shí),并發(fā)問(wèn)題依然存在,其實(shí)存在并發(fā)的根本原因不是我們用了什么方式去讀取或者寫(xiě)入(方式的不同的確在一定程度上可以解決一些問(wèn)題,但根本原因沒(méi)有得到根治),而是在兩個(gè)或者更多個(gè)進(jìn)程(有需要讀取的也有需要寫(xiě)入的)在爭(zhēng)搶同一個(gè)文件時(shí)程序如何給出一個(gè)可以讓雙方滿意的方案,于是順著這個(gè)思路,我有對(duì)程序做了以下改進(jìn):
//讀取鎖,可以讓一個(gè)文件被多個(gè)進(jìn)程同時(shí)讀取,也可以保證只被一個(gè)進(jìn)程改寫(xiě)
ReaderWriterLock locker = new ReaderWriterLock();//讀取鎖()
寫(xiě)入:
Stream s = null;
try
{
locker.AcquireWriterLock(1500);//寫(xiě)鎖定(寫(xiě)入時(shí)間最大允許在1500毫秒內(nèi)完成,超時(shí)就立即退出)
if (!File.Exists(FileName))
{
s = File.Create(FileName);
}
else
{
//創(chuàng)建此文件的一個(gè)副本,以供同時(shí)訪問(wèn)此文件的讀取進(jìn)程使用(就像打印機(jī)的復(fù)制功能),由于使用了寫(xiě)鎖定,其他的寫(xiě)入進(jìn)程都將轉(zhuǎn)化為讀取進(jìn)程,而讀取進(jìn)程是不存在并發(fā)問(wèn)題的
File.Copy(FileName,FileName.Replace(".xml","Temp.xml"),true);
s = File.Open(FileName, FileMode.Create, FileAccess.ReadWrite,FileShare.ReadWrite);
}
BinaryFormatter b = new BinaryFormatter();
b.Serialize(s, ds);
s.Close();
}
finally
{
if(s!=null)
{
s.Close();
}
locker.ReleaseWriterLock();//釋放寫(xiě)鎖定
//這里可以加入刪除臨時(shí)文件的代碼,但不建議這樣做,我測(cè)試了下,會(huì)產(chǎn)生新的讀寫(xiě)并發(fā)問(wèn)題。
}
讀取:
Stream s = null;
Stream sTemp = null;
try
{
locker.AcquireReaderLock(1500);//讀鎖定(當(dāng)所用文件被寫(xiě)鎖定時(shí)超時(shí)時(shí)間為1500毫秒)
s = File.Open(FileName, FileMode.Open, FileAccess.Read);
BinaryFormatter b = new BinaryFormatter();
ds = (DataSet)b.Deserialize(s);
s.Close();
}
catch//這里使用catch主要是因?yàn)楫?dāng)讀取方法所讀的文件正在被改寫(xiě)時(shí)會(huì)獲取空內(nèi)容導(dǎo)致異常,或者寫(xiě)入超時(shí)導(dǎo)致文件內(nèi)容出錯(cuò)時(shí)異常,或者讀鎖定超時(shí)后讀取臨時(shí)文件時(shí)剛好臨時(shí)文件被刪除時(shí)發(fā)生異常
{
locker.ReleaseReaderLock();//釋放鎖
locker.AcquireReaderLock(1500);//再次鎖定
if(File.Exists(FileName.Replace(".xml","Temp.xml")))
{
//讀取副本文件
sTemp = File.Open(FileName.Replace(".xml","Temp.xml"), FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
BinaryFormatter b = new BinaryFormatter();
ds = (DataSet)b.Deserialize(s);
sTemp.Close();
}
}
finally
{
if(s!=null)
{
s.Close();
}
if(sTemp!=null)
{
sTemp.Close();
}
locker.ReleaseReaderLock();
}
好了,以上就是最終的改進(jìn)方法,想通了很簡(jiǎn)單,就是創(chuàng)造個(gè)臨時(shí)文件而已,從這件事我覺(jué)得大家在解決一個(gè)問(wèn)題時(shí)不一定老是要從純技術(shù)的角度去思考問(wèn)題,之前我就想過(guò)用內(nèi)存緩存的方式去做,但總覺(jué)得,會(huì)有一個(gè)更簡(jiǎn)單的方法的,于是使用了這種看似非常老土,而又沒(méi)有任何技術(shù)含量的方式去解決了。不管使用哪種方式,能解決問(wèn)題那就是好方法。其實(shí)想想世間萬(wàn)物都是相通的,只要我們發(fā)揮人類善于發(fā)掘和聯(lián)想的能力,任何問(wèn)題都不是問(wèn)題。
另外,我在讀操作中使用了try{}catch{},雖然進(jìn)入catch流程的幾率很小,但總會(huì)影響效率,不知各位看客啊有什么更好的方法指點(diǎn)一下.
NET技術(shù):Web網(wǎng)站緩存文件并發(fā)問(wèn)題解決方案,轉(zhuǎn)載需保留來(lái)源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。