1. 程式人生 > 其它 >Spring Boot 整合Redisson基礎篇

Spring Boot 整合Redisson基礎篇

摘要:介紹Redisson中分散式物件和集合的基礎操作,包括物件桶、集合、列表和雜湊。

綜述

  測試環境為:Spring Boot版本 2.5.x 和 Redisson 單機。關於如何中Spring Boot專案整合Redisson,請戳《Spring Boot 整合Redisson配置篇》。

  RedissonClient是執行緒安全的,由於其內部是通過Netty通訊,所以除了同步執行方式,也支援非同步執行。

Redisson 工具類

  首先提供一個Redisson 工具類,方便下文用於演示。

import org.redisson.api.*;
import org.redisson.client.codec.StringCodec;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;

@Component
public class RedisUtils {

    private RedisUtils() {
    }

    /**
     * 預設快取時間
     */
    private static final Long DEFAULT_EXPIRED = 32000L;

    /**
     * 自動裝配redisson client物件
     */
    @Resource
    private RedissonClient redissonClient;

    /**
     * 用於操作key
     * @return RKeys 物件
     */
    public RKeys getKeys() {
        return redissonClient.getKeys();
    }
    /**
     * 移除快取
     *
     * @param key
     */
    public void delete(String key) {
        redissonClient.getBucket(key).delete();
    }

    /**
     * 獲取getBuckets 物件
     *
     * @return RBuckets 物件
     */
    public RBuckets getBuckets() {
        return redissonClient.getBuckets();
    }

    /**
     * 讀取快取中的字串,永久有效
     *
     * @param key 快取key
     * @return 字串
     */
    public String getStr(String key) {
        RBucket<String> bucket = redissonClient.getBucket(key);
        return bucket.get();
    }

    /**
     * 快取字串
     *
     * @param key
     * @param value
     */
    public void setStr(String key, String value) {
        RBucket<String> bucket = redissonClient.getBucket(key);
        bucket.set(value);
    }

    /**
     * 快取帶過期時間的字串
     *
     * @param key     快取key
     * @param value   快取值
     * @param expired 快取過期時間,long型別,必須傳值
     */
    public void setStr(String key, String value, long expired) {
        RBucket<String> bucket = redissonClient.getBucket(key, StringCodec.INSTANCE);
        bucket.set(value, expired <= 0L ? DEFAULT_EXPIRED : expired, TimeUnit.SECONDS);
    }

    /**
     * string 操作,如果不存在則寫入快取(string方式,不帶有redisson的格式資訊)
     *
     * @param key     快取key
     * @param value   快取值
     * @param expired 快取過期時間
     */
    public Boolean setIfAbsent(String key, String value, long expired) {
        RBucket<String> bucket = redissonClient.getBucket(key, StringCodec.INSTANCE);
        return bucket.trySet(value, expired <= 0L ? DEFAULT_EXPIRED : expired, TimeUnit.SECONDS);
    }

    /**
     * 如果不存在則寫入快取(string方式,不帶有redisson的格式資訊),永久儲存
     *
     * @param key   快取key
     * @param value 快取值
     */
    public Boolean setIfAbsent(String key, String value) {
        RBucket<String> bucket = redissonClient.getBucket(key, StringCodec.INSTANCE);
        return bucket.trySet(value);
    }

    /**
     * 判斷快取是否存在
     *
     * @param key
     * @return true 存在
     */
    public Boolean isExists(String key) {
        return redissonClient.getBucket(key).isExists();
    }

    /**
     * 獲取RList物件
     *
     * @param key RList的key
     * @return RList物件
     */
    public <T> RList<T> getList(String key) {
        return redissonClient.getList(key);
    }

    /**
     * 獲取RMapCache物件
     *
     * @param key
     * @return RMapCache物件
     */
    public <K, V> RMapCache<K, V> getMap(String key) {
        return redissonClient.getMapCache(key);
    }

    /**
     * 獲取RSET物件
     *
     * @param key
     * @return RSET物件
     */
    public <T> RSet<T> getSet(String key) {
        return redissonClient.getSet(key);
    }

