Author: wenchu.cenwc
Email: wenchu.cenwc@alibaba-inc.com
Memcached 介紹與分析
Memcached是一種集中式Cache,支持分布式橫向擴(kuò)展??偨Y(jié)幾個它的特點來理解一下它的優(yōu)點和限制。
Memory:內(nèi)存存儲,不言而喻,速度快,對于內(nèi)存的要求高,不指出的話所緩存的內(nèi)容非持久化。對于CPU要求很低,所以常常采用將Memcached服務(wù)端和一些CPU高消耗Memory低消耗應(yīng)用部屬在一起。(作為我們AEP正好有這樣的環(huán)境,我們的接口服務(wù)器有多臺,接口服務(wù)器對于CPU要求很高(由于WS-Security),但是對于Memory要求很低,因此可以用作Memcached的服務(wù)端部屬機(jī)器)
集中式Cache:避開了分布式Cache的傳播問題,但是需要非單點保證其可靠性,這個就是后面集成中所作的cluster的工作,可以將多個Memcached作為一個虛擬的cluster,同時對于cluster的讀寫和普通的memcached的讀寫性能沒有差別。
分布式擴(kuò)展:Memcached的很突出一個優(yōu)點,就是采用了可分布式擴(kuò)展的模式??梢詫⒉繉僭谝慌_機(jī)器上的多個Memcached服務(wù)端或者部署在多個機(jī)器上的Memcached服務(wù)端組成一個虛擬的服務(wù)端,對于調(diào)用者來說完全屏蔽和透明。提高的單機(jī)器的內(nèi)存利用率,也提供了scale out的方式。
Socket通信:傳輸內(nèi)容的大小以及序列化的問題需要注意,雖然Memcached通常會被放置到內(nèi)網(wǎng)作為Cache,Socket傳輸速率應(yīng)該比較高(當(dāng)前支持Tcp和udp兩種模式,同時根據(jù)客戶端的不同可以選擇使用nio的同步或者異步調(diào)用方式),但是序列化成本和帶寬成本還是需要注意。這里也提一下序列化,對于對象序列化的性能往往讓大家頭痛,但是如果對于同一類的Class對象序列化傳輸,第一次序列化時間比較長,后續(xù)就會優(yōu)化,其實也就是說序列化最大的消耗不是對象序列化,而是類的序列化。如果穿過去的只是字符串,那么是最好的,省去了序列化的操作,因此在Memcached中保存的往往是較小的內(nèi)容。
特殊的內(nèi)存分配機(jī)制:首先要說明的是Memcached支持最大的存儲對象為1M。它的內(nèi)存分配比較特殊,但是這樣的分配方式其實也是對于性能考慮的,簡單的分配機(jī)制可以更容易回收再分配,節(jié)省對于CPU的使用。這里用一個酒窖比喻來說明這種內(nèi)存分配機(jī)制,首先在Memcached起來的時候可以通過參數(shù)設(shè)置使用的總共的Memory,這個就是建造一個酒窖,然后在有酒進(jìn)入的時候,首先申請(通常是1M)的空間,用來建酒架,酒架根據(jù)這個酒瓶的大小分割酒架為多個小格子安放酒瓶,將同樣大小范圍內(nèi)的酒瓶都放置在一類酒架上面。例如20cm半徑的酒瓶放置在可以容納20-25cm的酒架A上,30cm半徑的酒瓶就放置在容納25-30cm的酒架B上?;厥諜C(jī)制也很簡單,首先新酒入庫,看看酒架是否有可以回收的地方,如果有直接使用,如果沒有申請新的地方,如果申請不到,采用配置的過期策略。這個特點來看,如果要放的內(nèi)容大小十分離散,同時大小比例相差梯度很明顯,那么可能對于使用空間來說不好,可能在酒架A上就放了一瓶酒,但占用掉了一個酒架的位置。
Cache機(jī)制簡單:有時候很多開源的項目做的面面俱到,但是最后也就是因為過于注重一些非必要性的功能而拖累了性能,這里要提到的就是Memcached的簡單性。首先它沒有什么同步,消息分發(fā),兩階段提交等等,它就是一個很簡單的Cache,把東西放進(jìn)去,然后可以取出來,如果發(fā)現(xiàn)所提供的Key沒有命中,那么就很直白的告訴你,你這個key沒有任何對應(yīng)的東西在緩存里,去數(shù)據(jù)庫或者其他地方取,當(dāng)你在外部數(shù)據(jù)源取到的時候,可以直接將內(nèi)容置入到Cache中,這樣下次就可以命中了。這里會提到怎么去同步這些數(shù)據(jù),兩種方式,一種就是在你修改了以后立刻更新Cache內(nèi)容,這樣就會即時生效。另一種是說容許有失效時間,到了失效時間,自然就會將內(nèi)容刪除,此時再去去的時候就會命中不了,然后再次將內(nèi)容置入Cache,用來更新內(nèi)容。后者用在一些時時性要求不高,寫入不頻繁的情況。
客戶端的重要性:Memcached是用C寫的一個服務(wù)端,客戶端沒有規(guī)定,反正是Socket傳輸,只要語言支持Socket通信,通過Command的簡單協(xié)議就可以通信,但是客戶端設(shè)計的合理十分重要,同時也給使用者提供了很大的空間去擴(kuò)展和設(shè)計客戶端來滿足各種場景的需要,包括容錯,權(quán)重,效率,特殊的功能性需求,嵌入框架等等。
幾個應(yīng)用點:小對象的緩存(用戶的token,權(quán)限信息,資源信息)。小的靜態(tài)資源緩存。Sql結(jié)果的緩存(這部分用的好,性能提高相當(dāng)大,同時由于Memcached自身提供scale out,那么對于db scale out的老大難問題無疑是一劑好藥)。ESB消息緩存。
集成設(shè)計
為什么需要集成?直接使用現(xiàn)有的兩個Java實現(xiàn)Memcached是否就可以了?
當(dāng)前集成主要為了兩方面考慮,首先是方便的配置使用,如何將Memcached內(nèi)嵌到類似于ASF以及其他框架中去,并且通過配置文件方便使用,這就需要作部分的集成工作,這部分工作主要是定義了配置文件以及通過Stax去解析配置的功能。然后是如何管理Memcached,這部分內(nèi)容包括了初始化,運行期檢測,資源回收的工作。最后是擴(kuò)展,這里的擴(kuò)展分成兩部分(功能的擴(kuò)展以及框架實現(xiàn)的擴(kuò)展),功能擴(kuò)展例如當(dāng)前擴(kuò)展了虛擬的cluster,可以讓多個memcached Client組成一個虛擬的cluster,如果通過放入cluster的方式放入到其中一個Cache Client中的話,那么就可以在整個cluster都作好備份,這樣其實可以根據(jù)memcached的單機(jī)多實例以及多機(jī)多實例作交互備份,提高可靠性。當(dāng)然后續(xù)還有很多可以擴(kuò)展的內(nèi)容,這里只是一個開頭??蚣軐崿F(xiàn)的擴(kuò)展指的是這里采用了類似于Jdk的JAXP的框架設(shè)計,只是規(guī)定了框架API結(jié)構(gòu),至于實現(xiàn)者動態(tài)載入,這個和ASF等現(xiàn)在可擴(kuò)展的框架一樣,提供了很方便的擴(kuò)展點,后續(xù)的設(shè)計中會提到。
接口設(shè)計類圖:

圖1 Cache接口包類圖
ICache和IMemcachedCache實現(xiàn)的是最基本的Cache的功能,只是IMemcachedCache有所增強(qiáng),提供了對于虛擬的Cluster的操作,批量操作,統(tǒng)計的功能。ICacheManager和IMemcachedCacheManager分別是對于上面兩個Cache的管理類,根據(jù)配置文件解析,初始化客戶端池,建立虛擬集群,銷毀客戶端池等工作。
圖2 Memcached 實現(xiàn)包
省略了一些輔助類定義。這部分是具體的實現(xiàn),同時可以在圖上看到spi包內(nèi)的CacheManagerFactory就是用來提供擴(kuò)展使用的接口。只需要定義在jar的META-INF下面建立services目錄,建立兩個名為:com.alisoft.xplatform.asf.cache.IMemcachedCacheManager和com.alisoft.xplatform.asf.cache.spi.CacheManagerFactory的文件就可以替換MemcachedCacheManager和CacheManagerFactory的實現(xiàn)類,從而改變Memcached Client實現(xiàn)機(jī)制。如果沒有這兩個文件在Classpath目錄下面,那么默認(rèn)將會使用當(dāng)前框架中的兩個實現(xiàn)。
圖3 Memcached的結(jié)構(gòu)圖
Memcached Server就是部署在不同服務(wù)器或者在同一臺服務(wù)器上的Memcached實例,一般采用后臺守護(hù)進(jìn)程方式運行。SocketPool是客戶端連接到服務(wù)端的Socket通信層,Memcached Client可以歸屬為虛擬的Cluster,MemcachedCacheManager作用是管理Cluster和Cache。從這個結(jié)構(gòu)圖可以看出客戶端的每一層都是很獨立,這樣有利于層次的交互,以及組合擴(kuò)展。
測試與使用
1. 配置:
需要有一個名為memcached.xml的文件在classpath中,可以在jar里面也可以在任意classpath可以找的到的地方,需要注意的是,CacheManager實現(xiàn)了對于多個memcached.xml merge的功能。
具體的配置內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?>
<memcached>//總標(biāo)簽
//memcached Client的配置,也就是一個IMemcachedCache的配置。Name必須填,表示Cache的名稱,socketpool必須填,表示使用的遠(yuǎn)程通信連接池是哪一個,參看后面對于socketpool的定義。后面都是可選的,第三個參數(shù)表示傳輸?shù)臅r候是否壓縮,第四個參數(shù)表示默認(rèn)的編碼方式
<client name="mclient1" socketpool="pool1" compressEnable="true" defaultEncoding="UTF-8" >
<!--errorHandler></errorHandler-->//可定義錯誤處理類,一般不需要定義
</client>
<client name="mclient2" socketpool="pool2" compressEnable="true" defaultEncoding="UTF-8" >
</client>
//socketpool是通信連接池定義,每一個memcached Client要和服務(wù)端交互都必須有通信連接池作為底層數(shù)據(jù)通信的支持,name必填,表示名字,同時也是memcached client指定socketpool的依據(jù),failover表示對于服務(wù)器出現(xiàn)問題時的自動修復(fù)。initConn初始的時候連接數(shù),minConn表示最小閑置連接數(shù),maxConn最大連接數(shù),maintSleep表示是否需要延時結(jié)束(最好設(shè)置為0,如果設(shè)置延時的話那么就不能夠立刻回收所有的資源,如果此時從新啟動同樣的資源分配,就會出現(xiàn)問題),nagle是TCP對于socket創(chuàng)建的算法,socketTO是socket連接超時時間,aliveCheck表示心跳檢查,確定服務(wù)器的狀態(tài)。Servers是memcached服務(wù)端開的地址和ip列表字符串,weights是上面服務(wù)器的權(quán)重,必須數(shù)量一致,否則權(quán)重?zé)o效
<socketpool name="pool1" failover="true" initConn="10" minConn="5" maxConn="250" maintSleep="0"
nagle="false" socketTO="3000" aliveCheck="true">
<servers>10.0.68.210:12000,10.0.68.210:12222</servers>
<weights>5,5</weights>
</socketpool>
<socketpool name="pool2" failover="true" initConn="10" minConn="5" maxConn="250" maintSleep="0"
nagle="false" socketTO="3000" aliveCheck="true">
<servers>10.0.68.210:22000,10.0.68.210:22222</servers>
<weights>5,5</weights>
</socketpool>
//虛擬集群設(shè)置,這里將幾個client的cache設(shè)置為一個虛擬集群,當(dāng)對這些IMemcachedCache作集群操作的時候,就會自動地對集群中所有的Cache作插入,尋找以及刪除的操作,做一個虛擬交互備份
<cluster name="cluster1">
<memCachedClients>mclient1,mclient2</memCachedClients>
</cluster>
</memcached>
2. 測試代碼,這里就附帶一個單元測試類的代碼就可以很清楚的知道使用方法。
后話
沒有不好的,只有不適合的,適合的場景使用,根據(jù)場景適合的使用,才是提高性能的最有效手段。后需要根據(jù)所需的應(yīng)用場景繼續(xù)對這部分集成內(nèi)容作完善,實踐完善設(shè)計。