Spring Boot 操做 Memcache

1    第4-1課:Spring Boot 操做 Memcache

《精通 Spring Boot 42 講》共分五大部分,第四部分主要講解 Spring Boot 和中間件的使用,共 10 課,中間件是互聯網公司支撐高併發業務的必備組件,經常使用的組件有緩存、消息中間件、NoSQL 數據庫、定時任務等。經常使用的緩存中間件有 Memcache 和 Redis ,緩存主要支撐業務架構中高速讀寫;經常使用的消息中間件有 ActiveMQ 、RabbitMQ ,使用消息中間件的意義是,儘快地完成主線交易,其餘非實時業務異步或者解耦完成;最主流的 NoSQL 有 MongoDB、 ElasticSearch,前者主要是解決分佈式存儲和檢索的問題,後者主要解決分佈式文檔檢索的解決方案;定時任務經常使用開源框架 Quartz。以上的這些內容咱們都會在第四部分進行學習。java

在常見的企業架構中,隨着公司業務高速發展,最早出現瓶頸的是數據庫,這個時候不少企業就會考慮使用緩存來緩解數據庫的壓力,這是緩存使用最多的場景之一;另外在高併發搶購、分佈式 Session 等場景下,也會使用緩存來提升系統的高可用性。經常使用的緩存中間件有 Memcache 和 Redis,今天咱們先來學習 Memcache 的使用。git

1.1    Memcache 介紹

Memcache 是一個自由和開放源代碼、高性能、分配的內存對象緩存系統。簡單來講,Memcache 是一個高性能的分佈式內存對象的 key-value 緩存系統,用於加速動態 Web 應用程序,減輕數據庫負載,如今也有不少人將它做爲內存式數據庫在使用。github

它能夠應對任意多個鏈接,使用非阻塞的網絡 IO,因爲它的工做機制是在內存中開闢一塊空間,而後創建一個 Hash 表,Memcached 自動管理這些 Hash 表。算法

Memcache 由國外社區網站 LiveJournal 開發團隊開發,設計理念就是小而強大,它簡單的設計促進了快速部署、易於開發並解決面對大規模的數據緩存的許多難題,而所開放的 API 使得 Memcache 能用於 Java、C/C++/C#、Perl、Python、PHP、Ruby 等大部分流行的程序語言。spring

Memcache 和 Memcached 的區別:Memcache 是這個項目的名稱,而 Memcached 是服務器端的主程序名稱。數據庫

1.1.1  Memcache 特色

協議簡單編程

Memcache 的服務端客戶端通訊使用簡單的文本協議,經過 Telnet 便可在 Memcached 上存取數據。緩存

基於 Libevent 的事件處理安全

Libevent 是一套跨平臺的事件處理接口的封裝,可以兼容包括這些操做系統:Windows/Linux/BSD/Solaris 等操做系統的的事件處理,包裝的接口包括:poll、select(Windows)、epoll(Linux)、kqueue(BSD)/dev/pool(Solaris)。ruby

Memcache 使用 Libevent 來進行網絡併發鏈接的處理,可以保持在很大併發狀況下,仍舊可以保持快速的響應能力。

內置內存存儲方式

Memcache 中保存的數據都存儲在 Memcache 內置的內存存儲空間中。因爲數據僅存在於內存中,所以重啓 Memcache、重啓操做系統會致使數據所有丟失。Memcache LRU(Least Recently Used)算法自動刪除不使用的緩存,不過這個功能是能夠配置的,Memcache 啓動時經過「-M」參數能夠禁止 LRU。不過,Memcache 自己是爲緩存而設計的,建議開啓 LRU。

不適應場景

  • 緩存對象不能大於 1 MB
  • key 的長度大於 250 字符
  • Memcache 未提供任何安全策略
  • 不支持持久化

1.1.2  Memcache 安裝

在 Centos 下安裝使用 yum 命令安裝 Memcache 很是簡單:

yuminstall -y memcached

啓動:

11211150>> /usr/bin/memcached -b  -p-m-u root/tmp/memcached.log  &

啓動參數能夠配置,經常使用的命令選項以下:

  • m 內存
  • c 最大連接數
  • p 端口
  • u 用戶
  • t 線程數

查看 memcached 是否在運行:

ps-ef | grep memcached

1.2    Memcache 客戶端

Memcached Client 目前有 3 種:

  • Memcached Client for Java(已經中止更新)
  • SpyMemcached(已經中止更新)
  • XMemcached(主流使用)

Memcached Client for Java 比 SpyMemcached 更穩定、更早、更普遍;SpyMemcached 比 Memcached Client for Java 更高效;XMemcached 比 SpyMemcache 併發效果更好。

