redis系列:通過隊列案例學習list命令

摘要: 前言 這一篇文章將講述Redis中的list類型命令,同樣也是通過demo來講述,其他部分這裏就不在贅述了。 項目Github地址:https://github.com/rainbowda/learnWay/tree/master/learnRedis/case-list 案例 demo功能是隊列,整個demo的大致頁面如下。

前言
這一篇文章將講述Redis中的list類型命令,同樣也是通過demo來講述,其他部分這裏就不在贅述了。

項目Github地址:https://github.com/rainbowda/learnWay/tree/master/learnRedis/case-list

案例
demo功能是隊列,整個demo的大致頁面如下。左邊是存儲到Redis中的數據,右邊是從Redis中彈出的數據。

redis系列:通過隊列案例學習list命令

準備工作
首先定義一個存儲list的key

private static final String LIST_KEY = "list:1";
隊列的key就用list:1

redis操作對象

private RedisTemplate redisTemplate;
//string 命令操作對象
private ValueOperations valueOperations;
//list 命令操作對象
private ListOperations listOperations;
list在Redis中的結構可以看下圖(圖片來源於Redis in Action)。

圖片來源於Redis in Action

插入數據
頭部插入
命令介紹
redis系列:通過隊列案例學習list命令
接下來看看demo中頭部插入的功能,點擊下圖中頭部插入按鈕,然後在彈出框中填入數字0,點擊提交後整個頭部插入流程結束。可以看到左邊的隊列數據出現了一條{"data":"0"} 數據,在數據{"data":"1"} 上面。

redis系列:通過隊列案例學習list命令

來看看後臺的方法

@RequestMapping(value = "/leftPop",method = RequestMethod.GET)
public Object leftPop(){
return listOperations.leftPop(LIST_KEY);
}
如果需要在Redis中操作,可以敲下面的命令

lpush list:1 "{\"data\":\"0\"}"
尾部插入
命令介紹
redis系列:通過隊列案例學習list命令
接下來看看demo中尾部插入的功能,點擊下圖中尾部插入按鈕,然後在彈出框中填入數字11,點擊提交後整個新增流程結束。可以看到左邊的隊列數據出現了一條{"data":"11"} 數據,在數據{"data":"10"}下面。

redis系列:通過隊列案例學習list命令

來看看後臺的方法

@RequestMapping(value = "/rightPop",method = RequestMethod.GET)
public Object rightPop(){
return listOperations.rightPop(LIST_KEY);
}
如果需要在Redis中操作,可以敲下面的命令

rpush list:1 "{\"data\":\"11\"}"
列表查詢
命令介紹
同樣先看看相關的獲取值命令

redis系列:通過隊列案例學習list命令
後臺查詢方法,將新增的內容查詢出來

@RequestMapping(value = "/getList",method = RequestMethod.GET)
public List getList(){
List list = listOperations.range(LIST_KEY, 0, -1);

//可以用size獲取成員長度
//listOperations.size(LIST_KEY);

return list;

}
數據彈出
頭部彈出
redis系列:通過隊列案例學習list命令
接下來看看頭部彈出的功能,點擊下圖中頭部彈出按鈕,可以看到左邊的隊列頂部數據減少了,在右邊彈出的數據出現了左邊隊列數據消失的數據。

redis系列:通過隊列案例學習list命令

來看看後臺的方法

@RequestMapping(value = "/leftPop",method = RequestMethod.GET)
public Object leftPop(){
return listOperations.leftPop(LIST_KEY);
}
如果需要在Redis中操作,可以敲下面的命令

lpop list:1
尾部彈出
命令 用例 描述
RPOP RPOP key 移除並返回存於 key 的 list 的最後一個元素。
BRPOP BRPOP key [key ...] timeout 它是 RPOP 的阻塞版本,因爲這個命令會在給定list無法彈出任何元素的時候阻塞連接。
接下來看看尾部彈出的功能,點擊下圖中尾部彈出按鈕,可以看到左邊的隊列尾部數據減少了,在右邊彈出的數據出現了左邊隊列數據消失的數據。

redis系列:通過隊列案例學習list命令

來看看後臺的方法

@RequestMapping(value = "/rightPop",method = RequestMethod.GET)
public Object rightPop(){
return listOperations.rightPop(LIST_KEY);
}
如果需要在Redis中操作,可以敲下面的命令

rpop list:1
其他命令
redis系列:通過隊列案例學習list命令
RPOPLPUSH RPOPLPUSH source destination 原子性地返回並移除存儲在 source 的列表的最後一個元素(列表尾部元素), 並把該元素放入存儲在 destination 的列表的第一個元素位置(列表頭部)。
BRPOPLPUSH BRPOPLPUSH source destination timeout BRPOPLPUSH 是 RPOPLPUSH 的阻塞版本。
RPOPLPUSH和BRPOPLPUSH
這兩個命令作用其實是相同的,只不過BRPOPLPUSH是阻塞的,當沒有數據時,會一直阻塞,直到有數據。