    /**
     * 獲取RScoredSortedSet物件
     *
     * @param key
     * @param <T>
     * @return RScoredSortedSet物件
     */
    public <T> RScoredSortedSet<T> getScoredSortedSet(String key) {
        return redissonClient.getScoredSortedSet(key);
    }

}

常用RKeys的API操作

  每個Redisson物件例項都會有一個與之對應的Redis資料例項,可以通過呼叫getName方法來取得Redis資料例項的名稱(key)。所有與Redis key相關的操作都歸納在RKeys這個接口裡:

RKeys keys = client.getKeys();
//獲取所有key值
Iterable<String> allKeys = keys.getKeys();
//模糊查詢所有包含關鍵字key的值
Iterable<String> foundedKeys = keys.getKeysByPattern("key");
//刪除多個key值
long numOfDeletedKeys = keys.delete("obj1", "obj2", "obj3");
//模糊刪除key值
long deletedKeysAmount = keys.deleteByPattern("test?");
//隨機獲取key
String randomKey = keys.randomKey();
//查詢當前有多少個key
long keysAmount = keys.count();

  具體demo如下:

   private void getKeys() {
        RKeys keys = redisUtils.getRedisKeys();
        Iterable<String> allKeys = keys.getKeys();
        StringBuilder sb = new StringBuilder();
        for (String key : allKeys) {
            sb = sb.append(key).append(",");
        }
        log.info("所有的key:{}", sb.substring(0, sb.length() - 1));
        // 模糊查詢以 map 打頭的所有 key
        allKeys = keys.getKeysByPattern("map*");
        sb = new StringBuilder();
        for (String key : allKeys) {
            sb = sb.append(key).append(",");
        }
        log.info("模糊匹配到的key:{}", sb.substring(0, sb.length() - 1));
    }

  其中,getKeysByPattern是基於redis 的 scan 命令實現的,匹配規則示例如下:

  • h?llo subscribes to hello, hallo and hxllo
  • h*llo subscribes to hllo and heeeello
  • h[ae]llo subscribes to hello and hallo, but not hillo

通用物件桶(Object Bucket)

  Redisson的分散式RBucket Java物件是一種通用物件桶,可以用來存放任意型別的物件。除了同步介面外,還提供了非同步(Async)、反射式(Reactive)和RxJava2標準的介面。還可以通過RBuckets介面實現批量操作多個RBucket物件:

   /**
     * String 資料型別
     */
    private void strDemo() {
        redisUtils.setStr(DEMO_STR, "Hello, String.");
        log.info("String 測試資料:{}", redisUtils.getStr(DEMO_STR));
        redisUtils.setStr("myBucket", "myBucketIsXxx");
        RBuckets buckets = redisUtils.getBuckets();
        Map<String, String> foundBuckets = buckets.get("myBucket*");
        Map<String, Object> map = new HashMap<>();
        map.put("myBucket1", "value1");
        map.put("myBucket2", 30L);

        // 同時儲存全部通用物件桶。
        buckets.set(map);
        Map<String, String> loadedBuckets = buckets.get("myBucket1", "myBucket2", "myBucket3");
        log.info("跨桶String 測試資料:{}", loadedBuckets);
        map.put("myBucket3", 320L);
    }

雜湊(Hash)

  基於Redisson的分散式對映結構的RMap Java物件實現了java.util.concurrent.ConcurrentMap介面和java.util.Map介面。與HashMap不同的是,RMap保持了元素的插入順序。該物件的最大容量受Redis限制,最大元素數量是4 294 967 295個。

    /**
     * Hash型別
     */
    private void hashDemo() {
        RMap<Object, Object> map = redisUtils.getMap("mapDemo");
        map.put("demoId1", "123");
        map.put("demoId100", "13000");
        Object demoId1Obj = map.get("demoId1");
        log.info("Hash 測試資料:{}", demoId1Obj);
    }

