PHP消息隊列實現及應用講述

attachments-2020-04-kTQZDjwu5e85a43b6fdf6.jpg

1、認識消息隊列php

1.1 消息對列概念mysql

  從本質上說消息對列就是一個隊列結構的中間件,也就是說消息放入這個中間件以後就能夠直接返回,並不須要系統當即處理,而另外會有一個程序讀取這些數據,並按順序進行逐次處理。redis

  也就是說當你遇到一個併發特別大而且耗時特別長同時還不須要當即返回處理結果,使用消息隊列能夠解決這類問題。sql

1.2 核心結構shell

v2-3cc18fd891dc8911c67885add75b41e8_720w.jpg

由一個業務系統進行入隊,把消息逐次插入到消息隊列中,插入成功以後直接返回成功的結果,後續會有一個消息處理系統,這個系統會把消息系統中的記錄逐次進行取出並進行處理,完成一個出隊的流程。數據庫

1.3 應用場景緩存

  數據冗餘:好比訂單系統,後續須要嚴格的進行數據轉換和記錄,消息隊列能夠把這些數據持久化的存儲在隊列中,而後有訂單,後續處理程序進行獲取,後續處理完以後在把這條記錄進行刪除來保證每一條記錄都可以處理完成。服務器

  系統解耦:使用消息系統以後,入隊系統和出隊系統是分開的,也就說只要一天崩潰了,不會影響另一臺系統正常運轉。架構

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

  異步通訊:消息自己使用入隊以後能夠直接返回。

  擴展性:例如訂單隊列,不只能夠處理訂單,還能夠給其餘業務使用。

  排序保證:有些場景須要按照產品的順序進行處理好比單進單出從而保證數據按照必定的順序處理,使用消息隊列是能夠的。

以上都是消息隊列常見的使用場景,固然消息隊列只是一箇中間件,能夠配合其餘產品進行使用。

1.4 常見隊列實現優缺點

  隊列介質

    一、數據庫,例如mysql(可靠性高,易實現,速度慢)

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

    三、消息系統,例如rabbitMq (專業性強,可靠,學習成本高)

  消息處理觸發機制

    一、死循環方式讀取:易實現,故障時沒法及時恢復;(比較適合作秒殺,比較集中,運維集中維護)

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

    三、守護進程:相似於php-fpm 和php-cg,須要shell基礎

2、解藕案例:隊列處理「訂單系統」和「配送系統」

  簡單說一下程序解耦:程序解耦就是避免出現你老婆和你媽同時掉到水裏先去救誰的問題

  對於訂單流程,咱們能夠設計兩個系統,一個是「訂單系統」 另一個是 「配送系統」, 在網購的時候咱們應該都見過,當我提交了一個訂單以後,我在後臺能夠看到個人貨物正在配送中。這個時候就要參與進來一個「配送系統」。

  若是咱們在作架構的時候把 「訂單系統」 和 「配送系統」 設計在一塊兒的話就會出現一些問題,首先對於訂單系統來講,由於系統的壓力會比較大,可是 "配送系統" 不必爲這些壓力作一些即時的反應。

  第二個咱們也不但願在訂單系統出現故障以後致使配送系統也出現故障,這個時候就會同時影響到兩個系統的正常運轉。因此咱們但願把這兩個系統進行解耦。這兩系統分開以後咱們能夠經過一箇中間的 「隊列表」 進行這兩個系統的溝通。

2.1 架構設計

v2-f4b41fc300d4d726d0ef7249e0fd167c_720w.jpg

  一、首先訂單系統會接收用戶的訂單,而後進行訂單的處理。

  二、而後會把這些訂單信息寫到隊列表中,這個隊列表是溝通這兩個系統的關鍵。

  三、由配送系統定時執行的一個程序來讀取隊列表進行處理。

  四、配送系統處理以後,會把已處理的記錄進行標記。

2.2 程序流程

v2-7fc21f190c53bd947a3245c1efe53e2e_720w.jpg

3、流量削峯案例:Redis 的 list 類型實現秒殺

  redis 基於內存,它的速度會很是快,redis 對數據庫有一個很是好的補充做用由於它是可持久化的,redis會週期性的把數據寫到硬盤裏,因此它不用擔憂斷電的問題,從這方面說它比另外一款緩存 memcache 更有優點些,另外 redis 提供五種數據類型(字符串,雙向鏈表,哈希,集合,有序集合)

  通常狀況下,作秒殺案例,搶購,瞬間高併發,須要排隊 的案例中 redis是一個很好的選擇。

3.1 redis數據類型中的 list 類型

  redis 的list 是一個雙向鏈表,能夠從頭部或者尾部追加數據。

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

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

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

  * RPOP: 移除並獲取列表的最後一個元素

  * LTRIM: 保留指定區間內的元素

  * LLEN: 獲取列表長度

  * LSET: 經過索引設置列表元素的值

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

  * LRANGE: 獲取列表指定範圍內的元素

3.2 架構設計

  一個簡單結構秒殺的程序設計。

v2-1f2cd37d3de4ba88d10dca7deab0dad8_720w.jpg

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

  二、將用戶的id存到redis列表中,讓它排隊。若是規定只有前10個用戶能夠參與成功,若是列表中的個數已經夠了就不會讓它繼續追加數據。這樣redis的列表長度就只會是10個

  三、最後在慢慢的將redis中的數據寫入到數據庫中,以減小數據的壓力

3.3 代碼級設計

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

  二、假使規定只有10人能夠秒殺成功,檢查 Redis 已經存放數據的長度,超出上限直接丟棄說明秒殺完成。

  三、最後在死循環處理存入Redis中的10條數據,而後在慢慢的取數據並存入到mysql數據庫中。

在秒殺這一塊對於數據庫的壓力特別的大,若是咱們沒有這樣的設計,會形成mysql的寫入瓶頸。咱們經過Redis的一個對列list,而後把秒殺的請求放入到Redis裏面, 最後經過入庫程序,把數據慢慢的寫入到數據庫,這樣的話就能夠實現流量的均衡,對mysql不會形成太大的壓力。 

4、RabbitMQ

  這裏講解一些RabbitMQ的使用,首先咱們以前講秒殺案例的時候提到了鎖的機制,防止其餘程序處理同一條記錄,若是咱們的系統架構很是的複雜,有多個程序實時的讀取一個隊列,或者我有多個發送程序,同時來操做一個或多個隊列,甚至我還想這些程序分佈在不一樣的機器上,這種狀況下用redis隊列就有些力不從心了。這種時候怎麼辦呢,咱們就須要來引入一些更專業的消息隊列系統,這些系統能夠更好的來解決問題。

4.1 RabbitMQ的架構和原理

v2-6c7c4cd4eb61a2cda91f5a047af730e1_720w.jpg

特色:完整的實現了AMQP,集羣簡化,持久化,跨平臺

 RabbitMQS使用

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

    二、生產者向消息通道發送消息

    三、消費者處理消息

  工做隊列

v2-71fff4fd96186587b22489ab65703e8a_720w.jpg

思想:

由生產者發送給消息系統,消息系統把任務封裝成消息隊列以後,而後供多個消費者使用同一個隊列。這不只解決了生產者和消費者之間的解耦,還能夠實現了消費者和任務的共享,減緩了服務器的壓力。

attachments-2020-04-v5tTZD975e85a3e0c8403.jpg

相關文章
相關標籤/搜索