詳細介紹消息隊列的概念、原理及使用場景(附案例)

詳細介紹消息隊列的概念、原理及使用場景(附案例)

眾所周知在對網(wǎng)站設(shè)計的時候,會遇到給用戶“群發(fā)短信”,“訂單系統(tǒng)有大量的日志”,“秒殺設(shè)計”等,服務器沒法處理這種瞬間迸發(fā)的壓力,這種情況要保證系統(tǒng)正常有效的使用,就需要“消息隊列”的幫助。本篇主要通過消息隊列的思路進行學習。

主要了解如下知識:

  1、隊列是個什么東西,他能干什么?  2、對列的應用場景有哪些?  3、如何使用隊列對業(yè)務進行解偶?  4、如何使用redis隊列來消除高壓力?  5、專業(yè)的對列系統(tǒng)rabbitmq如何使用?

歸納如下主要內(nèi)容

  @消息隊列的概念,原理和場景

  @解耦案例:隊列處理訂單系統(tǒng)和配送系統(tǒng)

  @流量削峰案例:Redis的List類型實現(xiàn)秒殺

  @RabbitMQ:更專業(yè)的消息系統(tǒng)實現(xiàn)方案

一、認識消息隊列

1.1?消息對列概念

從本質(zhì)上說消息對列就是一個隊列結(jié)構(gòu)的中間件,也就是說消息放入這個中間件之后就可以直接返回,并不需要系統(tǒng)立即處理,而另外會有一個程序讀取這些數(shù)據(jù),并按順序進行逐次處理。

也就是說當你遇到一個并發(fā)特別大并且耗時特別長同時還不需要立即返回處理結(jié)果,使用消息隊列可以解決這類問題。

1.2?核心結(jié)構(gòu)

詳細介紹消息隊列的概念、原理及使用場景(附案例)

由一個業(yè)務系統(tǒng)進行入隊,把消息逐次插入到消息隊列中,插入成功之后直接返回成功的結(jié)果,后續(xù)會有一個消息處理系統(tǒng),這個系統(tǒng)會把消息系統(tǒng)中的記錄逐次進行取出并進行處理,完成一個出隊的流程。

1.3?應用場景

數(shù)據(jù)冗余:比如訂單系統(tǒng),后續(xù)需要嚴格的進行數(shù)據(jù)轉(zhuǎn)換和記錄,消息隊列可以把這些數(shù)據(jù)持久化的存儲在隊列中,然后有訂單,后續(xù)處理程序進行獲取,后續(xù)處理完之后在把這條記錄進行刪除來保證每一條記錄都能夠處理完成。

系統(tǒng)解耦:使用消息系統(tǒng)之后,入隊系統(tǒng)和出隊系統(tǒng)是分開的,也就說只要一天崩潰了,不會影響另外一臺系統(tǒng)正常運轉(zhuǎn)。

流量削峰:例如秒殺和搶購,我們可以配合緩存來使用消息隊列,能夠有效的頂住瞬間訪問量,防止服務器承受不住導致崩潰。

異步通信:消息本身使用入隊之后可以直接返回。

擴展性:例如訂單隊列,不僅可以處理訂單,還可以給其他業(yè)務使用。

排序保證:有些場景需要按照產(chǎn)品的順序進行處理比如單進單出從而保證數(shù)據(jù)按照一定的順序處理,使用消息隊列是可以的。

以上都是消息隊列常見的使用場景,當然消息隊列只是一個中間件,可以配合其他產(chǎn)品進行使用。

1.4 常見隊列實現(xiàn)優(yōu)缺點

隊列介質(zhì)

1、數(shù)據(jù)庫,例如mysql(可靠性高,易實現(xiàn),速度慢)

2、緩存, 例如redis (速度快,單個消息報包過大時效率低)

3、消息系統(tǒng),例如rabbitMq (專業(yè)性強,可靠,學習成本高)

消息處理觸發(fā)機制

1、死循環(huán)方式讀取:易實現(xiàn),故障時無法及時恢復;(比較適合做秒殺,比較集中,運維集中維護)

2、定時任務:壓力均分,有處理上限;目前比較流行的處理觸發(fā)機制。(唯一的缺點是間隔和數(shù)據(jù)需要注意,不要等上一個任務沒有完成下一個任務又開始了)

3、守護進程:類似于php-fpm 和php-cg,需要shell基礎(chǔ)

二、解藕案例:隊列處理“訂單系統(tǒng)”和“配送系統(tǒng)

對于訂單流程,我們可以設(shè)計兩個系統(tǒng),一個是“訂單系統(tǒng)” 另外一個是 “配送系統(tǒng)”, 在網(wǎng)購的時候我們應該都見過,當我提交了一個訂單之后,我在后臺可以看到我的貨物正在配送中。這個時候就要參與進來一個“配送系統(tǒng)”。

如果我們在做架構(gòu)的時候把 “訂單系統(tǒng)” 和 “配送系統(tǒng)” 設(shè)計在一起的話就會出現(xiàn)一些問題,首先對于訂單系統(tǒng)來說,因為系統(tǒng)的壓力會比較大,但是 “配送系統(tǒng)” 沒必要為這些壓力做一些即時的反應。

第二個我們也不希望在訂單系統(tǒng)出現(xiàn)故障之后導致配送系統(tǒng)也出現(xiàn)故障,這個時候就會同時影響到兩個系統(tǒng)的正常運轉(zhuǎn)。所以我們希望把這兩個系統(tǒng)進行解耦。這兩系統(tǒng)分開之后我們可以通過一個中間的 “隊列表” 進行這兩個系統(tǒng)的溝通。

2.1 架構(gòu)設(shè)計

