欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis?使用?List?實現(xiàn)消息隊列的優(yōu)缺點

 更新時間:2022年01月11日 16:19:40   作者:InfoQ  
這篇文章主要介紹了Redis?使用?List?實現(xiàn)消息隊列有哪些利弊,小編結(jié)合消息隊列的特點一步步帶大家分析使用?Redis?的?List?作為消息隊列的實現(xiàn)原理,并分享如何把?SpringBoot?與?Redission?整合運用到項目中,需要的朋友可以參考下

分布式系統(tǒng)中必備的一個中間件就是消息隊列,通過消息隊列我們能對服務(wù)間進行異步解耦、流量消峰、實現(xiàn)最終一致性。

目前市面上已經(jīng)有 RabbitMQ、RochetMQ、ActiveMQ、Kafka 等,有人會問:“Redis 適合做消息隊列么?”

在回答這個問題之前,我們先從本質(zhì)思考:

  • 消息隊列提供了什么特性?
  • Redis 如何實現(xiàn)消息隊列?是否滿足存取需求?

今天,碼哥結(jié)合消息隊列的特點一步步帶大家分析使用 Redis 的 List 作為消息隊列的實現(xiàn)原理,并分享如何把 SpringBoot 與 Redission 整合運用到項目中。

什么是消息隊列

消息隊列是一種異步的服務(wù)間通信方式,適用于分布式和微服務(wù)架構(gòu)。消息在被處理和刪除之前一直存儲在隊列上。

每條消息僅可被一位用戶處理一次。消息隊列可被用于分離重量級處理、緩沖或批處理工作以及緩解高峰期工作負載。

  • Producer:消息生產(chǎn)者,負責產(chǎn)生和發(fā)送消息到 Broker;
  • Broker:消息處理中心。負責消息存儲、確認、重試等,一般其中會包含多個 queue;
  • Consumer:消息消費者,負責從 Broker 中獲取消息,并進行相應(yīng)處理;
  • 消息隊列的使用場景有哪些呢?
  • 消息隊列在實際應(yīng)用中包括如下四個場景:
  • 應(yīng)用耦合:發(fā)送方、接收方系統(tǒng)之間不需要了解雙方,只需要認識消息。多應(yīng)用間通過消息隊列對同一消息進行處理,避免調(diào)用接口失敗導(dǎo)致整個過程失敗;
  • 異步處理:多應(yīng)用對消息隊列中同一消息進行處理,應(yīng)用間并發(fā)處理消息,相比串行處理,減少處理時間;
  • 限流削峰:廣泛應(yīng)用于秒殺或搶購活動中,避免流量過大導(dǎo)致應(yīng)用系統(tǒng)掛掉的情況;
  • 消息驅(qū)動的系統(tǒng):系統(tǒng)分為消息隊列、消息生產(chǎn)者、消息消費者,生產(chǎn)者負責產(chǎn)生消息,消費者(可能有多個)負責對消息進行處理;

消息隊列滿足哪些特性

消息有序性

消息是異步處理的,但是消費者需要按照生產(chǎn)者發(fā)送消息的順序來消費,避免出現(xiàn)后發(fā)送的消息被先處理的情況。

重復(fù)消息處理

生產(chǎn)者可能因為網(wǎng)絡(luò)問題出現(xiàn)消息重傳導(dǎo)致消費者可能會收到多條重復(fù)消息。

同樣的消息重復(fù)多次的話可能會造成一業(yè)務(wù)邏輯多次執(zhí)行,需要確保如何避免重復(fù)消費問題。

可靠性

一次保證消息的傳遞。如果發(fā)送消息時接收者不可用,消息隊列會保留消息,直到成功地傳遞它。

當消費者重啟后,可以繼續(xù)讀取消息進行處理,防止消息遺漏。

List 實現(xiàn)消息隊列

Redis 的列表(List)是一種線性的有序結(jié)構(gòu),可以按照元素被推入列表中的順序來存儲元素,能滿足「先進先出」的需求,這些元素既可以是文字數(shù)據(jù),又可以是二進制數(shù)據(jù)。

LPUSH

生產(chǎn)者使用 LPUSH key element[element...] 將消息插入到隊列的頭部,如果 key 不存在則會創(chuàng)建一個空的隊列再插入消息。

如下,生產(chǎn)者向隊列 queue 先后插入了 「Java」「碼哥字節(jié)」「Go」,返回值表示消息插入隊列后的個數(shù)。

> LPUSH queue Java 碼哥字節(jié) Go
(integer) 3

RPOP

消費者使用 RPOP key 依次讀取隊列的消息,先進先出,所以 「Java」會先讀取消費:

> RPOP queue
"Java"
> RPOP queue
"碼哥字節(jié)"
> RPOP queue
"Go"

實時消費問題

65 哥:這么簡單就實現(xiàn)了么?

別高興的太早, LPUSH、RPOP 存在一個性能風險,生產(chǎn)者向隊列插入數(shù)據(jù)的時候,List 并不會主動通知消費者及時消費。

