變更歷史
2010-06-22 添加客戶端分佈和SASL驗證兩節,更新spring配置一節。 2010-06-23 添加maven依賴說明 2010-10-17 1.2.6 released 2011-01-04 1.3 released。添加failure模式和standby節點。
XMemcached簡介
XMemcached是一個新java memcached client。也許你還不知道memcached是什麼?可以先看看這裏。簡單來說,Memcached 是一個高性能的分佈式內存對象的key-value緩存系統,用於動態Web應用以減輕數據庫負載,現在也有很多人將它作爲內存式數據庫在使用,memcached通過它的自定義協議與客戶端交互,而XMemcached就是它的一個java客戶端實現。
Memcached的java客戶端已經存在兩個了:官方提供的基於傳統阻塞io由Greg Whalin維護的客戶端、Dustin Sallings實現的基於java nio的Spymemcached。另外還有一些在此基礎上的改進版本。相比於這些客戶端,XMemcached有什麼優點呢?或者說,它的主要特性有哪些?
XMemcached的主要特性
高性能
XMemcached同樣是基於java nio的客戶端,java nio相比於傳統阻塞io模型來說,有效率高(特別在高併發下)和資源耗費相對較少的優點。傳統阻塞IO爲了提高效率,需要創建一定數量的連接形成連接池,而nio僅需要一個連接即可(當然,nio也是可以做池化處理),相對來說減少了線程創建和切換的開銷,這一點在高併發下特別明顯。因此XMemcached與Spymemcached在性能都非常優秀,在某些方面(存儲的數據比較小的情況下)Xmemcached比Spymemcached的表現更爲優秀,具體可以看這個Java Memcached Clients Benchmark。
支持完整的協議
Xmemcached支持所有的memcached協議,包括1.4.0正式開始使用的二進制協議。
支持客戶端分佈
Memcached的分佈只能通過客戶端來實現,XMemcached實現了此功能,並且提供了一致性哈希(consistent hash)算法的實現。
允許設置節點權重
XMemcached允許通過設置節點的權重來調節memcached的負載,設置的權重越高,該memcached節點存儲的數據將越多,所承受的負載越大。
動態增刪節點
XMemcached允許通過JMX或者代碼編程實現節點的動態添加或者移除,方便用戶擴展和替換節點等。
支持JMX
XMemcached通過JMX暴露的一些接口,支持client本身的監控和調整,允許動態設置調優參數、查看統計數據、動態增刪節點等。
與Spring框架和Hibernate-memcached的集成
鑑於很多項目已經使用Spring作爲IOC容器,因此XMemcached也提供了對Spring框架的集成支持。Hibernate-memcached是一個允許將memcached作爲hibernate的二級緩存的開源項目,默認是使用Spymemcached,Xmemcached提供了對這個項目的支持,允許替換Spymemcached.
客戶端連接池
剛纔已經提到java nio通常對一個memcached節點使用一個連接,而XMemcached同樣提供了設置連接池的功能,對同一個memcached可以創建N個連接組成連接池來提高客戶端在高併發環境下的表現,而這一切對使用者來說卻是透明的。啓用連接池的前提條件是保證數據之間的獨立性或者數據更新的同步,對同一個節點的各個連接之間是沒有做更新同步的,因此應用需要保證數據之間是相互獨立的或者全部採用CAS更新來保證原子性。
可擴展性
XMemcached是基於java nio框架yanf4j實現的,因此在實現上結構相對清楚,分層比較明晰,在xmemcached 1.2.5之後已經將yanf4j合併到xmemcached,因此不再需要依賴yanf4j,下面是XMemcached的主要類的UML圖:
使用指南
在簡單介紹完XMemcached的主要特性之後,我們將進入XMemcached的使用環節,這裏將按照從簡單到複雜的順序講解一些例子,以方便用戶深入瞭解XMemcached的使用。
依賴包
Xmemcached依賴slf4j
在測試下面講到的代碼之前,請先自行下載依賴包或者下載包含了依賴包的xmemcached。
如果你使用maven
如果你使用maven構建你的項目,那麼只要添加dependency即可使用xmemcached(僅對1.2.5及以後版本有效)
<dependency> <groupId>com.googlecode.xmemcached</groupId> <artifactId>xmemcached</artifactId> <version>{版本號}</version> </dependency>
簡單例子
對於用戶來說,最主要的功能是存取數據,假設我們有一個memcached節點IP地址或者域名是host,端口是11211,一個簡單的存取數據的例子如下:
MemcachedClientBuilder builder = new XMemcachedClientBuilder( AddrUtil.getAddresses("localhost:11211")); MemcachedClient memcachedClient = builder.build(); try { memcachedClient.set("hello", 0, "Hello,xmemcached"); String value = memcachedClient.get("hello"); System.out.println("hello=" + value); memcachedClient.delete("hello"); value = memcachedClient.get("hello"); System.out.println("hello=" + value); } catch (MemcachedException e) { System.err.println("MemcachedClient operation fail"); e.printStackTrace(); } catch (TimeoutException e) { System.err.println("MemcachedClient operation timeout"); e.printStackTrace(); } catch (InterruptedException e) { // ignore } try { //close memcached client memcachedClient.shutdown(); } catch (IOException e) { System.err.println("Shutdown MemcachedClient fail"); e.printStackTrace(); }
爲了節省篇幅,本文的所有代碼示例都沒有給出完整的package名,具體包名請查詢javadoc或者使用IDE工具幫助引入。
因爲XMemcachedClient的創建有比較多的可選項,因此提供了一個XMemcachedClientBuilder類用於構建MemcachedClient。MemcachedClient是主要接口,操作memcached的主要方法都在這個接口,XMemcachedClient是它的一個實現。傳入的memcached節點列表要求是類似"host1:port1 host2:port2 …"這樣的字符串,通過AddrUtil.getAddresses方法獲取實際的IP地址列表。
存儲數據是通過set方法,它有三個參數,第一個是存儲的key名稱,第二個是expire時間(單位秒),超過這個時間,memcached將這個數據替換出去,0表示永久存儲(默認是一個月),第三個參數就是實際存儲的數據,可以是任意的java可序列化類型。獲取存儲的數據是通過get方法,傳入key名稱即可。如果要刪除存儲的數據,這是通過delete方法,它也是接受key名稱作爲參數。XMemcached由於是基於nio,因此通訊過程本身是異步的,client發送一個請求給memcached,你是無法確定memcached什麼時候返回這個應答,客戶端此時只有等待,因此還有個等待超時的概念在這裏。客戶端在發送請求後,開始等待應答,如果超過一定時間就認爲操作失敗,這個等待時間默認是5秒(1.3.8開始改爲5秒,之前是1秒),上面例子展現的3個方法調用的都是默認的超時時間,這三個方法同樣有允許傳入超時時間的重載方法,例如
value=client.get(“hello”,3000);
就是等待3秒超時,如果3秒超時就跑出TimeutException,用戶需要自己處理這個異常。因爲等待是通過調用CountDownLatch.await(timeout)方法,因此用戶還需要處理中斷異常InterruptException。最後的MemcachedException表示Xmemcached內部發生的異常,如解碼編碼錯誤、網絡斷開等等異常情況。
touch更新數據超時時間
經常有這樣的需求,就是希望更新緩存數據的超時時間(expire time),在沒有touch協議之前,你需要整體的get-set一次:
value=client.get("a"); client.set("a",new-expire-time,value);
兩次操作,加上value的序列化/反序列化、網絡傳輸,這個開銷可不小。幸好,現在memcached已經支持touch協議,只需要傳遞key就更新緩存的超時時間:
client.touch(key,new-expire-time);
xmemcached 1.3.6開始支持二進制協議的touch命令,1.3.8開始支持文本協議的touch命令。
有時候你希望獲取緩存數據並更新超時時間,這時候可以用getAndTouch方法(僅二進制協議支持):
client.getAndTouch(key,new-expire-time);
客戶端分佈
Memcached的分佈是通過客戶端實現的,客戶端根據key的哈希值得到將要存儲的memcached節點,並將對應的value存儲到相應的節點。
XMemcached同樣支持客戶端的分佈策略,默認分佈的策略是按照key的哈希值模以連接數得到的餘數,對應的連接就是將要存儲的節點。如果使用默認的分佈策略,你不需要做任何配置或者編程。
XMemcached同樣支持一致性哈希(consistent hash),通過編程設置:
MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil .getAddresses(properties.getProperty("test.memcached.servers")) builder.setSessionLocator(new KetamaMemcachedSessionLocator()); MemcachedClient client=builder.build();
配置的方式請見spring配置一節。
XMemcached還提供了額外的一種哈希算法——選舉散列,在某些場景下可以替代一致性哈希
MemcachedClientBuilder builder = new XMemcachedClientBuilder( AddrUtil.getAddresses("server1:11211 server2:11211 server3:11211")); builder.setSessionLocator(new ElectionMemcachedSessionLocator()); MemcachedClient mc = builder.build();
CAS操作
Memcached是通過cas協議實現原子更新,所謂原子更新就是compare and set,原理類似樂觀鎖,每次請求存儲某個數據同時要附帶一個cas值,memcached比對這個cas值與當前存儲數據的cas值是否相等,如果相等就讓新的數據覆蓋老的數據,如果不相等就認爲更新失敗,這在併發環境下特別有用。XMemcached提供了對CAS協議的支持(無論是文本協議還是二進制協議),CAS協議其實是分爲兩個步驟:獲取CAS值和嘗試更新,因此一個典型的使用場景如下:
GetsResponse<Integer> result = client.gets("a"); long cas = result.getCas(); //嘗試將a的值更新爲2 if (!client.cas("a", 0, 2, cas)) { System.err.println("cas error"); }
首先通過gets方法獲取一個GetsResponse,此對象包裝了存儲的數據和cas值,然後通過cas方法嘗試原子更新,如果失敗打印”cas error”。顯然,這樣的方式很繁瑣,並且如果你想嘗試多少次原子更新就需要一個循環來包裝這一段代碼,因此XMemcached提供了一個*CASOperation*接口包裝了這部分操作,允許你嘗試N次去原子更新某個key存儲的數據,無需顯式地調用gets獲取cas值,上面的代碼簡化爲:
client.cas("a", 0, new CASOperation<Integer>() { public int getMaxTries() { return 1; } public Integer getNewValue(long currentCAS, Integer currentValue) { return 2; } });
CASOpertion接口只有兩個方法,一個是設置最大嘗試次數的getMaxTries方法,這裏是嘗試一次,如果嘗試超過這個次數沒有更新成功將拋出一個TimeoutException,如果你想無限嘗試(理論上),可以將返回值設定爲Integer.MAX_VALUE;另一個方法是根據當前獲得的GetsResponse來決定更新數據的getNewValue方法,如果更新成功,這個方法返回的值將存儲成功,這個方法的兩個參數是最新一次gets返回的GetsResponse結果。
更全面的例子
一些更全面的例子,展現了MemcachedClient接口的主要方法:
MemcachedClientBuilder builder = new XMemcachedClientBuilder( AddrUtil.getAddresses(“localhost:12000”)); MemcachedClient client = builder.build(); client.flushAll(); if (!client.set("hello", 0, "world")) { System.err.println("set error"); } if (client.add("hello", 0, "dennis")) { System.err.println("Add error,key is existed"); } if (!client.replace("hello", 0, "dennis")) { System.err.println("replace error"); } client.append("hello", " good"); client.prepend("hello", "hello "); String name = client.get("hello", new StringTranscoder()); System.out.println(name); client.deleteWithNoReply(“hello”);
首先存儲了hello對應的world字符串,然後調用add和replace方法去嘗試添加和替換,因爲數據已經存在,因此add會失敗,同樣replace在數據存在的情況纔會成功,也就是將hello對應的數據更新爲dennis,然後通過append和prepend方法在dennis前後加上了字符串hello和good,因此通過get返回的結果是hello dennis good。而刪除數據則是通過deleteWithNoReply方法,這個方法刪除數據並且告訴memcached不用返回應答,因此這個方法不會等待應答直接返回,特別適合於批量處理;同樣地,set、add、replace等方法也有相應的withNoReply重載版本,具體請看API文檔。
迭代所有key
Memcached本身並沒有提供迭代所有key的方法,但是通過"stats items"和"stats cachedump"統計協議可以做到迭代所有的key,這個迭代過程是低效,因此如無必要,並不推薦使用此方法。XMemcached僅提供了文本協議的迭代支持,其他協議暫未支持。
想迭代所有的key,你只需要獲取一個KeyIterator即可:
MemcachedClient client=... KeyIterator it=client.getKeyIterator(AddrUtil.getOneAddress("localhost:11211")); while(it.hasNext()) { String key=it.next(); }
Incr/Decr
下面這個例子展現了incr/decr操作的使用,兩個操作類似Java中的原子類如AtomicIntger,用於原子遞增或者遞減變量數值:
assert(1==this.memcachedClient.incr("a", 5, 1)); assert(6==this.memcachedClient.incr("a", 5)); assert(10==this.memcachedClient.incr("a", 4)); assert(9==this.memcachedClient.decr("a", 1)); assert(7==this.memcachedClient.deccr("a", 2));
incr和decr都有三個參數的方法,第一個參數指定遞增的key名稱,第二個參數指定遞增的幅度大小,第三個參數指定當key不存在的情況下的初始值。兩個參數的重載方法省略了第三個參數,默認指定爲0。
Xmemcached還提供了一個稱爲計數器的封裝,它封裝了incr/decr方法,使用它就可以類似AtomicLong那樣去操作計數:
Counter counter=client.getCounter("counter",0); counter.incrementAndGet(); counter.decrementAndGet(); counter.addAndGet(-10);
其中getCounter的第二個參數是計數器的初始值。
命名空間
從1.4.2開始,xmemcached提供了memcached命名空間的封裝使用,你可以將一組緩存項放到同一個命名空間下,可以讓整個命名空間下所有的緩存項同時失效,例子:
String ns = "namespace" ; this.memcachedClient.withNamespace(ns, new MemcachedClientCallable<Void>() { public Void call(MemcachedClient client) throws MemcachedException, InterruptedException, TimeoutException { //a,b,c都在namespace下 client.set("a",1); client.set("b",1); client.set("c",1); return null; } }); //獲取命名空間內的a對應的值 Integer aValue = this.memcachedClient.withNamespace(ns, new MemcachedClientCallable<Integer>() { public Integer call(MemcachedClient client) throws MemcachedException, InterruptedException, TimeoutException { return client.get("a"); } }); //使得命名空間失效 this.memcachedClient.invalidateNamespace(ns);
查看統計信息
Memcached提供了統計協議用於查看統計信息:
Map<InetSocketAddress,Map<String,String>> result=client.getStats();
getStats方法返回一個map,其中存儲了所有已經連接並且有效的memcached節點返回的統計信息,你也可以統計具體的項目,如統計items項目:
Map<InetSocketAddress,Map<String,String>> result=client.getStatsByItem("items");
只要向getStatsByItem傳入需要統計的項目名稱即可。
SASL驗證
Memcached 1.4.3開始支持SASL驗證客戶端,在服務器配置啓用SASL之後,客戶端需要通過授權驗證纔可以跟memcached繼續交互,否則將被拒絕請求。XMemcached 1.2.5開始支持這個特性。假設memcached設置了SASL驗證,典型地使用CRAM-MD5或者PLAIN的文本用戶名和密碼的驗證機制,假設用戶名爲cacheuser,密碼爲123456,那麼編程的方式如下:
MemcachedClientBuilder builder = new XMemcachedClientBuilder( AddrUtil.getAddresses("localhost:11211")); builder.addAuthInfo(AddrUtil.getOneAddress("localhost:11211"), AuthInfo .typical("cacheuser", "123456")); // Must use binary protocol builder.setCommandFactory(new BinaryCommandFactory()); MemcachedClient client=builder.build();
請注意,授權驗證僅支持二進制協議。
如果採用Spring配置,請參見spring配置一節。
高級主題
與Spring框架集成
通過XMemcachedClientFactoryBean類,即可與spring框架集成,簡單的配置如下:
<bean name="memcachedClient" class="net.rubyeye.xmemcached.utils.XMemcachedClientFactoryBean" destroy-method="shutdown"> <property name="servers"> <value>host1:port host2:port2</value> </property> </bean>
那麼你就可以在需要使用MemcachedClient的地方引用這個bean.
更完整的配置例子,設置備份節點、協議類型、一致性哈希、權重、連接池大小甚至SASL驗證信息(xmemcached 1.2.5支持),具體請看註釋:
<bean name="server1" class="java.net.InetSocketAddress"> <constructor-arg> <value>host1</value> </constructor-arg> <constructor-arg> <value>port1</value> </constructor-arg> </bean> <bean name="memcachedClient" class="net.rubyeye.xmemcached.utils.XMemcachedClientFactoryBean" destroy-method="shutdown"> <property name="servers"> <value>host1:port,host2:port host3:port,host4:port</value> </property> <!-- server's weights --> <property name="weights"> <list> <value>1</value> <value>2</value> <value>3</value> </list> </property> <!-- AuthInfo map,only valid on 1.2.5 or later version --> <property name="authInfoMap"> <map> <entry key-ref="server1"> <bean class="net.rubyeye.xmemcached.auth.AuthInfo" factory-method="typical"> <constructor-arg index="0"> <value>cacheuser</value> </constructor-arg> <constructor-arg index="1"> <value>123456</value> </constructor-arg> </bean> </entry> </map> </property> <!-- nio connection pool size --> <property name="connectionPoolSize" value="2"></property> <!-- Use binary protocol,default is TextCommandFactory --> <property name="commandFactory"> <bean class="net.rubyeye.xmemcached.command.BinaryCommandFactory"></bean> </property> <!-- Distributed strategy --> <property name="sessionLocator"> <bean class="net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator"></bean> </property> <!-- Serializing transcoder --> <property name="transcoder"> <bean class="net.rubyeye.xmemcached.transcoders.SerializingTranscoder" /> </property> <!-- ByteBuffer allocator --> <property name="bufferAllocator"> <bean class="net.rubyeye.xmemcached.buffer.SimpleBufferAllocator"></bean> </property> <!-- Failure mode --> <property name="failureMode" value="false"/> </bean>
配置選項參數表:
屬性名 | 值 |
servers | memcached節點列表,形如“主節點1:port,備份節點1:port 主節點2:port,備份節點2:port“的字符串,可以不設置備份節點,主備節點逗號隔開,不同分組空格隔開。 |
weights | 與servers對應的節點的權重 |
authInfoMap | 授權驗證信息,僅在xmemcached 1.2.5及以上版本有效 |
connectionPoolSize | nio連接池大小,默認爲1 |
commandFactory | 協議工廠,net.rubyeye.xmemcached.command.BinaryCommandFactory,TextCommandFactory(默認),KestrelCommandFactory |
sessionLocator | 分佈策略,一致性哈希net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator或者ArraySessionLocator(默認) |
transcoder | 序列化轉換器,默認使用net.rubyeye.xmemcached.transcoders.SerializingTranscoder,更多選項參見javadoc |
bufferAllocator | IoBuffer分配器,默認爲net.rubyeye.xmemcached.buffer.SimpleBufferAllocator,可選CachedBufferAllocator(不推薦) |
failureMode | 是否啓用failure模式,true爲啓用,默認不啓用 |
Spring 3.0和Builder配置
Spring 3.0修改了查找destroy method的方式,因此如果還是採用上面的配置來集成xmemcached話,會在啓動的時候拋出一個異常,信息類似“Couldn't find a destroy method named 'shutdown' on bean”,這種情況下xmemcached就無法正常工作,spring的IOC容器也無法正常啓動。有沒有解決辦法呢?答案是有的,暫時可以通過XmemcachedClientBuilder的工廠方法方式來創建MemcachedClient,也就是通過factory-bean加上factory-method指定的方式,一個示範配置如下:
<bean name="memcachedClientBuilder" class="net.rubyeye.xmemcached.XMemcachedClientBuilder"> <!-- XMemcachedClientBuilder have two arguments.First is server list,and second is weights array. --> <constructor-arg> <list> <bean class="java.net.InetSocketAddress"> <constructor-arg> <value>localhost</value> </constructor-arg> <constructor-arg> <value>12000</value> </constructor-arg> </bean> <bean class="java.net.InetSocketAddress"> <constructor-arg> <value>localhost</value> </constructor-arg> <constructor-arg> <value>12001</value> </constructor-arg> </bean> </list> </constructor-arg> <constructor-arg> <list> <value>1</value> <value>2</value> </list> </constructor-arg> <property name="authInfoMap"> <map> <entry key-ref="server1"> <bean class="net.rubyeye.xmemcached.auth.AuthInfo" factory-method="typical"> <constructor-arg index="0"> <value>cacheuser</value> </constructor-arg> <constructor-arg index="1"> <value>123456</value> </constructor-arg> </bean> </entry> </map> </property> <property name="connectionPoolSize" value="2"></property> <property name="commandFactory"> <bean class="net.rubyeye.xmemcached.command.TextCommandFactory"></bean> </property> <property name="sessionLocator"> <bean class="net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator"></bean> </property> <property name="transcoder"> <bean class="net.rubyeye.xmemcached.transcoders.SerializingTranscoder" /> </property> </bean> <!-- Use factory bean to build memcached client --> <bean name="memcachedClient3" factory-bean="memcachedClientBuilder" factory-method="build" destroy-method="shutdown"/>
設置節點權重
如果是通過spring配置,請看上一節,如果需要編程設置,通過下面代碼:
MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("localhost:12000 localhost:12001"),new int[]{1,3}); MemcachedClient memcachedClient=builder.build();
傳入一個int數組,裏面的元素就是節點對應的權重值,比如這裏設置"localhost:12000"節點的權重爲1,而"localhost:12001"的權重爲3。注意,xmemcached的權重是通過複製連接的多個引用來實現的,比如權重爲3,那麼就複製3個同一個連接的引用放在集合中讓MemcachedSessionLocator查找。
改變節點權重,可以通過setServerWeight方法:
public interface XMemcachedClientMBean{ .... /** * Set a memcached server's weight * * @param server * @param weight */ public void setServerWeight(String server, int weight); }
使用二進制協議
如果使用spring配置,請參見與spring集成一節。
Memcached 1.4開始正式啓用二進制協議,xmemcached 1.2開始支持二進制協議,啓用這一特性也非常簡單,設置相應的CommandFactory即可:
MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("localhost:12000 localhost:12001"),new int[]{1,3}); builder.setCommandFactory(new BinaryCommandFactory());//use binary protocol MemcachedClient memcachedClient=builder.build();
默認使用的TextCommandFactory,也就是文本協議。
JMX支持
可以通過JMX查看xmemcached的狀態,在jvm啓動參數中添加:
java -Dxmemcached.jmx.enable=true
即可通過JMX監控xmemcached狀態,xmemcached通過RMI暴露服務接口:
service:jmx:rmi:///jndi/rmi://[host]:7077/xmemcachedServer
你可以在jconsole中查看這些MBean。 提供的MBean包括:
MBean | 描述 |
net.rubyeye.xmemcached.monitor.StatisticsHandlerMBean | 用於查看Client統計信息 |
net.rubyeye.xmemcached.impl.OptimizerMBean | 用於調整性能參數 |
net.rubyeye.xmemcached.XMemcachedClientMBean | 動態添加或者刪除節點,查看有效服務器等信息 |
JMX的更多選項:
選項 | 描述 |
-Dxmemcached.rmi.port | RMI端口 |
-Dxmemcached.rmi.name | RMI服務名 |
動態添加/刪除節點
在JMX支持一節提到的JMX方式操作外,還可以通過編程方式:
MemcachedClient client=new XMemcachedClient(AddrUtil.getAddresses("server1:11211 server2:11211")); //Add two new memcached nodes client.addServer("server3:11211 server4:11211"); //Remove memcached servers client.removeServer("server1:11211 server2:11211");
Nio連接池
Xmemcached是基於java nio的client實現,默認對一個memcached節點只有一個連接,這在通常情況下已經有非常優異的表現。但是在典型的高併發環境下,nio的單連接也會遇到性能瓶頸。因此XMemcached支持設置nio的連接池,允許建立多個連接到同一個memcached節點,但是請注意,這些連接之間是不同步的,因此你的應用需要自己保證數據更新的同步,啓用連接池可以通過下面代碼:
MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("localhost:12000")); builder.setConnectionPoolSize(5);
如果採用Spring配置,請參見與Spring集成一節。
Failure模式和standby節點
從1.3版本開始,xmemcached支持failure模式。所謂failure模式是指,當一個memcached節點down掉的時候,發往這個節點的請求將直接失敗,而不是發送給下一個有效的memcached節點。具體可以看memcached的文檔。默認不啓用failure模式,啓用failure模式可以通過下列代碼:
MemcachedClientBuilder builder=…… builder.setFailureMode(true);
不僅如此,xmemcached還支持主輔模式,你可以設置一個memcached的節點的備份節點,當主節點down掉的情況下,會將本來應該發往主節點的請求轉發給standby備份節點。使用備份節點的前提是啓用failure模式。備份節點設置如下:
MemcachedClient builder=new XmemcachedClientBuilder(AddrUtil.getAddressMap("localhost:11211,localhost:11212 host2:11211,host2:11212"));
上面的例子,將localhost:11211的備份節點設置爲localhost:11212,而將host2:11211的備份節點設置爲host2:11212
形如“host:port,host:port"的字符串也可以使用在spring配置中,完全兼容1.3之前的格式。
與Kestrel交互
Kestrel是twitter開源的一個scala寫的簡單高效MQ,它支持 memcached文本協議,但是並不完全兼容,例如它不支持flag,導致很多利用flag做序列化的客戶端無法正常運作。因此Xmemcached特意提供了KestrelCommandFactory?用於支持Kestrel。使用KestrelCommandFactory?即可擁有如下好處:
默認關閉get優化,因爲kestrel不支持bulk get;
支持kestrel的阻塞獲取和可靠獲取;
允許向kestrel存儲任意java序列化類型。設置KestrelCommandFactory:
MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("localhost:12000 localhost:12001"),new int[]{1,3}); builder.setCommandFactory(new KestrelCommandFactory()); MemcachedClient memcachedClient=builder.build();
關於最後一點需要補充說明,由於kestrel不支持flag,因此xmemcached在存儲的數據之前加了4個字節的flag,如果你的全部應用都使用xmemcached,那麼沒有問題,如果使用其他clients,會有兼容性的問題,因此Xmemcached還允許關閉這個功能,通過
client.setPrimitiveAsString(true);
設置爲true後,原生類型都將存儲爲字符串,而序列化類型將無法存儲了。
與tokyotyrant交互
通過使用TokyoTyrantTranscoder就可以跟TokyoTyrant進行交互,但是由於TokyoTyrant對memcached文本協議的flag,exptime不支持,因此內部TokyoTyrantTranscoder加了4個字節的flag在value前面,如果你的全部應用都使用xmemcached,那麼沒有問題,如果使用其他clients,會有兼容性的問題,這一點與跟kestrel交互相同。
MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses("localhost:12000 localhost:12001"),new int[]{1,3}); builder.setTranscoder(new TokyoTyrantTranscoder()); MemcachedClient memcachedClient=builder.build();
與Hibernate-memcached集成
大多數配置與採用spymemcahed一樣,具體請看hibernate-memcached的wiki頁。如果使用 xmemcached,首先需要將memcacheClientFactory 設置爲XmemcachedClientFactory。
Property | Value |
hibernate.memcached.memcacheClientFactory | net.rubyeye.xmemcached.utils.hibernate.XmemcachedClientFactory |
其他一般選項有很大不同:
Property | Value |
hibernate.memcached.servers | localhost:11211 localhost:11212 |
ibernate.memcached.cacheTimeSeconds | 300 |
hibernate.memcached.keyStrategy | HashCodeKeyStrategy |
hibernate.memcached.readBufferSize | DEFAULT_SESSION_READ_BUFF_SIZE |
hibernate.memcached.operationTimeout | DEFAULT_OP_TIMEOUT |
hibernate.memcached.hashAlgorithm | NATIVE_HASH,KETAMA_HASH etc. |
hibernate.memcached.commandFactory | TextCommandFactory , BinaryCommandFactory |
hiberante.memcached.sessionLocator | ArrayMemcachedSessionLocator,KetamaMemcachedSessionLocator |
壓縮、sanitizeKeys等雜項
數據壓縮
memcached存儲大數據的效率是比較低的,當數據比較大的時候xmemcached會幫你壓縮在存儲,取出來的時候自動解壓並反序列化,這個大小閥值默認是16K,可以通過Transcoder接口的setCompressionThreshold(1.2.1引入)方法修改閥值,比如設置爲1K:
memcachedClient.getTranscoder()).setCompressionThreshold(1024);
這個方法是在1.2.1引入到Transcoder接口,在此之前,你需要通過強制轉換來設置:
((SerializingTranscoder)memcachedClient.getTranscoder()).setCompressionThreshold(1024);
packZeros
XMemcached的序列化轉換器在序列化數值類型的時候有個特殊處理,如果前面N個字節都是0,那麼將會去除這些0,縮減後的數據將更小,例如數字3序列化是0x0003,那麼前面3個0將去除掉成一個字節0x3。反序列化的時候將自動在前面根據數值類型補0。這一特性默認是開啓的,如果考慮到與其他client兼容的話需要關閉此特性可以通過:
memcachedClient.getTranscoder()).setPackZeros(false);
sanitizeKeys
在官方客戶端有提供一個sanitizeKeys選項,當選擇用URL當key的時候,MemcachedClient會自動將URL encode再存儲。默認是關閉的,想啓用可以通過:
memcachedClient.setSanitizeKeys(true);
這一特性將在1.2.1中引入。
來源:https://code.google.com/p/xmemcached/wiki/User_Guide_zh#與tokyotyrant交互