曾經有一段時間 SpyMemcached 使用比較普遍,我簡單介紹一下。

Spymemcached 介紹

Spymemcached 是一個採用 Java 開發的異步、單線程的 Memcached 客戶端,使用 NIO 實現。Spymemcached 是 Memcached 的一個流行的 Java Client 庫,性能表現出色,普遍應用於 Java + Memcached 項目中。

Spymemcached 最先由 Dustin Sallings 開發,Dustin 後來和別人一塊兒創辦了 Couchbase(原NorthScale),職位爲首席架構師,2014 年加入 Google。

XMemcached 簡介

如今使用最普遍的 Memcache Java 客戶端是 XMemcached,它是一個新的 Java Memcache Client 。Memcached 經過它的自定義協議與客戶端交互,而 XMemcached 就是它的一個 Java 客戶端實現。相比其餘客戶端,XMemcached 有什麼優勢呢?

XMemcached 的主要特性

XMemcached 支持設置鏈接池、宕機報警、使用二進制文件、一致性哈希算法、進行數據壓縮等操做,總結以下:

  • 高性能,由 Nio 支持;
  • 協議完整,Xmemcached 支持全部的 Memcached 協議,包括 1.4.0 正式開始使用的二進制協議;
  • 支持客戶端分佈,提供了一致性哈希(Consistent Hash)算法的實現;
  • 容許設置節點權重,XMemcached 容許經過設置節點的權重來調節 Memcached 的負載,設置的權重越高,該 Memcached 節點存儲的數據將越多,所承受的負載越大;
  • 動態增刪節點,Memcached 容許經過 JMX 或者代碼編程實現節點的動態添加或者移除,方便用戶擴展和替換節點等;
  • XMemcached 經過 JMX 暴露的一些接口,支持 Client 自己的監控和調整,容許動態設置調優參數、查看統計數據、動態增刪節點等;
  • 支持客戶端鏈接池,對同一個 Memcached 能夠建立 N 個鏈接組成鏈接池來提升客戶端在高併發環境下的表現,而這一切對使用者來講倒是透明的;
  • 可擴展性,XMemcached 是基於 Java Nio 框架 Yanf4j 實現的,所以在實現上結構相對清楚,分層比較明晰。

1.3    快速上手

上面介紹了這麼多,最須要關注的是 XMemcached 是最佳的選擇,下面咱們先用一個示例,來感覺一下 Spring Boot 使用 Xmemcached 集成 Memcache。

1.3.1  添加配置

添加依賴包:

<dependency>
<groupId></groupId>   com.googlecode.xmemcached
<artifactId></artifactId>   xmemcached
<version></version>   2.4.5
</dependency>

添加配置文件:

# 單個 Memcached 配置
memcached.servers=192.168.0.161:11211
# 鏈接池
memcached.poolSize=10
#操做超時時間
memcached.opTimeout=6000

配置 Memcached 的地址和端口號、鏈接池和操做超時時間,使用集羣時能夠拼接多個地址:"host1:port1 host2:port2 …"

建立 XMemcachedProperties 類,讀配置信息:

@Component
@ConfigurationProperties"memcached"(prefix =)
publicclass XMemcachedProperties {
private   String servers;
privateint   poolSize;
privatelong   opTimeout;
//省略 getter/setter   
}

1.3.2  啓動加載

利用 @Configuration 註解,在啓動時對 Memcached 進行初始化。

@Configuration
publicclass MemcachedBuilder {
protectedstatic   Logger logger =  LoggerFactory.getLogger(MemcachedBuilder.class);
@Resource   
private   XMemcachedProperties xMemcachedProperties;
 
@Bean   
public  MemcachedClient getMemcachedClient()    {
null        MemcachedClient memcachedClient =;
try       {
new            MemcachedClientBuilder builder =XMemcachedClientBuilder(AddrUtil.getAddresses(xMemcachedProperties.getServers()));
            builder.setConnectionPoolSize(xMemcachedProperties.getPoolSize());
            builder.setOpTimeout(xMemcachedProperties.getOpTimeout());
            memcachedClient = builder.build();
catch        }(IOException e) {
"inint MemcachedClient failed "            logger.error(,e);
        }
return       memcachedClient;
    }
}

由於 XMemcachedClient 的建立有比較多的可選項,因此提供了一個 XMemcachedClientBuilder 類用於構建 MemcachedClient。MemcachedClient 是主要接口,操做 Memcached 的主要方法都在這個接口,XMemcachedClient 是它的一個實現。

