XMemcached 中文api

y_keven發表於2014-07-21


變更歷史

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互動

相關文章