1. 程式人生 > >基於多級緩存的充電系統優化實踐

基於多級緩存的充電系統優化實踐

n) 解決 清理 moni 組織 超時時間 microsoft ole rim

基於多級緩存的充電系統優化實踐

原文是發表在文章中,剛看了下文章主要用於轉載,因此在隨筆中重新發布一下。

摘要

正如前文中《海量並發下充電業務優化實踐》所述,在充電過程中由於涉及到大量的實時數據處理,隨著設備規模的擴大,各個節點和服務均感受到較大的壓力。為了解決這個情況,文中探討內存緩存組件的使用及相關的過程優化,預計優化後對於Redis的調用頻率可以下降50%以上,極大緩解當前Redis的服務壓力的同時為未來的系統規模增長打下良好基礎。

充電過程中設計的實時數據主要有四類:遙信、遙測、電量和BMS數據。這四類數據在充電過程中持續上傳直到充電結束。雲端在終端充電過程中的處理壓力也是由這些實時數據引起的。

由於當前實時數據處理過程的無狀態性和通訊集群與實時數據處理集群之間負載均衡的存在(圖1),無法確定由哪個節點來處理哪些終端的實時數據,在此場景下只能依賴中心化的緩存服務,各個處理節點通過中心化的緩存服務來獲取數據恢復上下文。
技術分享圖片

圖1 充電和通訊服務部署圖
隨著終端規模的擴大,雖然實時數據處理集群的性能問題可以通過水平擴展來解決,但是卻給中心化的緩存和數據庫服務帶來了較大壓力。此時需要對於充電業務的實時數據處理流程進行優化(圖2)。優化從兩方面入手:

  1. 引入消息隊列來抗擊實時數據流量的突然變化,並且將處理節點和終端的對應關系相對固定;
  2. 改變實時數據處理節點的邏輯,由無狀態的服務改為帶狀態和上下文的處理節點。
    其中第1點是其他文章的主題,本文中主要涉及到第2點內容。

技術分享圖片

圖2 優化後充電和通訊服務部署圖
因此在目前的程序架構下造成訪問Redis和數據庫較多的問題。平時對於Redis的每分鐘調用次數(TPM)平均值為一百萬次(月均值),遇到設備繁忙或者網絡波動時,峰值有可能到達兩百萬次的量級,對於Redis的性能產生了很大的壓力,而且也給整個架構帶來一些隱患。
技術分享圖片

圖3 Redis單日訪問量(TPM_Pool是指各個連接池的每分鐘調用數)

對充電業務的優化改造方案計劃充分利用現有計算資源,增加內存緩存的使用力度,緩解Redis和數據庫的壓力。
使用內存緩存需要考慮的有如下三個方面:

  1. 控制內存占用的機制,不至於耗盡計算資源
  2. 內存緩存需要有多樣的超時機制,滿足復雜的業務需求
  3. 內存緩存跟主緩存之間的同步機制

基於上面一些考慮,在公共技術部門的幫助下確定使用MemoryCache作為內存緩存改造的主要組件。

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的構造函數構造對應名稱的實例來使用。

二 MemoryCache常用方法

1、Add方法

MemoryCache的Add方法有多個重載,該方法的用處是緩存項插入MemoryCache的實例中。

2、AddOrGetExisting方法

MemoryCache的AddOrGetExisting方法有多個重載,該方法的用處是緩存項插入MemoryCache的實例中,如果該緩存的key值已經存在,則返回該key值對應的緩存項。

3、Contains方法

該方法用於檢查MemoryCache實例中是否存在某個key值

4、CreateCacheEntryChangeMonitor方法

創建 CacheEntryChangeMonitor 實例。此更改監視器用於監視中指定的緩存條目 keys 集合項更改時觸發事件。

5、Get方法

返回某個key值對應的緩存項內容的引用。

6、GetCacheItem方法

從緩存中返回CacheItem實例形式的指定項。

7、GetEnumerator方法

創建緩存項的枚舉器,用於循環訪問緩存項的集合。

8、GetValues方法

該方法有多個重載,用於批量獲取一組key值對應的緩存項。

9、Remove方法

該方法有多個重載,用於從緩存中刪除緩存項。

10、Set方法

如果key值存在則更新緩存項,如果不存在則插入緩存項。

11、Trim方法

從緩存中刪除總數百分比的緩存項。刪除規則參考MemoryCache.Trim 方法 (Int32)

三 MemoryCache如何滿足內存緩存的要求

在前文中提到了對於內存緩存的三個基本要求,下面看MemoryCache如何滿足這三個方面的要求。
1、控制內存占用

可以通過在配置文件中配置cacheMemoryLimitMegabytesphysicalMemoryLimitPercentage屬性分別對於MemoryCache的實例占用內存的絕對值和物理內存相對值進行限制。

2、緩存項失效

在MemoryCache的實例增加緩存項時可以指定緩存項策略,即CacheItemPolicy類對象。該類中有兩個屬性SlidingExpiration和SlidingExpiration:

  • AbsoluteExpiration:絕對超時時間
  • SlidingExpiration:滑動超時時間,即多少時間未使用即超時

3、跟主緩存之間的同步機制

在指定緩存項策略時還可以在其ChangeMonitors屬性中增加監視器,實現監視數據源變化的功能。這些監視器都是派生自ChangeMonitor抽象類,目前在.Net中已經實現的監視器有四種:

  • CacheEntryChangeMonitor
  • FileChangeMonitor
  • HostFileChangeMonitor
  • SqlChangeMonitor

如果有必要的話,可以根據業務時間實現自定義的監視器實現內存緩存和主緩存之間的同步機制。

另外,根據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

參考資料

  • MemoryCache 類
  • ChangeMonitor 類

基於多級緩存的充電系統優化實踐