在方法 getMemcachedClient() 添加 @Bean 註解,表明啓動時候將方法構建好的實例注入到 Spring 容器中,後面在須要使用的類中,直接注入 MemcachedClient 便可。

1.3.3  進行測試

咱們建立一個 MemcachedTests 類,來測試 Memcached 配置信息是否配置正確。

@RunWith(SpringRunner.class)
@SpringBootTest
publicclass MemcachedTests {
@Autowired   
private   MemcachedClient memcachedClient;
}

測試 Memcached 的 get 、set 方法。

Test@
public void testGetSet() throws Exception {
set"hello"0"Hello,xmemcached"    memcachedClient.(,,);
valueget"hello"    String= memcachedClient.();
out"hello="value    System..println(+);
"hello"    memcachedClient.delete();
}

存儲數據是經過 set 方法,它有三個參數,第一個是存儲的 key 名稱,第二個是 expire 時間(單位秒),超過這個時間,memcached 將這個數據替換出去,0 表示永久存儲(默認是一個月),第三個參數就是實際存儲的數據,能夠是任意的 Java 可序列化類型。

獲取存儲的數據是經過 get 方法,傳入 key 名稱便可;若是要刪除存儲的數據,能夠經過 delete 方法,它也是接受 key 名稱做爲參數。

執行 testMemcached() 單元測試以後,控制檯會輸出:

hello=Hello,xmemcached

證實 Memcached 配置、設置和獲取值成功。

1.4    XMemcached 語法介紹

XMemcached 有很是豐富的語法來支持,咱們對緩存使用的各類場景,接下來一一介紹。

1.4.1  經常使用操做

除過上面的 get、set、delete 等方法外,Memcache 還有不少經常使用的操做。

Test@
public void testMore() throws Exception {
ifset"hello"0"world"   (!memcachedClient.(,,)) {
"set error"        System.err.println();
    }
ifadd"hello"0"dennis"   (!memcachedClient.(,,)) {
"Add error,key is existed"        System.err.println();
    }
if"hello"0"dennis"   (!memcachedClient.replace(,,)) {
"replace error"        System.err.println();
    }
"hello"" good"    memcachedClient.append(,);
"hello""hello "    memcachedClient.prepend(,);
get"hello"new    String name = memcachedClient.(,StringTranscoder());
out    System..println(name);
"hello"    memcachedClient.deleteWithNoReply();
}
  • add 命令,用於將 value(數據值)存儲在指定的 key(鍵)中。若是 add 的 key 已經存在,則不會更新數據(過時的 key 會更新),以前的值將仍然保持相同,而且將得到響應 NOT_STORED。
  • replace 命令,用於替換已存在的 key(鍵)的 value(數據值)。若是 key 不存在,則替換失敗,而且將得到響應 NOT_STORED。
  • append 命令,用於向已存在 key(鍵)的 value(數據值)後面追加數據。
  • prepend 命令,用於向已存在 key(鍵)的 value(數據值)前面追加數據。
  • deleteWithNoReply 方法,這個方法刪除數據而且告訴 Memcached,不用返回應答,所以這個方法不會等待應答直接返回,比較適合於批量處理。

1.4.2  Incr 和 Decr

Incr 和 Decr 相似數據的增和減,兩個操做相似 Java 中的原子類如 AtomicIntger,用於原子遞增或者遞減變量數值,示例以下:

Test@
public void testIncrDecr() throws Exception {
memcachedClient.deleteIncr   ("");
memcachedClient.deleteDecr   ("");
System.out.printlnmemcachedClient.incrIncr   (("", 6, 12));
System.out.printlnmemcachedClient.incrIncr   (("", 3));
System.out.printlnmemcachedClient.incrIncr   (("", 2));
System.out.printlnmemcachedClient.decrDecr   (("", 1, 6));
System.out.printlnmemcachedClient.decrDecr   (("", 2));
}

爲了防止數據干擾,在測試開始前前調用 delete() 方法清除兩個 key 值。

輸出:

12
15
17
6
4

Incr 和 Decr 都有三個參數的方法,第一個參數指定遞增的 key 名稱,第二個參數指定遞增的幅度大小,第三個參數指定當 key 不存在的狀況下的初始值,兩個參數的重載方法省略了第三個參數,默認指定爲 0。

1.4.3  Counter

Xmemcached 還提供了一個稱爲計數器的封裝,它封裝了 incr/decr 方法,使用它就能夠相似 AtomicLong 那樣去操做計數,示例以下:

Test@
public void testCounter() throws Exception {
    MemcachedClient memcachedClient = memcachedUtil.getMemcachedClient();
"counter"10    Counter counter=memcachedClient.getCounter(,);
out"counter="get    System..println(+counter.());
long   c1 =counter.incrementAndGet();
out"counter="    System..println(+c1);
long   c2 =counter.decrementAndGet();
out"counter="    System..println(+c2);
long-10   c3 =counter.addAndGet();
out"counter="    System..println(+c3);
}
  • memcachedClient.getCounter("counter",10),第一個參數爲計數器的 key,第二參數當 key 不存在時的默認值;
  • counter.incrementAndGet(),執行一次給計數器加 1;
  • counter.decrementAndGet(),執行一次給計數器減 1。

查看 counter.addAndGet(-10) 源碼(以下),發現 addAndGet() 會根據傳入的值的正負來判斷,選擇直接給對應的 key 加多少或者減多少,底層也是使用了 incr() 和 decr() 方法。

public long addAndGet(long delta) throws MemcachedException, InterruptedException, TimeoutException {
return0Lthisthisthisthisthisthis   delta >=?.memcachedClient.incr(.key, delta,.initialValue) :.memcachedClient.decr(.key, -delta,.initialValue);
}

Counter 適合在高併發搶購場景下作併發控制。

1.4.4  CAS 操做

Memcached 是經過 CAS 協議實現原子更新,所謂原子更新就是 Compare and Set,原理相似樂觀鎖,每次請求存儲某個數據同時要附帶一個 CAS 值,Memcached 比對這個 CAS 值與當前存儲數據的 CAS 值是否相等,若是相等就讓新的數據覆蓋老的數據,若是不相等就認爲更新失敗,這在併發環境下特別有用。XMemcached 提供了對 CAS 協議的支持(不管是文本協議仍是二進制協議),CAS 協議實際上是分爲兩個步驟:獲取 CAS 值和嘗試更新,所以一個典型的使用場景以下:

"a"GetsResponse<Integer> result = client.gets();
longcas = result.getCas();
//嘗試將 a 的值更新爲 2
if"a"02(!client.cas(,,, cas)) {
"cas error"    System.err.println();
}

首先經過 gets 方法獲取一個 GetsResponse,此對象包裝了存儲的數據和 CAS 值,而後經過 CAS 方法嘗試原子更新,若是失敗打印「cas error」。顯然,這樣的方式很繁瑣,而且若是你想嘗試多少次原子更新就須要一個循環來包裝這一段代碼,所以 XMemcached 提供了一個 CASOperation 接口包裝了這部分操做,容許你嘗試 N 次去原子更新某個 key 存儲的數據,無需顯式地調用 gets 獲取 CAS 值,上面的代碼簡化爲:

"a"0newclient.cas(,,CASOperation<Integer>() {
public int getMaxTries()             {
return1           ;
        }
 
public Integer getNewValue(long currentCAS, Integer currentValue)        {
return2               ;
        }
});

CASOpertion 接口只有兩個方法,一個是設置最大嘗試次數的 getMaxTries 方法,這裏是嘗試一次,若是嘗試超過這個次數沒有更新成功將拋出一個 TimeoutException,若是你想無限嘗試(理論上),能夠將返回值設定爲 Integer.MAX_VALUE;另外一個方法是根據當前得到的 GetsResponse 來決定更新數據的 getNewValue 方法,若是更新成功,這個方法返回的值將存儲成功,其兩個參數是最新一次 gets 返回的 GetsResponse 結果。

1.4.5  設置超時時間

XMemcached 因爲是基於 nio,所以通信過程自己是異步的,client 發送一個請求給 Memcached,你是沒法肯定 Memcached 何時返回這個應答,客戶端此時只有等待,所以還有個等待超時的概念在這裏。客戶端在發送請求後,開始等待應答,若是超過必定時間就認爲操做失敗,這個等待時間默認是 5 秒,也能夠在獲取的時候配置超時時間。

valueget"hello"3000=client.(,);

就是等待 3 秒超時,若是 3 秒超時就跑出 TimeutException,用戶須要本身處理這個異常。由於等待是經過調用 CountDownLatch.await(timeout) 方法,因此用戶還須要處理中斷異常 InterruptException,最後的 MemcachedException 表示 Xmemcached 內部發生的異常,如解碼編碼錯誤、網絡斷開等異常狀況。

1.4.6  更新緩存過時時間

常常有這樣的需求,就是但願更新緩存數據的超時時間(expire time),如今 Memcached 已經支持 touch 協議,只須要傳遞 key 就更新緩存的超時時間:

newclient.touch(key,-expire-time);