我們需要寫一個 while(true) 不停地調(diào)用 RPOP 指令,當有新消息就會返回消息,否則返回空。

程序需要不斷輪詢并判斷是否為空再執(zhí)行消費邏輯,這就會導(dǎo)致即使沒有新消息寫入到隊列,消費者也要不停地調(diào)用 RPOP 命令占用 CPU 資源。

65 哥:要如何避免循環(huán)調(diào)用導(dǎo)致的 CPU 性能損耗呢?

Redis 提供了 BLPOP、BRPOP 阻塞讀取的命令, 消費者在在讀取隊列沒有數(shù)據(jù)的時候自動阻塞,直到有新的消息寫入隊列,才會繼續(xù)讀取新消息執(zhí)行業(yè)務(wù)邏輯。

BRPOP queue 0

參數(shù) 0 表示阻塞等待時間無無限制

重復(fù)消費

  • 消息隊列為每一條消息生成一個「全局 ID」;
  • 生產(chǎn)者為每一條消息創(chuàng)建一條「全局 ID」,消費者把一件處理過的消息 ID 記錄下來判斷是否重復(fù)。

其實這就是冪等,對于同一條消息,消費者收到后處理一次的結(jié)果和多次的結(jié)果是一致的。

消息可靠性

65 哥:消費者從 List 中讀取一條在消息處理過程中宕機了就會導(dǎo)致消息沒有處理完成,可是數(shù)據(jù)已經(jīng)沒有保存在 List 中了咋辦?

本質(zhì)就是消費者在處理消息的時候崩潰了,就無法再還原消息,缺乏一個消息確認機制。

Redis 提供了 RPOPLPUSH、BRPOPLPUSH(阻塞) 兩個指令,含義是從 List 從讀取消息的同時把這條消息復(fù)制到另一個 List 中(備份),并且是原子操作。

我們就可以在業(yè)務(wù)流程正確處理完成后再刪除隊列消息實現(xiàn)消息確認機制。如果在處理消息的時候宕機了,重啟后再從備份 List 中讀取消息處理。

LPUSH redisMQ 公眾號 碼哥字節(jié)
BRPOPLPUSH redisMQ redisMQBack

生產(chǎn)者用 LPUSH 把消息插入到 redisMQ 隊列中,消費者使用 BRPOPLPUSH 讀取消息「公眾號」,同時該消息會被插入到 「redisMQBack」隊列中。

如果消費成功則把「redisMQBack」的消息刪除即可,異常的話可以繼續(xù)從 「redisMQBack」再次讀取消息處理。

需要注意的是

如果生產(chǎn)者消息發(fā)送的很快,而消費者處理速度慢就會導(dǎo)致消息堆積,給 Redis 的內(nèi)存帶來過大壓力。

Redission 實戰(zhàn)

在 Java 中,我們可以利用 Redission 封裝的 API 來快速實現(xiàn)隊列,接下來碼哥基于 SpringBoot 2.1.4 版本來交大家如何整合并實戰(zhàn)。

詳細 API 文檔大家可查閱: https://github.com/redisson/redisson/wiki/7.-Distributed-collections

添加依賴

<dependency>
  <groupId>org.redisson</groupId>
  <artifactId>redisson-spring-boot-starter</artifactId>
  <version>3.16.7</version>
</dependency>

添加 Redis 配置,碼哥的 Redis 沒有配置密碼,大家根據(jù)實際情況配置即可。

spring:
  application:
    name: redission
  redis:
    host: 127.0.0.1
    port: 6379
    ssl: false

Java 代碼實戰(zhàn)

RBlockingDeque 繼承 java.util.concurrent.BlockingDeque ,在使用過程中我們完全可以根據(jù)接口文檔來選擇合適的 API 去實現(xiàn)業(yè)務(wù)邏輯。

主要方法如下

碼哥采用了雙端隊列來舉例

@Slf4j
@Service
public class QueueService {

    @Autowired
    private RedissonClient redissonClient;

    private static final String REDIS_MQ = "redisMQ";

