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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章