集合(Set)

  基於Redisson的分散式Set結構的RSet Java物件實現了java.util.Set介面。通過元素的相互狀態比較保證了每個元素的唯一性。該物件的最大容量受Redis限制,最大元素數量是4 294 967 295個。

    /**
     * Set 測試
     */
    private void setDemo() {
        RSet<String> set = redisUtils.getSet("setKey");
        set.add("value777");
        log.info("Set 測試資料");
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            log.info(next);
        }
    }

列表(List)

  基於Redisson分散式列表(List)結構的RList Java物件在實現了java.util.List介面的同時,確保了元素插入時的順序。該物件的最大容量受Redis限制,最大元素數量是4 294 967 295個。

    /**
     * List資料型別
     */
    private void listDemo() {
        RList<String> list = redisUtils.getList("listDemo");
        list.add("listValue1");
        list.add("listValue2");

        log.info("List 測試資料:{}", list.get(1));
    }

綜合示例

  將上述各個demo放入一個API中,以便快速測試:


import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping(value = "/redisson", method = RequestMethod.POST)
public class StudyRedissonController {

    @Resource
    private RedisUtils redisUtils;

    private static String DEMO_STR = "demoStr";

    @PostMapping("/learnRedisson")
    public void learnRedisson() {
        //三種資料結構使用示例
        strDemo();
        hashDemo();
        listDemo();
        setDemo();
        getKeys();
    }

    private void getKeys() {
        RKeys keys = redisUtils.getKeys();
        Iterable<String> allKeys = keys.getKeys();
        StringBuilder sb = new StringBuilder();
        for (String key : allKeys) {
            sb = sb.append(key).append(",");
        }
        log.info("所有的key:{}", sb.substring(0, sb.length() - 1));
        // 模糊查詢以 map 打頭的所有 key
        allKeys = keys.getKeysByPattern("map*");
        sb = new StringBuilder();
        for (String key : allKeys) {
            sb = sb.append(key).append(",");
        }
        log.info("模糊匹配到的key:{}", sb.substring(0, sb.length() - 1));
    }
    /**
     * Hash型別
     */
    private void hashDemo() {
        RMap<Object, Object> map = redisUtils.getMap("mapDemo");
        map.put("demoId1", "123");
        map.put("demoId100", "13000");
        Object demoId1Obj = map.get("demoId1");
        log.info("Hash 測試資料:{}", demoId1Obj);
    }

    /**
     * String 資料型別
     */
    private void strDemo() {
        redisUtils.setStr(DEMO_STR, "Hello, String.");
        log.info("String 測試資料:{}", redisUtils.getStr(DEMO_STR));
        redisUtils.setStr("myBucket", "myBucketIsXxx");
        RBuckets buckets = redisUtils.getBuckets();
        Map<String, String> foundBuckets = buckets.get("myBucket*");
        Map<String, Object> map = new HashMap<>();
        map.put("myBucket1", "value1");
        map.put("myBucket2", 30L);

        // 同時儲存全部通用物件桶。
        buckets.set(map);
        Map<String, String> loadedBuckets = buckets.get("myBucket1", "myBucket2", "myBucket3");
        log.info("跨桶String 測試資料:{}", loadedBuckets);
        map.put("myBucket3", 320L);
    }

    /**
     * List資料型別
     */
    private void listDemo() {
        RList<String> list = redisUtils.getList("listDemo");
        list.add("listValue1");
        list.add("listValue2");

        log.info("List 測試資料:{}", list.get(1));
    }
    /**
     * Set 測試
     */
    private void setDemo() {
        RSet<String> set = redisUtils.getSet("setKey");
        set.add("value777");
        log.info("Set 測試資料");
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            log.info(next);
        }
    }

}

  啟動服務,呼叫如上API,則控制檯列印的執行結果如下:


操作redis的執行結果

結束語

  本文中,Wiener介紹了基於Redisson的redis基礎操作,包括物件桶、集合、列表和雜湊表。大家對於這件事都是怎麼看的呢?歡迎在文章下方留言討論,三人行必有我師焉!小編會仔仔細細地看每條留言。

Reference