Redis使用ZSET實現訊息佇列使用總結一

2023-03-15 15:07:32

轉載請註明出處:

1.zset為什麼可以做訊息佇列

  zset做訊息佇列的特性有:

  1. 有序性:zset中所有元素都被自動排序。這讓zset很適合用於有序的訊息佇列,因為可以根據一個或多個標準(比如訊息的到達時間或優先順序)按需檢索訊息。

  2. 元素唯一性:zset的每個元素都是獨一無二的,這對於實現某些訊息需求(比如冪等性)是非常有幫助的。

  3. 成員和分數之間的對映關係:有序集合中的每個成員都有一個分數,這樣就可以將相同的資料劃分到不同的 queue 中,以及為每個 queue 設定不同的延時。

  4. 高效的新增刪除操作:因為zset會自動維護元素之間的順序,所以在新增或刪除元素時無需進行手動排序,從而能提升操作速度。

  綜上所述,Redis的zset天然支援按照時間順序的訊息佇列,可以利用其成員唯一性的特性來保證訊息不被重複消費,在實現高吞吐率等方面也有很大的優勢。

2.zset實現訊息佇列的步驟

 Redis的zset有序集合是可以用來實現訊息佇列的,一般是按照時間戳作為score的值,將訊息內容作為value存入有序集合中。

 實現步驟:

  1. 使用者端將訊息推播到Redis的有序集合中。

  2. 有序集合中,每個成員都有一個分數(score)。在這裡,我們可以設成訊息的時間戳,也就是當時的時間。

  3. 當需要從訊息佇列中獲取訊息時,使用者端獲取有序集合前N個元素並進行操作。一般來說,N取一個適當的數值,比如10。

  需要注意的是,Redis的zset是有序集合,它的元素是有序的,並且不能有重複元素。因此,如果需要處理有重複訊息的情況,需要在訊息體中加入某些唯一性標識來保證不會重複。

3.使用jedis實現訊息佇列範例

 Java可以通過Redis的Java使用者端包Jedis來使用Redis,Jedis提供了豐富的API來操作Redis,下面是一段實現用Redis的zset型別實現的訊息佇列的程式碼。

import redis.clients.jedis.Jedis;
import java.util.Set;

public class RedisMessageQueue {
    private Jedis jedis; //Redis連線物件
    private String queueName; //佇列名字

    /**
     * 建構函式
     * @param host Redis主機地址
     * @param port Redis埠
     * @param password Redis密碼
     * @param queueName 佇列名字
     */
    public RedisMessageQueue(String host, int port, String password, String queueName){
        jedis = new Jedis(host, port);
        jedis.auth(password);
        this.queueName = queueName;
    }

    /**
     * 傳送訊息
     * @param message 訊息內容
     */
    public void sendMessage(String message){
        //獲取當前時間戳
        long timestamp = System.currentTimeMillis();
        //將訊息新增到有序集合中
        jedis.zadd(queueName, timestamp, message);
    }

    /**
     * 接收訊息
     * @param count 一次接收的訊息數量
     * @return 返回接收到的訊息
     */
    public String[] receiveMessage(int count){
        //設定最大輪詢時間
        long timeout = 5000;
        //獲取當前時間戳
        long start = System.currentTimeMillis();

        while (true) {
            //獲取可用的訊息數量
            long size = jedis.zcount(queueName, "-inf", "+inf");
            if (size == 0) {
                //如果無訊息,休眠50ms後繼續輪詢
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                //計算需要獲取的訊息數量count與當前可用的訊息數量size的最小值
                count = (int) Math.min(count, size);
                //獲取訊息
                Set<String> messages = jedis.zrange(queueName, 0, count - 1);
                String[] results = messages.toArray(new String[0]);
                //移除已處理的訊息
                jedis.zremrangeByRank(queueName, 0, count - 1);
                return results;
            }

            //檢查是否超時
            if (System.currentTimeMillis() - start > timeout) {
                return null; //超時返回空
            }
        }
    }

    /**
     * 銷燬佇列
     */
    public void destroy(){
        jedis.del(queueName);
        jedis.close();
    }
}

  使用範例:

public static void main(String[] args) {
    //建立訊息佇列
    RedisMessageQueue messageQueue = new RedisMessageQueue("localhost", 6379, "password", "my_queue");

    //生產者傳送訊息
    messageQueue.sendMessage("message1");
    messageQueue.sendMessage("message2");

    //消費者接收訊息
    String[] messages = messageQueue.receiveMessage(10);
    System.out.println(Arrays.toString(messages)); //輸出:[message1, message2]

    //銷燬佇列
    messageQueue.destroy();
}

  在實際應用中,可以結合執行緒池或者訊息監聽器等方式,將訊息接收過程放置於獨立的執行緒中,以提高訊息佇列的處理效率。

4.+inf與-inf

  +inf 是 Redis 中用於表示正無窮大的一種特殊值,也就是無限大。在使用 Redis 的 zset 集合時,+inf 通常用作 ZREVRANGEBYSCORE 命令的上限值,表示查詢 zset 集合中最大的分數值。+inf 後面的 -inf 表示 zset 中最小的分數值。這兩個值一起可以用來獲取 zset 集合中的所有元素或一個特定範圍內的元素。例如:

# 獲取 zset 集合中所有元素
ZREVRANGE queue +inf -inf WITHSCORES

# 獲取 zset 集合中第1到第10個元素(分數從大到小排列)
ZREVRANGE queue +inf -inf WITHSCORES LIMIT 0 9

# 獲取 zset 集合中分數在 1581095012 到當前時間之間的元素
ZREVRANGEBYSCORE queue +inf 1581095012 WITHSCORES

  在這些命令中,+inf 代表了一個最大的分數值,-inf 代表了一個最小的分數值,用於確定查詢的分數值範圍。

5.redis使用list與zset做訊息佇列有什麼區別

  Redis 使用 List 和 ZSET 都可以實現訊息佇列,但是二者有以下不同之處:

  1. 資料結構不同:List 是一個有序的字串列表,ZSET 則是一個有序集合,它們的底層實現機制不同。

  2. 儲存方式不同:List 只能儲存字串型別的資料,而 ZSET 則可以儲存帶有權重的元素,即除了元素值外,還可以為每個元素指定一個分數。

  3. 功能不同: List 操作在元素新增、刪除等方面比較方便,而 ZSET 在處理資料排序和範圍查詢等方面比 List 更加高效。

  4. 應用場景不同: 對於需要精細控制排序和分值的場景可以選用 ZSET,而對於只需要簡單的佇列操作,例如先進先出,可以直接採用 List。

  綜上所述,List 和 ZSET 都可以用於訊息佇列的實現,但如果需要更好的效能和更高階的排序功能,建議使用 ZSET。而如果只需要簡單的佇列操作,則 List 更加適合。