服務(wù)集成平臺5.6的性能測試進(jìn)入尾聲,這期的優(yōu)化也算告一段落。這次主要的優(yōu)化工作還是在三個方面:應(yīng)用服務(wù)器(Apache,JBoss)配置,業(yè)務(wù)流程,Cache Client包(http://code.google.com/p/memcache-client-forjava/ )。這里把過去和這次優(yōu)化對于Cache的使用作一個經(jīng)驗分享,希望大家能夠用好Cache,提速你的應(yīng)用。
這里還是通過一些點滴的啟示來介紹優(yōu)化的一些心得,很多時候還是要根據(jù)具體情況來判斷如何去具體實施,因此這里所說的僅僅是在一些場景下適用,并非放之四海皆準(zhǔn)的教條。同時也希望看此文的各位同學(xué),如果有更好的思路可以給我反饋,技術(shù)在交流中才會有發(fā)展。
積少成多,集腋成裘
性能提不上去,多半是在一些容易成為瓶頸的“暗點”(IO,帶寬,連接數(shù),資源競爭等等)。Memcached Cache現(xiàn)在已經(jīng)被大家廣泛使用,但是千萬不要認(rèn)為對Cache的操作是低損耗的,要知道這類集中式Cache對Socket連接數(shù)(會牽涉到linux操作系統(tǒng)文件句柄可用數(shù)),帶寬,網(wǎng)絡(luò)IO都是有要求的,有要求就意味著會有損失,因此積少成多,集腋成裘。服務(wù)集成平臺是一個高速的服務(wù)路由器,其大部分的業(yè)務(wù)數(shù)據(jù),訪問控制策略,安全策略以及對應(yīng)的一些控制閥值被緩存在Cache服務(wù)端,因此對于Cache的依賴性很強。每一次對于客戶端的性能提升,總會給服務(wù)集成平臺性能帶來不小的影響,但是每一次優(yōu)化速度后,客戶端可以優(yōu)化的空間越來越小,這時候需要一些策略來配合,提升應(yīng)用整體性能。當(dāng)前主要采用了以下幾點策略:
1. 從數(shù)據(jù)獲取角度來做優(yōu)化,采用本地數(shù)據(jù)緩存。(因為大家的應(yīng)用需要能夠線形擴(kuò)展,支持集群,所以才不使用應(yīng)用服務(wù)器本地緩存,但是在某些緩存數(shù)據(jù)時間性不敏感或者修改幾率較小的情況下,可以采用本地緩存結(jié)合集中式緩存,減少對遠(yuǎn)端服務(wù)器訪問次數(shù),提升應(yīng)用性能)。
Cache Client的IMemcachedCache 接口中的public Object get(String key,int localTTL)方法就是本地數(shù)據(jù)緩存結(jié)合遠(yuǎn)程Cache獲取數(shù)據(jù)的接口。具體流程參看下圖:
2. 從數(shù)據(jù)更新角度,采用異步數(shù)據(jù)更新。(即不等待數(shù)據(jù)更新結(jié)果,直接進(jìn)行其他業(yè)務(wù)流程)。這類操作使用場景比較局限,首先數(shù)據(jù)不會用作判斷(特別是高并發(fā)系統(tǒng)中的閥值),其次不需要返回結(jié)果作為后續(xù)流程處理輸入(例如計數(shù)器),時時性要求比較低。(這類操作其實是采用了集群數(shù)據(jù)傳播的一種策略,原先對于集群中所有節(jié)點都想即時傳播到,但是這樣對于性能損失很大,因此采用key對應(yīng)的主Node采用即時設(shè)置數(shù)據(jù),其他的通過后臺任務(wù)數(shù)據(jù)傳播來實現(xiàn),由于key對應(yīng)的主Node是數(shù)據(jù)第一操作和讀取節(jié)點,因此這類數(shù)據(jù)傳播操作時時性要求較低,適合這樣處理)。具體接口參見Cache Client 使用文檔。
3. 一次獲取,多次使用。這點和系統(tǒng)設(shè)計有關(guān),當(dāng)前服務(wù)集成平臺的安全流程是鏈狀的,一次請求會經(jīng)歷很多安全攔截器,而在每一個安全攔截器中會根據(jù)情況獲取具體的業(yè)務(wù)數(shù)據(jù)或者流程控制策略等緩存數(shù)據(jù),每一個安全攔截器都是彼此獨立的,在很早以前是每一個安全攔截器各自在需要數(shù)據(jù)的時候去遠(yuǎn)程獲取,但是壓力測試下來發(fā)現(xiàn)請求次數(shù)相當(dāng)多,而且好些重復(fù)獲取,因此將這些業(yè)務(wù)數(shù)據(jù)作為上下文在鏈?zhǔn)綑z查中傳遞,按需獲取和設(shè)置,最大程度上復(fù)用了數(shù)據(jù)。(其實也是一種減少數(shù)據(jù)獲取的方式)。
4. 規(guī)劃好你的Cache區(qū)。有些同學(xué)在使用Cache的時候問我是否有什么需要注意的,我覺得在使用Cache之前,針對需要緩存的數(shù)據(jù)需要做好規(guī)劃。那些數(shù)據(jù)需要放在一個Cache虛擬節(jié)點上,那些數(shù)據(jù)必須分開放。一方面是根據(jù)自己業(yè)務(wù)系統(tǒng)的數(shù)據(jù)耦合程度(未來系統(tǒng)是否需要合并或者拆分),另一方面根據(jù)數(shù)據(jù)量及讀寫頻繁度來合理分配(畢竟網(wǎng)絡(luò)IO還是稀缺資源)。當(dāng)然有時候業(yè)務(wù)系統(tǒng)設(shè)計者自己也不知道未來的發(fā)展,那么最簡單的方式給Key加上前綴,當(dāng)前可以合并,未來也可以拆分。同時數(shù)據(jù)粒度也需要考慮,粒度設(shè)計太小,那么交互頻繁度就會很高,如果粒度太大,那么網(wǎng)絡(luò)流量就會很大,同時將來業(yè)務(wù)模塊拆分就會有問題。
巧用Memcached Cache特有接口
Memcached Cache提供了計數(shù)器一整套接口和add,replace兩個接口。這些特有接口可以很好的滿足一些應(yīng)用的高并發(fā)性處理需求。例如對于資源訪問次數(shù)控制,采用Cache的計數(shù)器接口就可以實現(xiàn)在集群中的數(shù)量控制,原本通過Cache的get和put是無法解決并發(fā)問題的(就算是本地緩存一樣),這就是一組原子操作的接口。而Add和Replace可以滿足無需通過get方法獲取內(nèi)容,就可以對于key是否存在的不同情況作出相應(yīng)處理,也是一種原子性操作。這些原子操作接口對于高并發(fā)系統(tǒng)在集群中的設(shè)計會很有幫助。
Cache Client Cluster
Memcached Cache是集中式Cache,它僅僅是支持將數(shù)據(jù)能夠分片分區(qū)的存儲到一臺或者多臺的Cache Server實例中,但是這些數(shù)據(jù)并沒有作冗余,因此任何一個服務(wù)實例不可用,都會導(dǎo)致部分緩存數(shù)據(jù)丟失。當(dāng)然很多人采取持久化等方式來保證數(shù)據(jù)的完整性,但是這種方式對于效率以及恢復(fù)的復(fù)雜性都會有影響。
簡單的來想,為什么不把數(shù)據(jù)在多保存一份或者多份呢,當(dāng)其中一份不可用的情況下,就用另外一份補上。這就是最原始的Cache Client Cluster的構(gòu)想。在這里具體的設(shè)計細(xì)節(jié)就不多說了,主要說一下幾個要點,也讓使用Cache Client Cluster的同學(xué)有大致的一個了解。
先來看看Cache Cluster的結(jié)構(gòu)圖:

這張圖上需要注意四個角色:Application(使用Cache的應(yīng)用),Cache Cluster(Cache配置的虛擬集群),Cache Node(Cache的虛擬節(jié)點,在同一個Cluster中的Cache Node數(shù)據(jù)保持完全一致),Cache Instance(Cache虛擬節(jié)點中實際包含的Memcached Cache服務(wù)端實例)。
應(yīng)用僅僅操作Cache Node,不了解具體數(shù)據(jù)存儲或數(shù)據(jù)獲取是操作哪一個Cache 服務(wù)端實例。(這點也就是Memcached Cache可擴(kuò)展性的基礎(chǔ)設(shè)計)。Cache Cluster又將多個Cache Node組成了虛擬的集群,通過數(shù)據(jù)冗余,保證了服務(wù)可用性和數(shù)據(jù)完整性。
當(dāng)前 Cache Client Cluster主要有兩種配置模式:active 和 standby。(這里是借鑒了硬件的名詞,其實并不完全一樣,因為還是考慮到了效率問題)
Cache Client Cluster主要的功能點:
1. 容錯。當(dāng)被分配到讀取或者操作數(shù)據(jù)的Cache虛擬節(jié)點不可用的情況下,集群其他節(jié)點支持代替錯誤節(jié)點服務(wù)于客戶端應(yīng)用。
2. 數(shù)據(jù)冗余。當(dāng)操作集群中某一個Cache虛擬節(jié)點時,數(shù)據(jù)會異步傳播到其他集群節(jié)點。
3. 軟負(fù)載。客戶端通過對操作的key作算法(當(dāng)前采用簡單的key hash再取余的方式)選擇集群中的節(jié)點,達(dá)到集群中節(jié)點簡單的負(fù)載分擔(dān)。同時也由于這種模式,可以使得key都有默認(rèn)的第一操作節(jié)點,此節(jié)點的操作保持時時更新,而其他節(jié)點可以通過客戶端異步更新來實現(xiàn)效率提升。
4. 數(shù)據(jù)恢復(fù)。當(dāng)集群中某一節(jié)點失效后恢復(fù)時,其數(shù)據(jù)可能已經(jīng)完全丟失,此時通過配置成為Active模式可以將其他節(jié)點上冗余的數(shù)據(jù)Lazy復(fù)制到該節(jié)點(獲取一個復(fù)制一個,同時只支持一個冗余節(jié)點的數(shù)據(jù)獲取(不采取遍歷,防止低效))。
Active模式擁有1,2,3,4的特性。Standby模式擁用1,2,3特性。(其實本來只考慮讓Standby擁有1特性)。未來不排除還會有更多需要的特性加入。Active在key不存在的情況下會有些低效,因為會判斷一個冗余節(jié)點是否存在內(nèi)容,然后決定是否修復(fù)當(dāng)前節(jié)點。(考慮采用短期失敗標(biāo)示之類的,不過效率不一定高,同時增加了復(fù)雜度)
運行期動態(tài)擴(kuò)容部署
Memcached cache客戶端算法中比較出名的是Consistent Hashing算法,其目的也就是為了在節(jié)點增加或者減少以后,通過算法盡量減小數(shù)據(jù)重新分布的代價。采用虛擬節(jié)點,環(huán)狀和二叉樹等方式可以部分降低節(jié)點增加和減少對于數(shù)據(jù)分布的影響,但是始終還是有部分?jǐn)?shù)據(jù)會失效,這點還是由于Memcached Cache是集中式Cache所決定的。
但如果有了Cache Cluster的話,數(shù)據(jù)有了冗余,就可以通過逐步修改集群中虛擬節(jié)點配置,達(dá)到對于單個虛擬節(jié)點的配置動態(tài)擴(kuò)容。
支持動態(tài)部署前提:
配置文件動態(tài)加載。(配置文件可以在Classpath中,也可以是Http資源的方式)通過Cache Client 中Cache Manager可以停止Cache 服務(wù),重新加載配置文件,即時生效。
當(dāng)前動態(tài)部署的兩種方式:
1. 修改集群配置中某一套虛擬節(jié)點的服務(wù)實例配置(socketPool配置),增加或者減少后端數(shù)據(jù)存儲實例。然后動態(tài)加載新的配置文件(可以通過指定遠(yuǎn)端的http配置作為新的配置文件),通過集群的lazy的修復(fù)方式,逐漸的將數(shù)據(jù)從冗余節(jié)點復(fù)制到新的節(jié)點上來,最終實現(xiàn)數(shù)據(jù)遷移。
2. 修改集群配置中某一套虛擬節(jié)點的服務(wù)實例配置(socketPool配置),增加或者減少后端數(shù)據(jù)存儲實例。然后動態(tài)加載新的配置文件(可以通過指定遠(yuǎn)端的http配置作為新的配置文件),在調(diào)用Cache Manager主動將數(shù)據(jù)由某一虛擬節(jié)點復(fù)制到指定的集群中,實現(xiàn)數(shù)據(jù)批量遷移,然后根據(jù)需要看是否需要修改其他幾套虛擬節(jié)點配置。
存在的問題:
1. 當(dāng)前沒有做到不停止服務(wù)來動態(tài)部署。(后續(xù)考慮實現(xiàn),當(dāng)前將編譯配置和重新啟動服務(wù)器的工作節(jié)省了)
2. 不論是lazy復(fù)制還是批量數(shù)據(jù)遷移,都是會將原本有失效時間的數(shù)據(jù)變成了無失效時間的數(shù)據(jù)。(這個問題暫時還沒有一種可行的高效的方式解決)
后話
性能優(yōu)化這點事還是那句老話,需要了再去做也不遲。同時如果你開發(fā)的是一個每天服務(wù)訪問量都是上億,甚至更高的系統(tǒng),那么有時候斤斤計較會收獲不少。(當(dāng)然是不影響系統(tǒng)本身業(yè)務(wù)流程的基礎(chǔ))。
Cache客戶端自從作為開源放在Google上也收到了不少朋友的支持和反饋,同時自己業(yè)務(wù)系統(tǒng)以及其他部門同學(xué)的使用促使我不斷的去優(yōu)化和滿足必要的一些功能擴(kuò)展(但是對于Cache來說,還是那句話,簡單就是美,高效是使用Cache的最原始的需求)。
當(dāng)前Cache Client版本已經(jīng)到了2.5版本,在Google上有詳細(xì)的Demo(單元測試,壓力測試,集群測試)和說明使用文檔。是否速度會慢于其他Memcached客戶端,這不好說的很絕對,反正大家自己拉下去比較一下看看就知道了,當(dāng)然為了集群和其他的一些必要的附加功能還是做了一些性能犧牲。
項目地址在:http://code.google.com/p/memcache-client-forjava/
在首頁的右側(cè)有demo,doc,binary,src的鏈接,直接可以下載使用和察看。希望對需要的同學(xué)有幫助。