    /**
     * 發(fā)送消息到隊列頭部
     *
     * @param message
     */
    public void sendMessage(String message) {
        RBlockingDeque<String> blockingDeque = redissonClient.getBlockingDeque(REDIS_MQ);

        try {
            blockingDeque.putFirst(message);
            log.info("將消息: {} 插入到隊列。", message);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 從隊列尾部阻塞讀取消息,若沒有消息,線程就會阻塞等待新消息插入,防止 CPU 空轉(zhuǎn)
     */
    public void onMessage() {
        RBlockingDeque<String> blockingDeque = redissonClient.getBlockingDeque(REDIS_MQ);
        while (true) {
            try {
                String message = blockingDeque.takeLast();
                log.info("從隊列 {} 中讀取到消息:{}.", REDIS_MQ, message);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

單元測試

@RunWith(SpringRunner.class)
@SpringBootTest(classes = RedissionApplication.class)
public class RedissionApplicationTests {
    @Autowired
    private QueueService queueService;
    @Test
    public void testQueue() throws InterruptedException {
        new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                queueService.sendMessage("消息" + i);
            }
        }).start();
        new Thread(() -> queueService.onMessage()).start();
        Thread.currentThread().join();
    }
}

總結(jié)

可以使用 List 數(shù)據(jù)結(jié)構(gòu)來實現(xiàn)消息隊列,滿足先進先出。為了實現(xiàn)消息可靠性,Redis 提供了 BRPOPLPUSH 命令是解決。

Redis 是一個非常輕量級的鍵值數(shù)據(jù)庫,部署一個 Redis 實例就是啟動一個進程,部署 Redis 集群,也就是部署多個 Redis 實例。

而 Kafka、RabbitMQ 部署時,涉及額外的組件,例如 Kafka 的運行就需要再部署 ZooKeeper。相比 Redis 來說,Kafka 和 RabbitMQ 一般被認為是重量級的消息隊列。

需要注意的是,我們要避免生產(chǎn)者過快,消費者過慢導(dǎo)致的消息堆積占用 Redis 的內(nèi)存。

在消息量不大的情況下使用 Redis 作為消息隊列,他能給我們帶來高性能的消息讀寫,這似乎也是一個很好消息隊列解決方案。

到此這篇關(guān)于Redis 使用 List 實現(xiàn)消息隊列有哪些利弊的文章就介紹到這了,更多相關(guān)redis list消息隊列內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis實現(xiàn)持久化的方式匯總

    Redis實現(xiàn)持久化的方式匯總

    Redis是一種高級key-value數(shù)據(jù)庫。它跟memcached類似,不過數(shù)據(jù)可以持久化,而且支持的數(shù)據(jù)類型很豐富。今天我們就來看看如何實現(xiàn)Redis持久化,需要的朋友可以參考下
    2022-10-10
  • redis?for?windows?6.2.6安裝包最新步驟詳解

    redis?for?windows?6.2.6安裝包最新步驟詳解

    這篇文章主要介紹了redis?for?windows?6.2.6安裝包全網(wǎng)首發(fā),使用Windows計劃任務(wù)自動運行redis服務(wù),文章給大家講解的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-04-04
  • 控制Redis的hash的field中的過期時間

    控制Redis的hash的field中的過期時間

    這篇文章主要介紹了控制Redis的hash的field中的過期時間問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • 淺析Redis中紅鎖RedLock的實現(xiàn)原理

    淺析Redis中紅鎖RedLock的實現(xiàn)原理

    RedLock?是一種分布式鎖的實現(xiàn)算法,由?Redis?的作者?Salvatore?Sanfilippo(也稱為?Antirez)提出,本文主要為大家詳細介紹了紅鎖RedLock的實現(xiàn)原理,感興趣的可以了解下
    2024-02-02
  • Linux安裝單機版Redis的完整步驟

    Linux安裝單機版Redis的完整步驟

    這篇文章主要給大家介紹了關(guān)于Linux安裝單機版Redis的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-09-09
  • Redis在項目中的使用(JedisPool方式)

    Redis在項目中的使用(JedisPool方式)

    項目操作redis是使用的RedisTemplate方式,另外還可以完全使用JedisPool和Jedis來操作redis,本文給大家介紹Redis在項目中的使用,JedisPool方式,感興趣的朋友跟隨小編一起看看吧
    2021-12-12
  • redis的五大數(shù)據(jù)類型應(yīng)用場景分析

    redis的五大數(shù)據(jù)類型應(yīng)用場景分析

    這篇文章主要介紹了redis的五大數(shù)據(jù)類型實現(xiàn)原理,本文給大家分享五大數(shù)據(jù)類型的應(yīng)用場景分析,需要的朋友可以參考下
    2021-08-08
  • RedisTemplate訪問Redis的更好方法

    RedisTemplate訪問Redis的更好方法

    這篇文章主要為大家介紹了RedisTemplate訪問Redis的更好方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • Redis 的查詢很快的原因解析及Redis 如何保證查詢的高效

    Redis 的查詢很快的原因解析及Redis 如何保證查詢的高效

    由于redis是內(nèi)存數(shù)據(jù)庫,歸功于它的數(shù)據(jù)結(jié)構(gòu)所以查詢效率非常高,今天通過本文給大家介紹下Redis 的查詢很快的原因解析及Redis 如何保證查詢的高效,感興趣的朋友一起看看吧
    2022-03-03
  • 深入解析Redis的LRU與LFU算法實現(xiàn)

    深入解析Redis的LRU與LFU算法實現(xiàn)

    這篇文章主要重點介紹了Redis的LRU與LFU算法實現(xiàn),并分析總結(jié)了兩種算法的實現(xiàn)效果以及存在的問題,并闡述其優(yōu)劣特性,感興趣的小伙伴跟著小編一起來看看吧
    2023-07-07

最新評論