原文是發表在文章中,剛看了下文章主要用於轉載,所以在隨筆中從新發布一下。html
正如前文中《海量併發下充電業務優化實踐》所述,在充電過程當中因爲涉及到大量的實時數據處理,隨着設備規模的擴大,各個節點和服務均感覺到較大的壓力。爲了解決這個狀況,文中探討內存緩存組件的使用及相關的過程優化,預計優化後對於Redis的調用頻率能夠降低50%以上,極大緩解當前Redis的服務壓力的同時爲將來的系統規模增加打下良好基礎。數據庫
充電過程當中設計的實時數據主要有四類:遙信、遙測、電量和BMS數據。這四類數據在充電過程當中持續上傳直到充電結束。雲端在終端充電過程當中的處理壓力也是由這些實時數據引發的。緩存
因爲當前實時數據處理過程的無狀態性和通信集羣與實時數據處理集羣之間負載均衡的存在(圖1),沒法肯定由哪一個節點來處理哪些終端的實時數據,在此場景下只能依賴中心化的緩存服務,各個處理節點經過中心化的緩存服務來獲取數據恢復上下文。
安全
圖1 充電和通信服務部署圖
隨着終端規模的擴大,雖然實時數據處理集羣的性能問題能夠經過水平擴展來解決,可是卻給中心化的緩存和數據庫服務帶來了較大壓力。此時須要對於充電業務的實時數據處理流程進行優化(圖2)。優化從兩方面入手:服務器
圖2 優化後充電和通信服務部署圖
所以在目前的程序架構下形成訪問Redis和數據庫較多的問題。平時對於Redis的每分鐘調用次數(TPM)平均值爲一百萬次(月均值),遇到設備繁忙或者網絡波動時,峯值有可能到達兩百萬次的量級,對於Redis的性能產生了很大的壓力,並且也給整個架構帶來一些隱患。
網絡
圖3 Redis單日訪問量(TPM_Pool是指各個鏈接池的每分鐘調用數)數據結構
對充電業務的優化改造方案計劃充分利用現有計算資源,增長內存緩存的使用力度,緩解Redis和數據庫的壓力。
使用內存緩存須要考慮的有以下三個方面:架構
基於上面一些考慮,在公共技術部門的幫助下肯定使用MemoryCache做爲內存緩存改造的主要組件。併發
MemoryCache類是.Net 4.0以後出現的,其命名空間是System.Runtime.Caching(位於 System.Runtime.Caching.dll中)。MemoryCache繼承自ObjectCache並實現了IEnumerable和IDisposable接口。負載均衡
MemoryCache的構造函數有兩個:
MemoryCache(String, NameValueCollection)
MemoryCache(String, NameValueCollection, Boolean)
使用構造函數能夠構造出非默認的緩存實例並能夠自定義部分屬性。
MemoryCache類有7個屬性:
名稱 | 描述 |
---|---|
CacheMemoryLimit | 獲取計算機上緩存可以使用的內存量(以字節爲單位)。 |
Default | 獲取對默認 MemoryCache 實例的引用。 |
DefaultCacheCapabilities | 獲取緩存提供的功能的說明。 |
Item[String] | 經過使用 MemoryCache 類的實例的默認索引器屬性,獲取或設置緩存中的值。 |
Name | 獲取緩存的名稱。 |
PhysicalMemoryLimit | 獲取緩存可以使用的物理內存的百分比。 |
PollingInterval | 獲取在緩存更新其內存統計信息以前需等待的最大時間量。 |
MemoryCache的屬性都是隻讀屬性,設置這些屬性能夠經過程序配置文件中的<memoryCache>
配置節進行配置。配置實例:
<configuration> <system.runtime.caching> <memoryCache> <namedCaches> <add name="Default" cacheMemoryLimitMegabytes="10" physicalMemoryLimitPercentage="10" pollingInterval="00:01:00" /> </namedCaches> </memoryCache> </system.runtime.caching> </configuration>
注意上文中配置節的name屬性值爲Default
,若是寫其餘名字沒法設置MemoryCache.Default的相關屬性,只能使用MemoryCache的構造函數構造對應名稱的實例來使用。
一、Add方法
MemoryCache的Add方法有多個重載,該方法的用處是緩存項插入MemoryCache的實例中。
二、AddOrGetExisting方法
MemoryCache的AddOrGetExisting方法有多個重載,該方法的用處是緩存項插入MemoryCache的實例中,若是該緩存的key值已經存在,則返回該key值對應的緩存項。
三、Contains方法
該方法用於檢查MemoryCache實例中是否存在某個key值
四、CreateCacheEntryChangeMonitor方法
建立 CacheEntryChangeMonitor 實例。此更改監視器用於監視中指定的緩存條目 keys 集合項更改時觸發事件。
五、Get方法
返回某個key值對應的緩存項內容的引用。
六、GetCacheItem方法
從緩存中返回CacheItem實例形式的指定項。
七、GetEnumerator方法
建立緩存項的枚舉器,用於循環訪問緩存項的集合。
八、GetValues方法
該方法有多個重載,用於批量獲取一組key值對應的緩存項。
九、Remove方法
該方法有多個重載,用於從緩存中刪除緩存項。
十、Set方法
若是key值存在則更新緩存項,若是不存在則插入緩存項。
十一、Trim方法
從緩存中刪除總數百分比的緩存項。刪除規則參考MemoryCache.Trim 方法 (Int32)
在前文中提到了對於內存緩存的三個基本要求,下面看MemoryCache如何知足這三個方面的要求。
一、控制內存佔用
能夠經過在配置文件中配置cacheMemoryLimitMegabytes
和physicalMemoryLimitPercentage
屬性分別對於MemoryCache的實例佔用內存的絕對值和物理內存相對值進行限制。
二、緩存項失效
在MemoryCache的實例增長緩存項時能夠指定緩存項策略,即CacheItemPolicy類對象。該類中有兩個屬性SlidingExpiration和SlidingExpiration:
三、跟主緩存之間的同步機制
在指定緩存項策略時還能夠在其ChangeMonitors屬性中增長監視器,實現監視數據源變化的功能。這些監視器都是派生自ChangeMonitor
抽象類,目前在.Net中已經實現的監視器有四種:
若是有必要的話,能夠根據業務時間實現自定義的監視器實現內存緩存和主緩存之間的同步機制。
另外,根據MSDN的資料,MemoryCache的操做是線程安全的,這一點在處理充電實時數據的時候很是重要。
內存緩存是Redis數據和部分數據庫數據在內存中的映射。每一個處理節點的內存緩存只是Redis中數據的子集(圖4)。爲了使用方便,緩存數據在內存中的數據類型和數據結構跟Redis中的數據組織方式基本一致。
圖4 內存緩存和Redis數據集的關係
緩存項在獲取時若是獲取不到則從Redis中進行恢復,不然直接使用MemoryCache中的數據;MemoryCache緩存項設置後(好比調用Add或者Set方法)若是發現數據有變化則同步設置Redis的值,保持二者的數據同步。對於數據的超時時間則根據不一樣類型數據上傳頻率來肯定,超時自動清理,有須要時再從Redis中恢復數據。
使用MemoryCache將緩存放在內存中後,不但能夠顯著減小對於Redis數據的查詢。並且只在終端數據變化須要更新時,將數據同步到Redis中也能夠減小對於Redis的更新頻率。預計改造後對於Redis的查詢操做可以減小80%`90%,對於Redis的設置操做能夠減小60%~70%。
使用內存緩存對於原業務流程進行優化改造,看起來並不複雜,可是引入內存緩存後帶來的不只僅只是對於緩存存儲方式的改變,而是牽扯到一系列相關的流程的變化,包括引入消息隊列,合理分配處理接口的負載,多級緩存之間的同步機制等等。這些業務流程的變化對於充電穩定性的影響很是大,在系統運行過程當中改變業務流程,猶如在給行駛在高速路上的汽車更換輪胎,如何保證不一樣流程之間的切換也是須要在設計和部署時須要詳細考慮的問題。
對於實時數據處理系統而言,有多種多樣的架構設計,可是沒有一種架構是普適性的。各類架構都有本身的侷限性。隨着規模的擴大,須要不斷地優化架構設計,平衡系統各個模塊之間的負載,深刻挖掘系統總體性能。正如上文所言,爲了解決處理節點的高負載而選擇了無狀態的處理機制,規模擴大後爲了下降中心化的緩存服務(Redis)的壓力,又有必要引入內存緩存,緩解Redis的壓力,優化處理流程,提升系統內部模塊交互的費效比。經過一系列的優化改造,在不增長服務器的狀況下,系統總體穩定性和處理性能又會有較大提高,爲將來發展打下良好基礎。
原文連接:http://www.cnblogs.com/zhu-wj/p/7461104.html