网站架构之缓存应用(摘录)
網(wǎng)站緩存這個話題并不新穎,但是能否將它用好,可是一門學(xué)問,同一件工具在不同人的手中會做出不同的事情來。這里我來分享總結(jié)下我對于網(wǎng)站架構(gòu)中緩存應(yīng)用的一些看法和經(jīng)驗,大家有好的想法可以補充?
??? 第一:緩存的一些基本概念。
??????? 1:緩存(CACHE)與緩沖(BUFFER)的區(qū)別,我認為緩存可以在某種程序上理解成一級緩存(Primary Cache),數(shù)據(jù)全局共享。緩沖則屬于二級緩存,只對一部分對象共享數(shù)據(jù),二級緩存在某種程序上主要是降低一級緩存組件的訪問壓力以及提高緩存的存取效率。
??????? 2:緩存的一些基本屬性:命中率,表示緩存命中的次數(shù)/總的請求數(shù),這是緩存設(shè)計的重要質(zhì)量指標之一;緩存執(zhí)行效率,例如GET,INSERT,DELETE等;容量,即緩存介質(zhì)的容量最大值;成本,即開發(fā)成本,部署成本,軟硬件成本。
??????? 3:緩存的問題,存儲介質(zhì)的選擇往往左右緩存的設(shè)計,緩存在不命中時往往會使性能下降。
??? 第二:網(wǎng)站中緩存的應(yīng)用場景:
??????? 1:可以緩存整個頁面的html,提高訪問響應(yīng)能力;
??????? 2:針對局部頁面元素進行緩存;
??????? 3:對復(fù)雜數(shù)據(jù)的結(jié)果進行緩存,例如一個查詢需要結(jié)合多個數(shù)據(jù)集,然后根據(jù)這些數(shù)據(jù)集進行相應(yīng)的運算,即使每個子集查詢有緩存,但還是需要額外的運算,這種情況可以考慮緩存計算后的結(jié)果。
??????? 4:對耗時的查詢進行緩存,例如產(chǎn)品列表頁的查詢。
??????? 5:和上下文相關(guān)的用戶數(shù)據(jù),例如用戶從訂單埴寫頁進入到訂單成功頁,或者是從產(chǎn)品列表頁點擊詳細產(chǎn)品進行預(yù)訂時的訂單填寫頁,此時這兩個頁面之間都需要傳遞大量的相關(guān)數(shù)值,我們可以把所有的數(shù)值封裝在一個類中,然后通過緩存進行通信。
??? 第三:影響緩存命中率的因素。
??????? 1:數(shù)據(jù)時實性,每個業(yè)務(wù)系統(tǒng)都對自己的數(shù)據(jù)有相應(yīng)的要求,有些數(shù)據(jù)的實時性非常強,像每日的股票信息,這種情況如果設(shè)置了緩存,緩存的命中率會特別低。
??????? 2:緩存粒度問題,一般來說是緩存的跨度太大,即此時的KEY值包含的條件太多,會出現(xiàn)緩存命中率特別低的情況。
??? 第四:提高緩存命中率的方法:
??????? 1:增大存儲介質(zhì)的容量;
??????? 2:對非常熱點的數(shù)據(jù)進行捕捉,可以采用實時更新緩存的方式來平衡緩存與實時性的問題,例如可以單獨開啟一個后臺服務(wù)來定時做更新緩存的工作。
??????? 3:調(diào)整緩存KEY值的算法,盡量保證緩存KEY的細粒度,KEY-VALUE就是很好的細粒度例子。
??????? 4:根據(jù)業(yè)務(wù)調(diào)整緩存的過期策略。
???????
??? 第五:如何實現(xiàn)緩存組件:
??????? 1:采用二級緩存架構(gòu),即在web server上設(shè)置二級緩存,這里的二級緩存即上面的提到的緩沖,只對具體的webserver進行數(shù)據(jù)共享,二級緩存可以考慮采用微軟企業(yè)庫的緩存組件來完成。由于一級緩存的實現(xiàn)往往都是單獨的服務(wù)器,為了減少緩存服務(wù)器的壓力,在webserver上對數(shù)據(jù)進行緩沖,在降低緩存服務(wù)器壓力的情況下最大的好處在于緩存的存取速度上。
??????? 2:一級緩存由單獨的緩存服務(wù)器來完成,至于緩存服務(wù)器采用哪種緩存方案,可以根據(jù)不同的場景來決定。如果考慮到部署的方便性,可以采用微軟企業(yè)庫來完成一級緩存,如果服務(wù)器允許,可以采用memcached來實現(xiàn)。
??????? cache服務(wù)器采用微軟企業(yè)庫實現(xiàn)的優(yōu)缺點:
??????? 1:優(yōu)點,開發(fā)以及部署都非常容易,不需要安裝第三方軟件等等;
??????? 2:缺點,需要自己開發(fā)客戶端功能以實現(xiàn)分布式,這里我們可以采用一致性hash算法來實現(xiàn),同時如果服務(wù)以WCF形式公布開,在訪問效率上也不是最優(yōu)的,比起memcached的通信方式要差一些。
??????? memcached的優(yōu)缺點:
??????? 1:優(yōu)點,通信方式比起wcf要高;
??????? 2:缺點,需要第三方服務(wù)的支持,需要在服務(wù)器上安裝memcached服務(wù),這好像也不是什么重要的缺點。
??????? 最后,貼出網(wǎng)站網(wǎng)頁在數(shù)據(jù)訪問上的流程圖,供大家參考,在下面的文章中我會把實現(xiàn)的方案分享出來。
?
上一篇我主要總結(jié)了網(wǎng)站緩存中的一些基本概念,以及我對于網(wǎng)站架構(gòu)緩存應(yīng)用的架構(gòu)實現(xiàn)思路,這篇主要分享下如何利用微軟企業(yè)庫來實現(xiàn)一二級緩存的緩存服務(wù)。
???
??? 為了能夠有效的管理緩存,需要對使用緩存方法上做一些規(guī)范,即要想使用緩存組件提供的服務(wù),需要在指定的配置文件中按照一定的規(guī)則來配置緩存條目,不允許在配置之處使用緩存。下面先展示下一條Cache條目的配置:
<Region name="MyBlog">
??? <SubRegion name="default">??
????? <Cache CacheMode="LocalCacheOnlyMode" Key="BlogListConfigKey" BufferType="AbsoluteTime" BufferTimeSeconds="300" CacheType="AbsoluteTime" CacheTimeMinutes="30" CachePriority="Normal"/>????
??? </SubRegion>
? </Region>
?
??
???? 上面的代碼中,其實由三部分構(gòu)成:
???? 1:主分區(qū):Regin,如果一個網(wǎng)站分很多子系統(tǒng),可以為每個子系統(tǒng)定義一個這樣的主分區(qū),例如食品頻道Food,手機頻道Mobile等;
???? 2:子分區(qū):SubRegion,主分區(qū)下面的子分區(qū),即對子系統(tǒng)更加小的劃分,可以根據(jù)子系統(tǒng)的功能來劃分,例如產(chǎn)品列表頁List,詳細頁Detail等;
???? 3:緩存條目:Cache,指具體的一則緩存條目規(guī)則,這里的緩存條目規(guī)則并不是指某一條緩存設(shè)置,而是指一條緩存規(guī)則,與具體的緩存條目是一對多的關(guān)系。
??????? <1>:CacheMode,設(shè)置緩存模式,是只有二級緩存還是即有一級緩存也有二級緩存,例如用戶頁面之間的信息溝通就只需要二級緩存,即緩存在web server上。而產(chǎn)品列表頁的數(shù)據(jù)屬于全局數(shù)據(jù),就需要即采用二級緩存也需要一級緩存。
??????? <2>:BufferType,指二級緩存的過期方式,分為絕對過期,滑動過期,文件依賴。
??????? <3>:BufferTimeSeconds,二級緩存Timespan中的秒。
??????? <4>:CacheType,一級緩存的過期方式,類型同BufferType.
??????? <5>:CacheTimeMinutes,一級緩存Timespan中的分鐘。
??????? <6>:CachePriority,緩存的優(yōu)先級。
???? 二級緩存實現(xiàn):
???? 第一:IWebCacheProvider,緩存提供者接口,它公布了所有緩存組件需要的方法,接口之所以加上了ServeiceContract標簽,是由于下面的一級緩存WCF服務(wù)也繼承此接口的原因。小提示:WCF服務(wù)契約對于方法重載的實現(xiàn)和普通方式有小小區(qū)別,請注意OperationContract標簽的定義。
????
?[ServiceContract]
??? public? interface IWebCacheProvider
??? {
??????? [OperationContract(Name = "Add")]
??????? void Insert(string key, object value, string region, string subRegion);
??????? [OperationContract(Name = "AddByAbsoluteTime")]
??????? void Insert(string key, object value, string region, string subRegion, MyCacheItemPriority scavengingPriority, AbsoluteTimeCacheDependency absoluteTimeCacheDependency);
??????? [OperationContract(Name = "AddBySlidingTime")]
??????? void Insert(string key, object value, string region, string subRegion, MyCacheItemPriority scavengingPriority, SlidingTimeCacheDependency slidingTimeCacheDependency);
??????? [OperationContract(Name = "AddByFile")]
??????? void Insert(string key, object value, string region, string subRegion, MyCacheItemPriority scavengingPriority, FileCacheDependency fileCacheDependency);
??????? [OperationContract]
??????? void Delete(string key, string region, string subRegion);
??????? [OperationContract]
??????? object Get(string key, string region, string subRegion);
??????? [OperationContract]
??????? void Clear(string region);
??????? [OperationContract]
??????? int Count(string region);
??? }
?
???
??? 第二:EntLibWebCacheProvider,微軟企業(yè)庫實現(xiàn)緩存實現(xiàn)類。代碼并不貼了,基本就是利用企業(yè)庫的緩存組件實現(xiàn)上面的接口。
???
???? 第三:MyWebCacheServiceClient,提供緩存客戶端的實例。包含了兩個重要的屬性:一級緩存實例,二級緩存實例。余下的就是調(diào)用EntLibWebCacheProvider來完成緩存的調(diào)用,以及根據(jù)緩存規(guī)則,選擇操作一級緩存以及二級緩存。
?????????? 說明:下面代碼中的GetMemcachedWebCacheProvider是下篇文章會提到的利用memcached實現(xiàn)一級緩存,由于需要支持一級緩存在企業(yè)庫以及memcached之間的切換才出現(xiàn)的邏輯。
?????
????? //一級緩存
??????? IWebCacheProvider PrimaryCacheProvider;
??????? //二級緩存
??????? IWebCacheProvider SecondaryCacheProvider;
???????? /// <summary>
??????? /// 實例化二級緩存
??????? /// </summary>
??????? /// <param name="configFilePath"></param>
??????? /// <returns></returns>
??????? private IWebCacheProvider GetSecondaryCacheProvider()
??????? {
??????????? IWebCacheProvider provider = null;
??????????? provider = WebCacheProviderFactory.GetEntLibWebCacheProvider(configFilePath);
??????????? return provider;
??????? }
??????? /// <summary>
??????? /// 獲取一級緩存
??????? /// </summary>
??????? /// <param name="hashKey"></param>
??????? /// <param name="configFilePath"></param>
??????? /// <returns></returns>
??????? private IWebCacheProvider GetPrimaryCacheProvider(uint hashKey)
??????? {
??????????? IWebCacheProvider provider = null;
??????????? string cacheType = WebConfig.ChannelConfig["CacheType"].ToString().ToLower();
??????????? switch (cacheType)
??????????? {
??????????????? case "memcached":
??????????????????? provider = WebCacheProviderFactory.GetMemcachedWebCacheProvider(configFilePath);
??????????????????? break;
??????????????? case "entlib":
??????????????????? provider = servicePool.GetServiceClient(hashKey) as IWebCacheProvider;
??????????????????? break;
??????????? }
??????????
??????????? return provider;
??????? }
?
???????
?????? 一級緩存的實現(xiàn):由于一級緩存也采用微軟企業(yè)庫實現(xiàn),而企業(yè)庫本身是不具備分布式功能的,就算是memcached,本身也不具備分布式,而在于客戶端的實現(xiàn),所以企業(yè)庫我們也可以實現(xiàn)分布式。
??????? 首先:我們把實現(xiàn)了緩存的組件以WCF服務(wù)形式分布出來,在多個服務(wù)器上部署,形成一個服務(wù)器群;
??????? 其實:實現(xiàn)分布式的客戶端,讓服務(wù)的請求均勻的分布到之前部署的WCF緩存服務(wù)器上。這里采用一致性hash算法實現(xiàn),主要是根據(jù)key的hash值以及服務(wù)器數(shù)量來選擇存儲的服務(wù)器,這里貼些主要的實現(xiàn)代碼,供參考:
???????
??????? 1:定義一個hash的服務(wù)器實例集合,為每個服務(wù)器分配250個hash值,這里的值可以根據(jù)實際情況調(diào)整。
private Dictionary<uint, isRoc.Common.Cache.CacheProvider.IWebCacheProvider> hostDictionary;
?
??????? 2:定義一個hash的服務(wù)實例key集合,用來統(tǒng)計所有服務(wù)器實例中的hash key。?
private uint[] hostKeysArray;
?
??????? 3:創(chuàng)建服務(wù)器列表的hash值。
/// <summary>
??????? /// 重新設(shè)置服務(wù)器列表
??????? /// </summary>
??????? /// <param name="hosts">服務(wù)器列表</param>
??????? internal void Setup(List<WebCacheServerInfo> hosts)
??????? {
??????????? hostDictionary = new Dictionary<uint, isRoc.Common.Cache.CacheProvider.IWebCacheProvider>();
??????????? List<isRoc.Common.Cache.CacheProvider.IWebCacheProvider> clientList = new List<isRoc.Common.Cache.CacheProvider.IWebCacheProvider>();
??????????? List<uint> hostKeysList = new List<uint>();
??????????? foreach (WebCacheServerInfo host in hosts)
??????????? {
??????????????? //創(chuàng)建客戶端
??????????????? isRoc.Common.Cache.CacheProvider.IWebCacheProvider client = ServiceProxyFactory.Create<isRoc.Common.Cache.CacheProvider.IWebCacheProvider>(host .HostUri );
??????????????? //Create 250 keys for this pool, store each key in the hostDictionary, as well as in the list of keys.
??????????????? for (int i = 0; i < 250; i++)
??????????????? {
??????????????????? uint key = 0;
??????????????????? switch (this.HashAlgorithm)
??????????????????? {
??????????????????????? case EHashAlgorithm.KetamaHash:
??????????????????????????? key = (uint)Math.Abs(KetamaHash.Generate(host.HostUri + "-" + i));
??????????????????????????? break;
??????????????????????? case EHashAlgorithm.FnvHash32:
??????????????????????????? key = BitConverter.ToUInt32(new ModifiedFNV1_32().ComputeHash(Encoding.UTF8.GetBytes(host.HostUri + "-" + i)), 0);
??????????????????????????? break;
??????????????????? }
??????????????????? if (!hostDictionary.ContainsKey(key))
??????????????????? {
??????????????????????? hostDictionary.Add(key, client);
??????????????????????? hostKeysList.Add(key);
??????????????????? }
??????????????? }
??????????????? clientList.Add(client);
??????????? }
??????????? //Hostlist should contain the list of all pools that has been created.
??????????? clientArray = clientList.ToArray();
??????????? //Hostkeys should contain the list of all key for all pools that have been created.
??????????? //This array forms the server key continuum that we use to lookup which server a
??????????? //given item key hash should be assigned to.
??????????? hostKeysList.Sort();
??????????? hostKeysArray = hostKeysList.ToArray();
??????? }
??????? 4:如何根據(jù)key值來查找具體的緩存服務(wù)實例呢,即具體的key存儲在哪一臺服務(wù)器上呢?根據(jù)傳入的key值,計算出對應(yīng)的hash值,然后經(jīng)過特定的算法計算出服務(wù)器實例地址。?
?internal isRoc.Common.Cache.CacheProvider.IWebCacheProvider GetServiceClient(uint hash)
??????? {
??????????? //Quick return if we only have one host.
??????????? if (clientArray.Length == 1)
??????????? {
??????????????? return clientArray[0];
??????????? }
??????????? //New "ketama" host selection.
??????????? int i = Array.BinarySearch(hostKeysArray, hash);
??????????? //If not exact match...
??????????? if (i < 0)
??????????? {
??????????????? //Get the index of the first item bigger than the one searched for.
??????????????? i = ~i;
??????????????? //If i is bigger than the last index, it was bigger than the last item = use the first item.
??????????????? if (i >= hostKeysArray.Length)
??????????????? {
??????????????????? i = 0;
??????????????? }
??????????? }
??????????? return hostDictionary[hostKeysArray[i]];
??????? }
??????? 總結(jié):本文簡單的介紹了如何利用微軟企業(yè)庫來實現(xiàn)具有兩級緩存的緩存組件,上篇我提到過,實現(xiàn)一級緩存也可以采用memcached,采用memcached可以不用自己開發(fā)分布式客戶端,目前有兩個成熟的解決方案:1:Memcached.ClientLibrary2:EnyimMemcached。下篇我來介紹一級緩存如何通過memcached實現(xiàn),以及如何讓組件在一級緩存上即支持企業(yè)庫也支持memcached。?
這篇來講如何利用memcached實現(xiàn)一級緩存,以及如何讓一級緩存組件支持在企業(yè)庫,memcached或者其它第三方實施方案之間的切換。memcached本人并沒有太多經(jīng)驗,如果文中有說的不對的地方,還希望批評指出,且文中關(guān)于memcached的代碼大多來自網(wǎng)絡(luò)。
??
???? 創(chuàng)建memcached實現(xiàn)類MemcachedWebCacheProvider,由它來繼承緩存提供者接口IWebCacheProvider,主里memcached客戶端我采用.NET memcached client library ,這個類庫很久沒有更新這過了,沒有和java版同步,有部分功能目前沒有實現(xiàn)。
???? 1:初始化memcached服務(wù),這段初始化代碼在程序中保證執(zhí)行一次就夠,一般可以放在gloabl文件中,或者是設(shè)置一個靜態(tài)變量來存儲服務(wù)的狀態(tài)。
?
?private void Setup()
??????? {
??????????? String[] serverlist = { "127.0.0.1:11211" };
??????????? this._pool = SockIOPool.GetInstance("default");
??????????? this._pool.SetServers(serverlist); //設(shè)置服務(wù)器列
??????????? //各服務(wù)器之間負載均衡的設(shè)置
??????????? this._pool.SetWeights(new int[] { 1 });
??????????? //socket pool設(shè)置
??????????? this._pool.InitConnections = 5; //初始化時創(chuàng)建的連接數(shù)
??????????? this._pool.MinConnections = 5; //最小連接數(shù)
??????????? this._pool.MaxConnections = 250; //最大連接數(shù)
??????????? //連接的最大空閑時間,下面設(shè)置為6個小時(單位ms),超過這個設(shè)置時間,連接會被釋放掉
??????????? this._pool.MaxIdle = 1000 * 60 * 60 * 6;
??????????? //通訊的超時時間,下面設(shè)置為3秒(單位ms),.NET版本沒有實現(xiàn)
??????????? this._pool.SocketTimeout = 1000 * 3;
??????????? //socket連接的超時時間,下面設(shè)置表示連接不超時,即一直保持連接狀態(tài)
??????????? this._pool.SocketConnectTimeout = 0;
??????????? this._pool.Nagle = false; //是否對TCP/IP通訊使用Nalgle算法,.NET版本沒有實現(xiàn)
??????????? //維護線程的間隔激活時間,下面設(shè)置為60秒(單位s),設(shè)置為0表示不啟用維護線程
??????????? this._pool.MaintenanceSleep = 60;
??????????? //socket單次任務(wù)的最大時間,超過這個時間socket會被強行中斷掉(當前任務(wù)失敗)
??????????? this._pool.MaxBusy = 1000 * 10;
??????????? this._pool.Initialize();
??????? }
?
??????? 2:獲取一個memcached客戶端。
???????
??????? private MemcachedClient GetClient()
??????? {
??????????? MemcachedClient client = new MemcachedClient();
??????????? client.PoolName = "default";
??????????? return client;
??????? }
?
??????? 3:根據(jù)memcached提供的功能實現(xiàn)IWebCacheProvider,代碼就不貼了,大家可以自己去試試。
???????
??????? 到此我們就利用memcached實現(xiàn)了一級緩存,由于.NET memcached client library 實現(xiàn)了分布式,我們只需要在多臺服務(wù)器上安裝上memcached服務(wù),在初始化memcached代碼中增加了服務(wù)器相關(guān)配置即可。String[] serverlist = { "127.0.0.1:11211" };
???????
??????? 如何讓一級緩存組件支持多實現(xiàn)方案之間的切換。
??????? MyWebCacheServiceClient:客戶端緩存組件實例,它來完成一級緩存與二級緩存之間的聯(lián)系,以及根據(jù)配置文件來選擇一級緩存的實施方案。
??????? 第一:CacheServiceMode,根據(jù)它就可以決定緩存是只緩存二級緩存還是兩級都緩存。
???????????????? 1:LocalCacheOnlyMode,只啟用web server上的二級緩存。
???????????????? 2:BufferedLCacheServerMode,即啟用web server上的二級緩存也啟用cache server上的緩存。
???????????????? 3:Off,關(guān)閉緩存功能。
??????? 第二:IWebCacheProvider service = this .GetPrimaryCacheProvider(hashKey);方式?jīng)Q定了一級緩存的實施方案。
????????
/// <summary>
??????? /// 獲取一級緩存
??????? /// </summary>
??????? /// <param name="hashKey"></param>
??????? /// <param name="configFilePath"></param>
??????? /// <returns></returns>
??????? private IWebCacheProvider GetPrimaryCacheProvider(uint hashKey)
??????? {
??????????? IWebCacheProvider provider = null;
??????????? string cacheType = WebConfig.ChannelConfig["CacheType"].ToString().ToLower();
??????????? switch (cacheType)
??????????? {
??????????????? case "memcached":
??????????????????? provider = WebCacheProviderFactory.GetMemcachedWebCacheProvider(configFilePath);
??????????????????? break;
??????????????? case "entlib":
??????????????????? provider = servicePool.GetServiceClient(hashKey) as IWebCacheProvider;
??????????????????? break;
??????????? }
??????????
??????????? return provider;
??????? }
?
???????? 插入緩存的邏輯:原理就是根據(jù)配置文件中的CacheMode來完成緩存級別的判定以及一級緩存的方案。
public void Insert(string key, object value, string region, string subRegion, CacheItemConfig cacheItemConfig)
??????? {
??????????? if (string.IsNullOrEmpty(key) || value == null)
??????????????? return;
??????????? //關(guān)閉模式,不使用緩存
??????????? if (Options.CacheServiceMode == ECacheServiceMode.Off)
??????????? {
??????????????? return;
??????????? }
??????????? else if (Options.CacheServiceMode == ECacheServiceMode.BufferedLCacheServerMode
??????????????? || Options.CacheServiceMode == ECacheServiceMode.LocalAndCacheServerAndSql
??????????????? || Options.CacheServiceMode == ECacheServiceMode.LocalCacheOnlyMode)
??????????? {//使用帶緩沖的模式
??????????????? if (Options.BufferType == ECacheDependencyType.SlidingTime)
??????????????? {
??????????????????? SecondaryCacheProvider.Insert(key, value, region, subRegion, MyCacheItemPriority.Normal, Options.BufferSlidingTime);
??????????????? }
??????????????? else if (Options.BufferType == ECacheDependencyType.AbsoluteTime)
??????????????? {
??????????????????? SecondaryCacheProvider.Insert(key, value, region, subRegion, MyCacheItemPriority.Normal, Options.BufferAbsoluteTime);
??????????????? }
??????????????? if (Options.CacheServiceMode == ECacheServiceMode.LocalCacheOnlyMode)
??????????????? {//只使用本地緩存
??????????????????? return;
??????????????? }
??????????? }
??????????? checkKey(key);
??????????? uint hashKey = hash(key);
??????????? try
??????????? {
??????????????? if (Options.CacheServiceMode == ECacheServiceMode.CacheServerMode
??????????????????? || Options.CacheServiceMode == ECacheServiceMode.BufferedLCacheServerMode
??????????????????? || Options.CacheServiceMode == ECacheServiceMode.CacheServerAndSql
??????????????????? || Options.CacheServiceMode == ECacheServiceMode.LocalAndCacheServerAndSql)
??????????????? {//CacheServer模式使用Cache服務(wù)器保存Cache?????????????????????????????????????
??????????????????? IWebCacheProvider service = this .GetPrimaryCacheProvider(hashKey);
??????????????????? byte[] byteValue = SerializationHelper.SaveToBinaryBytes(value);
??????????????????? var cachePriority = ModelConverter.ToRefClass(cacheItemConfig.CachePriority);
??????????????????? if (cacheItemConfig.CacheType == ECacheDependencyType.AbsoluteTime)
??????????????????? {
??????????????????????? AbsoluteTimeCacheDependency absTime = new AbsoluteTimeCacheDependency();
??????????????????????? absTime.AbsoluteTime = DateTime.Now.AddMinutes(cacheItemConfig.CacheTimeMinutes);
??????????????????????? service.Insert(key, byteValue, region, subRegion, cachePriority, absTime);
??????????????????? }
??????????????????? else if (cacheItemConfig.CacheType == ECacheDependencyType.SlidingTime)
??????????????????? {
??????????????????????? SlidingTimeCacheDependency slTime = new SlidingTimeCacheDependency();
??????????????????????? slTime.SlidingTime = new TimeSpan(0, cacheItemConfig.CacheTimeMinutes, 0);
??????????????????????? service.Insert(key, byteValue, region, subRegion, cachePriority, slTime);
??????????????????? }
??????????????? }
??????????? }
??????????? catch (Exception ex)
??????????? {//出現(xiàn)異常,保存到數(shù)據(jù)庫中
??????????????? servicePool.ReplaceServiceClient(hashKey);
??????????????? this.SendLogEmail(ex);
??????????? }
??????????
??????? }
?
???????
??????? 客戶端調(diào)用代碼:為了調(diào)用方便,創(chuàng)建一個CacheHelper來幫助完成:
public class CacheHelper
??? {
??????? /// <summary>
??????? /// 主分區(qū)
??????? /// </summary>
??????? public const string REGION = "MyBlog";
??????? /// <summary>
??????? /// 子分區(qū)
??????? /// </summary>
??????? public const string SUB_REGION = "default";
??????? public const string BlogListConfigKey = "BlogListConfigKey";
??????? #region 頁面間數(shù)據(jù)傳遞
??????? /// <summary>
??????? /// 新增頁面間傳遞數(shù)據(jù)到WebCache
??????? /// </summary>
??????? /// <returns>返回PageKeyID,用于頁面間傳遞的鍵值</returns>
??????? public static string InsertPageParams(string configKey, object obj,string pageKey)
??????? {
??????????? string result = null;
??????????? MyWebCacheServiceClient cacheClient = CacheClientFactory.GetWebCacheServiceClient(REGION, SUB_REGION, configKey);
??????????? cacheClient.Insert(
??????????????? MyWebCacheServiceClient.BuildKey(configKey,pageKey),
??????????????? obj,
??????????????? REGION,
??????????????? SUB_REGION);
??????????? return result;
??????? }
??????? /// <summary>
??????? /// 從Cache里獲取頁面?zhèn)鬟fCache
??????? /// </summary>
??????? /// <param name="key">FlightCacheKey里的常量</param>
??????? /// <param name="pageKeyID">頁面?zhèn)鬟f的鍵值</param>
??????? public static object GetPageParams(string configKey, string pageKey)
??????? {
??????????? object result = null;
??????????? MyWebCacheServiceClient cacheClient = CacheClientFactory.GetWebCacheServiceClient(REGION,
??????????????? SUB_REGION, configKey);
??????????? result = cacheClient.Get(
??????????????? MyWebCacheServiceClient.BuildKey(configKey, pageKey),
??????????????? REGION,
??????????????? SUB_REGION);
??????????? return result;
??????? }
??????? #endregion
??? }
對于web系統(tǒng)中增加緩存服務(wù),使用起來還是挺方便的,目前可采用的方案比較多,有微軟的企業(yè)庫,memcached等等。但如果需要很好的對項目中的緩存進行監(jiān)控管理,也不是一件特別容易的事情,例如:監(jiān)控緩存服務(wù)器上都有哪些項目使用了緩存,具體都有多少個key,大小,單個key的命中率以及過期時間等信息。有了這些信息,就非常容易排查內(nèi)存為什么快用完的問題,如果再提供手動過期緩存的服務(wù),就更好了,有的時候由于數(shù)據(jù)出錯,需要緊急讓緩存失效,此種辦法影響最小。
???
??? 這篇我來總結(jié)了針對memcached的緩存管理。
???
??? 其實memcached本身也提供了一些緩存統(tǒng)計信息,例如:當前總共的緩存數(shù)量,使用的內(nèi)存量,總獲取次數(shù),總的寫入次數(shù),總的命中次數(shù)等等,但這種統(tǒng)計信息粒度太大:
??? 1:無法具體到單個key,如果我們想針對某一個key統(tǒng)計它的命中率情況,就不好辦了。
??? 2:無法分析系統(tǒng)中都有哪些項目使用了key,哪個項目占用的key多,內(nèi)存多。
??? 3:無法實現(xiàn)手工過期,這種需求某些特殊情況下也是很有幫助的。
?
??? 既然memcached本身不提供,我這里采用了一種變通的方式來記錄我們特定的信息。
???
??? 首先我們引進一個概念:分區(qū),這個分區(qū)可以理解成電腦上的硬盤分區(qū),用戶可以把不同的文件放在不同的分區(qū)上,這樣在管理上也容易些,同樣分區(qū)底下有子分區(qū),就像電腦上的文件一樣,子分區(qū)下面就是具體的key了,對于我們的cache后臺管理,可以這樣理解,一個項目可以分配為一個分區(qū),按項目功能模塊可以分為不同的子分區(qū),子分區(qū)下來分散著N多key。
?
?
實現(xiàn)方案:我們可以對每個key的訪問記錄下它的一些信息,例如:大小,所屬分區(qū)名,過期時間,訪問命中率,然后把這些信息在每個memcached 實例上創(chuàng)建一個特殊key,用于存儲key的訪問信息。
???
??? 注意點:
?????? 1:由于記錄訪問信息都需要更新特殊key,如果過于頻繁,會影響正常的cache性能,所以可以考慮形成一個內(nèi)存隊列,當數(shù)量達到多少后(如果key使用頻率不高,還可以設(shè)定時間,當過了這個時間,即使數(shù)量不夠也進行更新),統(tǒng)一更新特殊key內(nèi)容。
?????? 2:由于memcached有單個key大小限制,所以對于這種統(tǒng)計信息key,不能過大,記錄key訪問信息時,盡量以文本形式存儲,這樣能保證最小。
?????? 3:每個實例中對應(yīng)一個用于存儲key訪問信息的key,這樣可以統(tǒng)計更多的key。
??????????
??? 監(jiān)控視圖:通過上面的努力,我們可以形成三個視圖:
??? 第一:memcached 實例視圖,以某個具體cache實例為單位,呈現(xiàn)memcached服務(wù)本身所提供的統(tǒng)計信息,還包含此實例中包含了多少個分區(qū),即實例上包含了多少個項目使用的緩存。
??? 第二:分區(qū)視圖,根據(jù)分區(qū)名稱,集合所有節(jié)點的數(shù)據(jù),最終匯總出統(tǒng)計數(shù)據(jù),例如可以統(tǒng)計酒店項目總共使用了多少個key等,這對分析key的分布情況比較有幫助。
??? 第三:key視圖,呈現(xiàn)具體key的訪問信息,以及手工過期功能。
?
??? 總結(jié):上面的方案雖然能實現(xiàn)需求,但在實際生產(chǎn)環(huán)境中,盡量不要打開這種監(jiān)控功能,需要的時候再打開,盡量讓cache的效率最高。
?
原文來自:雨楓技術(shù)教程網(wǎng) http://www.fengfly.com
原文網(wǎng)址:http://www.fengfly.com/plus/view-197176-1.html
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/lzjsky/archive/2012/09/05/2671464.html
總結(jié)
以上是生活随笔為你收集整理的网站架构之缓存应用(摘录)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解密:LL与LR解析 2(译,完结)
- 下一篇: 系统架构师