SpringBoot整合Memcached例項
什麼是Memcached?
Memcached 是一個高效能的分散式記憶體物件快取系統,用於動態Web應用以減輕資料庫負載。它通過在記憶體中快取資料和物件來減少讀取資料庫的次數,從而提高動態、資料庫驅動網站的速度,以減少必須讀取外部資料來源(如資料庫或API)的次數。Memcached基於一個儲存鍵/值對的hashmap。其守護程序(daemon)是用C寫的,但是客戶端可以用任何語言來編寫,並通過memcached協議與守護程序通訊。
Memcached的api提供了一個分佈在多臺機器上的非常大的雜湊表。當表滿時,後續插入將導致以最近最少使用的順序清除舊資料。使用Memcached的應用程式通常在退回到較慢的備份儲存(如資料庫)之前,會將請求和新增到RAM中。
因為 Spring Boot 沒有針對Memcached提供對應的組建包,因此需要我們自己來整合。官方推出的 Java 客戶端 Spymemcached 是一個比較好的選擇之一。
安裝
使用Docker安裝memcached:
docker run --name my-memcache -d memcached memcached -m 64
順帶安裝memadmin管理工具,該工具有php編寫,需要php環境,這裡直接使用docker部署。一勞永逸。
Dockerfile FROM eboraas/apache-php RUN apt-get update && apt-get -y install php5-memcache && apt-get clean && rm -rf /var/lib/apt/lists/* ADD https://github.com/junstor/memadmin/archive/v1.0.12.tar.gz /var/www RUN rm -fr /var/www/html && ln -s /var/www/memadmin-1.0.12 /var/www/html && cd /var/www/ && tar xvf v1.0.12.tar.gz 構建 docker build -t memadmin . 執行測試 docker run -it --rm -p 11080:80 memadmin http://ip:10080/,使用者名稱密碼預設admin,可以修改/var/www/html/config.php
如果想方便的修改容器裡面的檔案,可以把檔案copy出來,然後再copy進去
或者先整個目錄copy出來,然後重新啟動一個容器通過-v對映進去
像這樣
$ docker cp memadmin:/var/www/html/ /usr/local/memadmin
$ docker stop memadmin
$ docker run -d --name memadmin -v /usr/local/memadmin:/var/www/html/ -p 11080:80 memadmin
使memcache伺服器使用64兆位元組進行儲存。
Spymemcached 介紹
Spymemcached 最早由 Dustin Sallings 開發,Dustin 後來和別人一起創辦了 Couchbase (原NorthScale),職位為首席架構師。2014 加入 Google。
Spymemcached 是一個採用 Java 開發的非同步、單執行緒的 Memcached 客戶端, 使用 NIO 實現。Spymemcached 是 Memcached 的一個流行的 Java client 庫,效能表現出色,廣泛應用於 Java + Memcached 專案中。
為了方便理解,我簡單寫了一個Springboot整合Memcached+spymemcached的例子。
整合SpringBoot例項
快取操作管理類
@Slf4j
public class OpeartionMemcachedManager {
/**
* The Memcached ip.
*/
@Value("${memcached.ip}")
String memcachedIp;
/**
* The Memcached port.
*/
@Value("${memcached.port}")
Integer memcachedPort;
/**
* The constant DEFAULT_TIMEOUT.
*/
public final static int DEFAULT_TIMEOUT = 5;
/**
* The constant timeUnitSeconds.
*/
public final static TimeUnit timeUnitSeconds = TimeUnit.SECONDS;
private MemcachedClient memcachedClient;
/**
* 初始化
*/
public void init() {
try {
//只採用單機模式,如果需要配置叢集模式可用AddrUtil.getAddresses(servers),
//可參考:https://blog.csdn.net/gtuu0123/article/details/4849905
memcachedClient = new MemcachedClient(new InetSocketAddress(memcachedIp, memcachedPort));
log.info("++++++++++++++++++++ Memcached 連線成功,Address:{}:{} ++++++++++++++++++++++", memcachedIp, memcachedPort);
} catch (IOException e) {
log.info("++++++++++++++++++++ Memcached 連線異常,Address:{}:{} ++++++++++++++++++++++{}", memcachedIp, memcachedPort, e);
}
}
/**
* 設定鍵值
*
* @param key 鍵
* @param expire 有效時間
* @param value 值
* @return boolean boolean
*/
public Boolean set(String key, int expire, Object value) {
OperationFuture<Boolean> result = memcachedClient.set(key, expire, value);
return getResult(result);
}
/**
* 根據鍵獲取值
*
* @param key the key
* @return the object
*/
public Object get(String key) {
return memcachedClient.get(key);
}
/**
* 以非同步的方式獲取值
*
* @param key the key
* @return the object
*/
public Object ascynGet(String key) {
Future<Object> objectFuture = memcachedClient.asyncGet(key);
return getResult(objectFuture);
}
/**
* 將物件新增到快取
*
* @param key the key
* @param value the value
* @param expire the expire
* @return the boolean
*/
public Boolean add(String key, Object value, int expire) {
Future<Boolean> f = memcachedClient.add(key, expire, value);
return getResult(f);
}
/**
* 替換某個鍵值
*
* @param key the 鍵
* @param value the 值
* @param expire the 過期時間
* @return the boolean
*/
public Boolean replace(String key, Object value, int expire) {
Future<Boolean> f = memcachedClient.replace(key, expire, value);
return getResult(f);
}
/**
* 刪除某個特定鍵
*
* @param key the key
* @return the boolean
*/
public Boolean delete(String key) {
Future<Boolean> f = memcachedClient.delete(key);
return getResult(f);
}
/**
* 立即從所有伺服器清除所有快取,慎用。
*
* @return the boolean
*/
@Deprecated
public Boolean flush() {
Future<Boolean> f = memcachedClient.flush();
return getResult(f);
}
/**
* 從快取中獲取多個鍵值。
*
* @param keys the 鍵集合
* @return the multi
*/
public Map<String, Object> getMulti(Collection<String> keys) {
return memcachedClient.getBulk(keys);
}
/**
* 從快取中獲取多個鍵值
*
* @param keys the 鍵陣列
* @return the multi
*/
public Map<String, Object> getMulti(String[] keys) {
return memcachedClient.getBulk(keys);
}
/**
* 非同步地從快取中獲取一組物件並使用它們進行解碼
*
* @param keys the 鍵集合
* @return the map
*/
public Map<String, Object> asyncGetMulti(Collection<String> keys) {
Map<String, Object> map = null;
Future<Map<String, Object>> f = memcachedClient.asyncGetBulk(keys);
try {
map = getResult(f);
} catch (Exception e) {
f.cancel(false);
}
return map;
}
/**
* 增加給定的計數器,返回新值。
*
* @param key the key
* @param by the 增值
* @param defaultValue the 預設值(如計時器不存在),如該key沒值,則取預設值
* @param expire the 過期時間
* @return the long
*/
public long increment(String key, int by, long defaultValue, int expire) {
return memcachedClient.incr(key, by, defaultValue, expire);
}
/**
* 以給定的數量增加給定的鍵。
*
* @param key the key
* @param by the 增值
* @return the long
*/
public long increment(String key, int by) {
return memcachedClient.incr(key, by);
}
/**
* 減量.
*
* @param key the key
* @param by the 減量
* @param defaultValue the 預設值(如果計數器不存在)
* @param expire the 過期時間
* @return the long
*/
public long decrement(String key, int by, long defaultValue, int expire) {
return memcachedClient.decr(key, by, defaultValue, expire);
}
/**
* 減量
*
* @param key the key
* @param by the 要減的值
* @return the long
*/
public long decrement(String key, int by) {
return memcachedClient.decr(key, by);
}
/**
* 非同步增量,並返回當前值.
*
* @param key the key
* @param by the 要增加的值
* @return the long
*/
public Long asyncIncrement(String key, int by) {
Future<Long> f = memcachedClient.asyncIncr(key, by);
return getResult(f);
}
/**
* Async decrement long.
* 非同步減量,並返回當前值
*
* @param key the key
* @param by the 要減少的值
* @return the long
*/
public Long asyncDecrement(String key, int by) {
Future<Long> f = memcachedClient.asyncDecr(key, by);
return getResult(f);
}
/**
* Gets result.
* 獲取返回結果
*
* @param <T> the type parameter
* @param future the future
* @return the result
*/
public <T> T getResult(Future<T> future) {
try {
return future.get(DEFAULT_TIMEOUT,
timeUnitSeconds);
} catch (Exception e) {
log.warn("獲取返回結果失敗!{}", e);
}
return null;
}
/**
* 關閉連線
*/
public void disConnect() {
if (memcachedClient == null) {
return;
}
memcachedClient.shutdown();
}
}
測試類
@RunWith(SpringRunner.class)
@SpringBootTest
public class MemcachedApplicationTests {
@Resource
private OpeartionMemcachedManager memcachedManager;
@Test
public void testSetGet() {
Boolean result = memcachedManager.set("someKey", 10000, "666666");
if (result) {
System.out.println("*********** " + memcachedManager.get("someKey").toString());
return;
}
System.out.println("*********** 操作失敗! ***********");
}
@Test
public void testAsyncGet2() {
//獲取值,如果在5秒內沒有返回值,將取消
Object myObj = null;
Object result = memcachedManager.ascynGet("someKey");
System.out.println(result);
}
@Test
public void testReplace() {
Boolean flag = memcachedManager.replace("someKey", "dashuai", 10000);
if (flag) {
System.out.println("更新替換鍵值成功!");
System.out.println("最終結果為:" + memcachedManager.get("someKey").toString());
return;
}
System.out.println("更新鍵值失敗!");
}
@Test
public void testAdd() {
Boolean flag = memcachedManager.add("someKey", "dashuai", 10000);
if (flag) {
System.out.println("最終結果為:" + memcachedManager.get("someKey").toString());
return;
}
System.out.println("新增鍵值失敗!");
}
@Test
public void delete() {
Boolean f = memcachedManager.delete("someKey");
System.out.println("刪除" + (f ? "成功!" : "失敗!"));
}
@Test
public void incrementTest() {
long result = memcachedManager.increment("increment", 5, 20, 10000);
System.out.println(result);
}
@Test
public void decrementTest() {
long result = memcachedManager.decrement("increment", 5, 20, 10000);
System.out.println(result);
}
@Test
public void asyncIncrement() {
Long result = memcachedManager.asyncIncrement("increment", 5);
System.out.println(result);
}
@Test
public void asyncGetMultiTest() {
memcachedManager.set("aa", 100000, "大帥");
memcachedManager.set("bb", 100000, "大傻");
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
Map map = memcachedManager.asyncGetMulti(list);
System.out.println(JSONParseUtils.object2JsonString(map));
}
@Test
public void flushTest() {
memcachedManager.flush();
Object result = memcachedManager.get("aa");
System.out.println(result);
}
}
原始碼地址:https://github.com/liaozihong/SpringBoot-Learning/tree/master/SpringBoot-Memcacherd