在Redis官方文檔中,用RPOPLPUSH命令舉了兩個例子,一個是Reliable queue(安全的隊列 ),另一個是Circular list(循環列表)。

Reliable queue(安全的隊列 )
Redis通常都被用做一個處理各種後臺工作或消息任務的消息服務器。 一個簡單的隊列模式就是:生產者把消息放入一個列表中,等待消息的消費者用 RPOP 命令(用輪詢方式), 或者用 BRPOP 命令(如果客戶端使用阻塞操作會更好)來得到這個消息。

然而,因爲消息有可能會丟失,所以這種隊列並是不安全的。例如,當接收到消息後,出現了網絡問題或者消費者端崩潰了, 那麼這個消息就丟失了。

RPOPLPUSH (或者其阻塞版本的 BRPOPLPUSH) 提供了一種方法來避免這個問題:消費者端取到消息的同時把該消息放入一個正在處理中的列表。 當消息被處理了之後,該命令會使用 LREM 命令來移除正在處理中列表中的對應消息。

另外,可以添加一個客戶端來監控這個正在處理中列表,如果有某些消息已經在這個列表中存在很長時間了(即超過一定的處理時限), 那麼這個客戶端會把這些超時消息重新加入到隊列中。

翻譯來自 http://www.redis.cn/commands/rpoplpush.html

Circular list(循環列表)
RPOPLPUSH 命令的 source 和 destination 是相同的話, 那麼客戶端在訪問一個擁有n個元素的列表時,可以在 O(N) 時間裏一個接一個獲取列表元素, 而不用像 LRANGE 那樣需要把整個列表從服務器端傳送到客戶端。

上面這種模式即使在以下兩種情況下照樣能很好地工作: 有多個客戶端同時對同一個列表進行旋轉(rotating):它們會取得不同的元素,直到列表裏所有元素都被訪問過,又從頭開始這個操作。 有其他客戶端在往列表末端加入新的元素。

這個模式讓我們可以很容易地實現這樣一個系統:有 N 個客戶端,需要連續不斷地對一批元素進行處理,而且處理的過程必須儘可能地快。 一個典型的例子就是服務器上的監控程序:它們需要在儘可能短的時間內,並行地檢查一批網站,確保它們的可訪問性。

值得注意的是,使用這個模式的客戶端是易於擴展(scalable)且安全的(reliable),因爲即使客戶端把接收到的消息丟失了, 這個消息依然存在於隊列中,等下次迭代到它的時候,由其他客戶端進行處理。

翻譯來自 http://www.redis.cn/commands/rpoplpush.html

案例-約瑟夫問題
約瑟夫問題(有時也稱爲約瑟夫斯置換),是一個出現在計算機科學和數學中的問題。在計算機編程的算法中,類似問題又稱爲約瑟夫環。

人們站在一個等待被處決的圈子裏。 計數從圓圈中的指定點開始,並沿指定方向圍繞圓圈進行。 在跳過指定數量的人之後,執行下一個人。 對剩下的人重複該過程,從下一個人開始,朝同一方向跳過相同數量的人,直到只剩下一個人,並被釋放。

問題即,給定人數、起點、方向和要跳過的數字,選擇初始圓圈中的位置以避免被處決。

來自維基百科 https://zh.wikipedia.org/wiki/%E7%BA%A6%E7%91%9F%E5%A4%AB%E6%96%AF%E9%97%AE%E9%A2%98

redis系列:通過隊列案例學習list命令

思路
定義一個list key爲josephus,利用

RPOPLPUSH josephus josephus
命令來構造循環鏈表,每當數到3時,使用rpop

rpop josephus
命令彈出

代碼實現
public class JosephusProblem extends RedisBaseConnection {

@Test
public void test() {
    //構造數據
    for (int i = 1; i <= 41; i++) {
        listOperations.leftPush("josephus", String.valueOf(i));
    }

    int index = 1;
    while (listOperations.size("josephus") > 0) {
        //當數到3時,彈出
        if (index == 3) {
            System.out.println(listOperations.range("josephus", 0, -1));
            System.out.println("當前被殺的人是:" + listOperations.rightPop("josephus"));
            index = 0;
        } else {
            listOperations.rightPopAndLeftPush("josephus", "josephus");
        }
        index++;
    }
}

}
整個代碼步驟如下

先是模擬有41個人(向redis中key爲josephus的list添加41個數據)
定義索引index
循環判斷key爲josephus的數據長度是否大於0
當索引index爲3時,調用Redis的rpop命令彈出對應的數據。索引index不爲3時,調用RPOPLPUSH命令,將對應的數據放到隊列頭部
索引index加1
運行結果有點長,這裏只截圖最後一部分的結果,如下

redis系列:通過隊列案例學習list命令

約瑟夫問題代碼請點擊JosephusProblem.java

建議學習的人最好每個命令都去敲下,加深印象。下面詩句送給你們。

紙上得來終覺淺,絕知此事要躬行。————出自《冬夜讀書示子聿》

原文鏈接

本文爲雲棲社區原創內容,未經允許不得轉載。

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