有時候你但願獲取緩存數據並更新超時時間,這時候能夠用 getAndTouch 方法(僅二進制協議支持):

newclient.getAndTouch(key,-expire-time);

若是在使用過程當中報如下錯誤:

Causederrorerrorby: net.rubyeye.xmemcached.exception.UnknownCommandException: Response,message:Unknow command TOUCH,key=Touch
250    at net.rubyeye.xmemcached.command.Command.decodeError(Command.java:)

說明安裝的 Memcached 服務不支持 touch 命令,建議升級。

測試示例:

Test@
public void testTouch() throws Exception {
set"Touch"2"Touch Value"    memcachedClient.(,,);
1000    Thread.sleep();
"Touch"6    memcachedClient.touch(,);
2000    Thread.sleep();
valueget"Touch"3000    String=memcachedClient.(,);
out"Touch="value    System..println(+);
}

1.4.7  Memcached 集羣

Memcached 的分佈是經過客戶端實現的,客戶端根據 key 的哈希值獲得將要存儲的 Memcached 節點,並將對應的 value 存儲到相應的節點。

XMemcached 一樣支持客戶端的分佈策略,默認分佈的策略是按照 key 的哈希值模以鏈接數獲得的餘數,對應的鏈接就是將要存儲的節點。若是使用默認的分佈策略,不須要作任何配置或者編程。

XMemcached 一樣支持一致性哈希(Consistent Hash),經過編程設置:

new"server1:11211 server2:11211 server3:11211"MemcachedClientBuilder builder =XMemcachedClientBuilder(AddrUtil.getAddresses());
newbuilder.setSessionLocator(KetamaMemcachedSessionLocator());
MemcachedClient client=builder.build();

XMemcached 還提供了額外的一種哈希算法——選舉散列,在某些場景下能夠替代一致性哈希:

newMemcachedClientBuilder builder =XMemcachedClientBuilder(
"server1:11211 server2:11211 server3:11211"                                    AddrUtil.getAddresses());
newbuilder.setSessionLocator(ElectionMemcachedSessionLocator());
MemcachedClient mc = builder.build();

在集羣的狀態下能夠給每一個服務設置不一樣的權重:

new"localhost:12000 localhost:12001"newint13MemcachedClientBuilder builder =XMemcachedClientBuilder(AddrUtil.getAddresses(),[]{,});
MemcachedClient memcachedClient=builder.build();

SASL 驗證

Memcached 1.4.3 開始支持 SASL 驗證客戶端,在服務器配置啓用 SASL 以後,客戶端須要經過受權驗證才能夠跟 Memcached 繼續交互,不然將被拒絕請求,XMemcached 1.2.5 開始支持這個特性。假設 Memcached 設置了 SASL 驗證,典型地使用 CRAM-MD 5 或者 PLAIN 的文本用戶名和密碼的驗證機制,假設用戶名爲 cacheuser,密碼爲 123456,那麼編程的方式以下:

newMemcachedClientBuilder builder =XMemcachedClientBuilder(
"localhost:11211"                AddrUtil.getAddresses());
"localhost:11211"builder.addAuthInfo(AddrUtil.getOneAddress(), AuthInfo
"cacheuser""123456"                .typical(,));
// Must use binary protocol
newbuilder.setCommandFactory(BinaryCommandFactory());
MemcachedClient client=builder.build();

請注意,受權驗證僅支持二進制協議。

1.4.8  查看統計信息

Memcached 提供了統計協議用於查看統計信息:

MapMapStringString<InetSocketAddress,<,>> result=client.getStats();

getStats 方法返回一個 map ,其中存儲了全部已經鏈接而且有效的 Memcached 節點返回的統計信息,你也能夠統計具體的項目,如統計 items 項目:

MapMapStringString"items"<InetSocketAddress,<,>> result=client.getStatsByItem();

只要向 getStatsByItem 傳入須要統計的項目名稱便可,咱們能夠利用這個功能,來作 Memcached 狀態監控等。

1.5    總結

Memcached 是一款很是流行的緩存中間件,被普遍應用在各場景中,使用緩存能夠環境數據庫壓力,某些場景下使用緩存能夠大大提升複用的 Tps 。 XMemcached 是 Memcached 的一個高性能 Nio 客戶端,支持 Memcached 底層各類操做,而且在 Memcached 協議的基礎上進行了封裝和完善,提供了鏈接池、集羣、數據壓縮、分佈式算法等高級功能,不管是完善度和性能各方面來看,XMemcached 都是目前最爲推薦的一款 Memcached 客戶端。

點擊這裏下載源碼

相關文章
相關標籤/搜索