詳細介紹消息隊列的概念、原理及使用場景(附案例)

1、首先訂單系統(tǒng)會接收用戶的訂單,然后進行訂單的處理。

2、然后會把這些訂單信息寫到隊列表中,這個隊列表是溝通這兩個系統(tǒng)的關(guān)鍵。

3、由配送系統(tǒng)定時執(zhí)行的一個程序來讀取隊列表進行處理。

4、配送系統(tǒng)處理之后,會把已處理的記錄進行標記。

2.2 程序流程

詳細介紹消息隊列的概念、原理及使用場景(附案例)

三、流量削峰案例:Redis 的 list 類型實現(xiàn)秒殺

redis 基于內(nèi)存,它的速度會非常快,redis 對數(shù)據(jù)庫有一個非常好的補充作用因為它是可持久化的,redis會周期性的把數(shù)據(jù)寫到硬盤里,所以它不用擔心斷電的問題,從這方面說它比另一款緩存 memcache 更有優(yōu)勢些,另外 redis 提供五種數(shù)據(jù)類型字符串,雙向鏈表,哈希,集合,有序集合)

一般情況下,做秒殺案例,搶購,瞬間高比你高發(fā),需要排隊 的案例中 redis是一個很好的選擇。

3.1 redis數(shù)據(jù)類型中的 list 類型

  redis 的list 是一個雙向鏈表,可以從頭部或者尾部追加數(shù)據(jù)。

  * LPUSH/LPUSHX :將值插入到(/存在的)列表頭部

  * RPUSH/RPUSHX: 將值插入到(/存在的)列表尾部

  * LPOP : 移除并獲取列表的第一個元素

  * RPOP: 移除并獲取列表的最后一個元素

  * LTRIM: 保留指定區(qū)間內(nèi)的元素

  * LLEN: 獲取列表長度

  * LSET: 通過索引設(shè)置列表元素的值

  * LINDEX: 通過索引獲取列表中的元素

  * LRANGE: 獲取列表指定范圍內(nèi)的元素

3.2 架構(gòu)設(shè)計

一個簡單結(jié)構(gòu)秒殺的程序設(shè)計。

詳細介紹消息隊列的概念、原理及使用場景(附案例)

1、首先記錄是哪一個用戶參與了秒殺同時記錄他的時間。

2、將用戶的id存到redis列表中,讓它排隊。如果規(guī)定只有前10個用戶可以參與成功,如果列表中的個數(shù)已經(jīng)夠了就不會讓它繼續(xù)追加數(shù)據(jù)。這樣redis的列表長度就只會是10個

3、最后在慢慢的將redis中的數(shù)據(jù)寫入到數(shù)據(jù)庫中,以減少數(shù)據(jù)的壓力

3.3 代碼級設(shè)計

1、當用戶開始秒殺時,將秒殺程序的請求寫入Redis (uid, time_stamp)中。

2、假使規(guī)定只有10人可以秒殺成功,檢查 Redis 已經(jīng)存放數(shù)據(jù)的長度,超出上限直接丟棄說明秒殺完成。

3、最后在死循環(huán)處理存入Redis中的10條數(shù)據(jù),然后在慢慢的取數(shù)據(jù)并存入到mysql數(shù)據(jù)庫中。

在秒殺這一塊對于數(shù)據(jù)庫的壓力特別的大,如果我們沒有這樣的設(shè)計,會造成mysql的寫入瓶頸。我們通過Redis的一個對列l(wèi)ist,然后把秒殺的請求放入到Redis里面, 最后通過入庫程序,把數(shù)據(jù)慢慢的寫入到數(shù)據(jù)庫,這樣的話就可以實現(xiàn)流量的均衡,對mysql不會造成太大的壓力。 

四、RabbitMQ

這里講解一些RabbitMQ的使用,首先我們之前講秒殺案例的時候提到了鎖的機制,防止其他程序處理同一條記錄,如果我們的系統(tǒng)架構(gòu)非常的復雜,有多個程序?qū)崟r的讀取一個隊列,或者我有多個發(fā)送程序,同時來操作一個或多個隊列,甚至我還想這些程序分布在不同的機器上,這種情況下用redis隊列就有些力不從心了。這種時候怎么辦呢,我們就需要來引入一些更專業(yè)的消息隊列系統(tǒng),這些系統(tǒng)可以更好的來解決問題。

4.1 RabbitMQ的架構(gòu)和原理

詳細介紹消息隊列的概念、原理及使用場景(附案例)

?特點:完整的實現(xiàn)了AMQP,集群簡化,持久化,跨平臺

RabbitMQS使用

1、RabbitMQ安裝 (rabbitmq-server, php-amqplib)

2、生產(chǎn)者向消息通道發(fā)送消息

3、消費者處理消息

工作隊列

詳細介紹消息隊列的概念、原理及使用場景(附案例)

思想:由生產(chǎn)者發(fā)送給消息系統(tǒng),消息系統(tǒng)把任務封裝成消息隊列之后,然后供多個消費者使用同一個隊列

這不僅解決了生產(chǎn)者和消費者之間的解耦,還可以實現(xiàn)了消費者和任務的共享,減緩了服務器的壓力。

五、總結(jié)

以上主要學習消息隊列的概念,原理,場景。解耦案例,以及了解RabbitMQ的簡單使用方法。

六、問題

redis 和消息服務器 選擇的最大區(qū)別是什么。

我的理解是Redis 是一個一個處理請求,redis屬于單線程,它和消息服務器 IO 的實現(xiàn)方式不同,一個是同步一個是異步,而redis使用的是同步阻塞,而消息服務器使用的是異步非阻塞。

更多Redis相關(guān)知識,請訪問Redis使用教程欄目!??

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點贊9 分享