<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ## 隊列的思考 ### 隊列的本質 單純說隊列也可代指一種[數據接口](https://baike.baidu.com/item/%E9%98%9F%E5%88%97/14580481?fr=aladdin)。 隊列MQ(Message Queue/消息隊列)不是指某一項技術,而是項目中常用的解耦解決方案,或者說是一種中間件平臺。 我們通常所說的隊列(服務)一般都是指消息隊列。 > 異步是一種編程模型,MQ是一種異步實現方式。隊列實際上也是一種異步模型的應用。 **消息隊列的組成:** - 數據結構(存/取任務:任務信息/任務參數payload) - 消息發布(任何客戶端都可以往數據結構中新增消息) - 消息消費(需要worker來消費消息,即執行任務) > 所以現在理解了吧,隊列并不是某一技術,而是多種技術的組合形成的解決方案。 異步是一種編程模型,隊列是其實現。 (有點類似于[訂閱/發布](http://mp.weixin.qq.com/s?__biz=MzA5NzkwNDk3MQ==&mid=2650585339&amp;idx=1&amp;sn=d3276dcd3637b6e9ed5491ce3c225214&source=41#wechat_redirect)) * * * * * ### 隊列即服務 隊列的本質就是服務/任務。任務執行需要payload(載荷/參數)。 既然是服務,那就是通用的,所以它即既可加入隊列中執行,也可以直接執行。 ``` if ($if_quque) { QueueClient::push('cancelOrderUpdateStorage', $data); } else { Logic('queue')->cancelOrderUpdateStorage($data); } ``` 服務/任務可以被直接調用,也可在隊列中調用。不論怎么調用,任務都是通用的。 ~~~ shopnc把隊列的 【具體任務的業務實現】 全部都放在 邏輯文件中了。 $logic_queue = Logic('queue'); ... while (true) { $result = $logic_queue->$method($arg); } 但是這樣有個問題:邏輯文件是在守護進程中載入內存了,如果后期有新的隊列任務,需要修改邏輯文件,那么就要停止守護進程,重新運行服務才能生效了。 ~~~ * * * * * ### 隊列要注意不能忽視的一個問題:worker如何執行 這里面有一個很重要的問題,又容易被忽視的問題就是,**worker如何執行。** **任務其實就是業務邏輯。** >[danger] 通常我們的業務邏輯代碼是基于系統代碼和框架環境(依賴于工具類/函數/應用配置/系統配置等等)的,所以隊列的業務邏輯也要能保證在一樣的環境下執行,否則就很麻煩,不統一會造成邏輯侵入嚴重。 那么問題來了: 如果還以傳統的方式,worker是個無限循環,如果每次還從框架的入口進去: ```php > php index.php/queue/task/sendmail ``` **那么每次都要加載初始化框架,這將是很大的開銷**(這用的還是訪問式web的url模式)。 所以隊列的運行就不能照搬**訪問式web運行方式**了。 我們希望只需要一次加載系統框架環境就可以,此后就直接循環執行task就可以,而無需每次初始化環境,**這種方式就有點類似于Swoole的運行方式了——長生命周期(邏輯內存常駐)**。 其實,成熟的框架已經想好了這個問題,都支持控制臺模式,url訪問模式和控制臺模式能能夠引入框架環境。 可參考:think-queue、shopnc的queue 之前的記錄: ```text **有兩種觀點:** 1. 隊列處理程序為了性能有時候沒必要完全引入整個MVC 2. 如果不引入MVC的話,那就相對獨立了,系統配置什么的,環境,自動加載,數據模型等,這就很不方便了。 當然如果框架系統考慮到了隊列,CLI那么提供一種CLI模式運行環境,那也是極好的。 另外需要注意的一個問題就是,假設a程序是處理程序,那么現在每個幾秒鐘去運行一次a還是運行a,在里面死循環比較好。**(這個和php的運行模式和生命周期有關,例子是Laravel和Swoole,下面有探討。)** 如果每隔幾秒去運行a,那就相當于另外的程序去主動調用a了(命令行/自動任務等),那么就要注意不能重復調用a,不能多個其他程序都去調用了a,這可以做一個文件鎖來保證a同一時間內只能被調用一次,也就是不允許并發執行。死循環的話,一個腳本太長時間執行,擔心影響性能,出現內存泄露,并且死循環不能是“死的”,需要提供可以控制的能力,可以停止執行,這可以用文件鎖/配置/信號處理之類的來做到。不能強行的對處理程序做“熱插拔”,否則如果再處理重要任務時異常中斷就可能會出現意外。 還要有監控的能力,知道程序是否執行了,最后的執行時間,執行日志等。 所以兩種方法各有優點和缺點,如果取其中間,調用&程序內循環,增加調用間隔,循環增加限制(可以是執行時間,也可以是循環次數),超過自動退出。 記住任何時候,最好的就是最合適的。 ``` ### 隊列消費的順序 有時,隊列消息的消費順序也至關重要,必須滿足先進先出,即先入的隊列要先被消費。 比如兩條消息,存款和取款,正常順序,先存款后是可以成功完成取款的,但是如果取款消息被先消費了,那么就會造成取款失敗。這樣來看的話,多工人搶占消息,多進程錯開取消息,是不行的!因為不能保證消費順序與入列順序的同步。那么在這種對 隊列消息消費順序 有要求的情況的任務,只能單進程一條一條的取消息,串行、阻塞執行消息任務了。 * * * * * ### 隊列:延伸思考——為什么要有隊列 如果計算機是神,或許根本不需要異步這東西。沒有任何東西是不需要,而我們非要平白無故的造出來的。 #### 隊列不是神,而是農民 等待還是要等待的,不可能不等待的,**計算機又不是神,該耗的時還是要耗**(只不過放在另外一個地方去耗,不用你當前一直干等著而已),不可能一秒鐘做完所有的事情,不可能無條件的滿足你,只是告訴你操作放到隊列中排隊去做了(交給另外一個人去做),當前馬上給你返回,不阻塞你,不用讓你當前一直等著而不能干別的。 為了用戶體驗和系統效率,只能選擇異步的方式。 #### 隊列的組成: 1. 工人(執行者) 2. 任務(消費者) 3. 消息(生產—消費媒介) 客戶端(生產者) #### 消息的安全性/隊列的可靠性 >[danger] 隊列是一種中間件服務,屬于外部系統,它本身的作用是為了[解耦](https://www.zhihu.com/question/20278169)(讓合適的人干合適的事情,并且相互之間沒有依賴)。**既然屬于外部系統,那么業務邏輯就不能依賴于隊列服務,所以消息重發、消息丟失、并發取消息、防重復消費等問題都需要考慮到,無論任何情況下都要確保業務邏輯的正確性。** 消息放在Redis里面,如果服務器故障,消息全部丟失怎么辦,不能出現取款永遠不能到賬吧,不能因為隊列的穩定性和故障,導致業務出錯,甚至出現BUG吧,所以在設計任何一條隊列消息和任務時時,都要考慮容錯性,比如要任務處理時必須要嚴格校驗到賬情況,有其他的業務記錄表,消息表等,**要用鎖,消息系統是不可靠的,即使出現打款消息重發,也不能導致系統多次打款,即要保證業務的冪等性**,同時在消息無法到達時,比如遲遲24小時還沒到賬,有可能是隊列丟消息了,那么系統要檢查出來,重發消息。這可以用計劃任務做,每隔24小時檢查一次,掃出超過24小時沒有到賬的訂單,看看是否是出現丟消息了。 隊列還要考慮一種情況,消息取了,但是任務進程卻被殺死了,那么也會出現消息假消費的問題,還有任何時候,任何程序執行到任何地方都有可能失敗,斷電啊,地震啊,進程意外被殺死啊,硬盤報廢啊,硬件不可逆損壞啊,……。 所以程序任何時候,任何部位都要考慮這些意外,都要做好容錯。 * * * * * ### 安全的守護進程實踐 #### 運行狀態文件 守護程序狀態可以用一個文件來控制表示,進程的生命周期中共有三種狀態: 1. name-runing 2. name-end 3. name-ending 操作只有兩種:啟動進程 和 停止進程。 只有 `無狀態文件` 和 `name-end` 時可以啟動進程(已停止 時可以啟動);只有 `name-runing` 時可以停止進程。 #### 確保安全,無并發問題:鎖 **程序運行過程中,會一直鎖住當前的狀態文件。** 這樣就能防止重復執行程序,確保沒有并發問題,保證其安全性。 鎖住當前狀態文件,除了保證程序的安全性,還能確保用戶不能手動誤刪除狀態文件,保護程序不會受外部影響,保證其可用性。 #### 怎樣判斷程序的運行狀態? **運行中:** `name-runing` **沒有運行:** 停止中:`name-ending` 已停止:`name-end` 或 `沒有任何狀態文件`(停止后,由于沒有鎖,狀態文件可能被用戶刪除) >[danger] 計劃任務,比如每隔一分鐘,掃描表,將沒有IP位置的字段全部更新(IP \> IP城市),如果檢測上次的任務進程還在執行,也就是還在崗,那么本次放棄執行,否則就會出現前一分鐘的進程和當前進程一起執行,造成重復并發問題而重復消費了,所以每個進程開始前都需要檢查狀態。其實就相當于計劃任務每次檢測當前有無執行,有則不管了,沒有則啟動一個,相當于是一個喚醒,睡著的就把你喚醒,醒著的就不必喚了,**所以計劃任務其實是一個喚醒任務。** > 這個不用文件記錄狀態,用MySQL表也可以,但是要用Inndb行鎖,任務名字段唯一索引。 > > 后臺進程表(id,任務名,進程ID:每次可能都不一樣,本次執行之間,本次結束時間,狀態,啟動命令) #### 運行日志 出了程序的運行狀態文件,我們還可以增加一個程序運行日志文件,記錄程序生命周期的運行動作: ```log 2018-4-30 12:00:37 start 2018-4-30 12:01:59 runing 2018-4-30 12:02:27 ending 2018-4-30 12:02:34 end ``` (運行日志只記錄程序的生命中期中的運行狀態等動作,不記錄程序本身日志信息) * * * * * ### 再談阻塞 ![](http://cdn.aipin100.cn/18-5-1/11923029.jpg) [Go并發編程案例解析](http://www.imooc.com/learn/982) 雖然最后還是阻塞的,總的執行時間無論怎樣都是一樣的,只是采用這樣非阻塞的方式運行可以異步的部分,在一定意義上達到了控制程序執行順序的目的了,如提前返回部分數據給用戶,使其盡早呈現結果,異步使得程序更加靈活,提高了程序整體的運行效率,雖然總的執行時間都是一樣的。 說錯了,串行的話總時間都是一樣的,真正異步并行執行的話(同時做多件事),執行時間會少。 * * * * * ### 擴展 [隊列 · php筆記 · 看云](http://www.hmoore.net/xiak/php-node/347805) [RabbitMQ, ZeroMQ, Kafka 是一個層級的東西嗎, 相互之間有哪些優缺點? - 知乎](https://www.zhihu.com/question/22480085/answer/23106407) [深入分析消息中間件的選型(AMQ,RMQ,Kafka,KMQ,ZMQ等)](https://www.toutiao.com/a6540024140257034759/?tt_from=weixin&utm_campaign=client_share&timestamp=1522769518&app=news_article_lite&utm_source=weixin&iid=25315997380&utm_medium=toutiao_android&wxshare_count=1) > 并且消息中間件服務器(一般簡單的稱之為Broker)中沒有消息堆積,……,消息中間件大道至簡:一發一存一消費,沒有最好的消息中間件,只有最合適的消息中間件。 [【系統架構】Web系統大規模并發:電商秒殺與搶購](http://mp.weixin.qq.com/s/zDbcV_vJeBOnAYxK0WEJQQ) > 必須盡可能“快”,在最短的時間里返回用戶的請求結果。為了實現盡可能快這一點,接口的后端存儲使用內存級別的操作會更好一點。仍然直接面向 MySQL之類的存儲是不合適的,如果有這種復雜業務的需求,都建議采用異步寫入。 > > 當然,也有一些秒殺和搶購采用“滯后反饋”,就是說秒殺當下不知道結果,一段時間后才可以從頁面中看到用戶是否秒殺成功。但是,這種屬于“偷懶”行為,同時給用戶的體驗也不好,容易被用戶認為是“暗箱操作”。(這個沒有辦法,不異步你就干等啊,異步就是告訴你正在排隊啊) > > 更可怕的問題是,是用戶的行為特點,系統越是不可用,用戶的點擊越頻繁,惡性循環最終導致“雪崩” > > 我們知道在多線程寫入同一個文件的時候,會存現“線程安全”的問題(多個線程同時運行同一段代碼,如果每次運行結果和單線程運行的結果是一 樣的,結果和預期相同,就是線程安全的)。(即沒有并發問題,也可客觀的理解為操作的冪等性) > > FIFO隊列:這樣的話,我們就不會導致某些請求永遠獲取不到鎖。看到這里,是不是有點**強行將多線程變成單線程**的感覺哈。(解決并發問題,鎖,隊列異步操作,其實都是將并行強制變為串行的解決方案。) [【系統架構】聊聊開源消息中間件的架構和原理](https://mp.weixin.qq.com/s/NwjYJde9_TC4PXMPpYw1Gw) [RocketMQ 源碼合集](https://mp.weixin.qq.com/s/xrnEMQ07kuaE9oCK1Z71hg) [消息隊列應用場景 - 13070113 - 博客園](http://www.cnblogs.com/stopfalling/p/5375492.html) > 按照以上約定,用戶的響應時間相當于是注冊信息寫入數據庫的時間,也就是50毫秒。**注冊郵件,發送短信寫入消息隊列后,直接返回,<span style="color:red;">因為寫入消息隊列的速度很快,基本可以忽略</span>**,因此用戶的響應時間可能是50毫秒。因此架構改變后,系統的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了兩倍。 [消息隊列使用的四種場景 - CSDN博客](https://blog.csdn.net/ntotl/article/details/72765713)(原文) [【原創】分布式之延時任務方案解析 - 孤獨煙 - 博客園](https://www.cnblogs.com/rjzheng/p/8972725.html) [為什么分布式一定要有redis?](https://mp.weixin.qq.com/s/gEU8HtsQNPXY8bzkK-Qllg) > 一剎那者為一念,二十念為一瞬,二十瞬為一彈指,二十彈指為一羅預,二十羅預為一須臾,一日一夜有三十須臾。 > 那么,經過周密的計算,一瞬間為0.36 秒,一剎那有 0.018 秒.一彈指長達 7.2 秒。 >[danger] **任何東西都要去深入思考,做到面面俱到,考慮每一種可能性,應對每一種突發情況,這樣才能保證是嚴謹的。** 避重就輕,拈輕怕重,避實就虛,怕麻煩,不肯深入問題的本質,這樣永遠都只會停留在表面,無法解決核心問題。 [【原創】分布式之消息隊列復習精講 - 孤獨煙 - 博客園](http://www.cnblogs.com/rjzheng/p/8994962.html) ~~~ 1:為什么使用消息隊列? 2:使用消息隊列有什么缺點? 3:消息隊列如何選型? 4:如何保證消息隊列是高可用的? 5:**如何保證消息不被重復消費?** 6:如何保證消費的可靠性傳輸? 7:如何保證消息的順序性? ~~~ >[danger] 如何保證不會出現重復消費消息? > > 我們不要總是將目光聚集在**如何防止重復取消息**的問題上,雖然這是造成重復消費問題的根源(其實這只是會造成重復消費的原因之一,還有 消費確認失效 等情況也可能會造成這個問題),但是解決重復消費的問題并不是只有這一個辦法,不要緊盯目標,而忘記自己本來是要干什么了,被自己狹窄的眼界給局限了。**不能死板,不能墨守成規,轉變思路,側面突圍,用最令人意想不到的方式解決問題才更漂亮。** 多給自己提問,當你面對這個問題時,你實際是在面對什么,當你在解決某個問題時,你實際是在解決什么。 > > 將目光從如何防止重復取消息上移開,還有很多解決這個問題的辦法:1. 增加消費記錄表,記錄消費的情況;2. 利用業務數據自己比對消費記錄;3. 消息ID為主鍵,做insert;**其實方案都是消費時自己再確認一遍,這也符合了正確性從不依賴于外部或其他系統的原則(比如不依賴,不信任取消息操作是否會重復取),不過很重要的一點是不能忘記鎖,查詢時一定要互斥鎖,不然還是于事無補,因為會出現并發問題。** [【系統架構】分布式之消息隊列復習精講(上)](https://mp.weixin.qq.com/s/uRaG2ZB8hBoxum73OHDHNQ) [從單一架構到分布式交易架構,網易嚴選的成功實踐](https://mp.weixin.qq.com/s/nv3Ht7OqTYQw31QFDX3gNg) >[danger] **沒有完美的架構設計,世上也沒有絕對的事情,沒有誰能保證絕對可靠、安全和高可用,但我們有補償和容錯(類似還有重試,確認等機制),也是能做到萬無一失的。** [【消息隊列 MQ 專欄】消息隊列之 ActiveMQ](https://mp.weixin.qq.com/s/ngfCCsuYJHBc6gRTIROgMQ) [分布式消息隊列 RocketMQ 源碼分析 —— Message 拉取與消費(上)](https://mp.weixin.qq.com/s/EwZDg5IRHJ5TGfGvpfl9Ew) [數據結構 | Java 隊列 —— Queue 詳細分析](https://mp.weixin.qq.com/s/FH5lQac65CT9Pt_TpT1E7Q) mq和數據庫區別主要是是否解決了通知問題 * * * * * 關鍵字:線程安全 * * * * * ### Lua:代碼級的原子性 [Lua 是一個小巧的腳本語言 - HackerVirus - 博客園](https://www.cnblogs.com/Leo_wl/p/8405661.html) > lua腳本是用C語言寫的,體積很小,運行速度很快,并且每次的執行都是作為一個原子事務來執行的 > > 原子性的操作:Redis會將整個腳本作為一個整體執行,中間不會被其他命令插入。因此在編寫腳本的過程中無需擔心會出現競態條件,無需使用事務。 [Lua 與 Redis - 挨踢人啊 - 博客園](https://www.cnblogs.com/itrena/p/5926878.html) [redis實現秒殺功能例子(采用lua的原子性保證數據的一致性) - CSDN博客](https://blog.csdn.net/futao127/article/details/80617214) [利用redis + lua解決搶紅包高并發的問題 - CSDN博客](https://blog.csdn.net/hengyunabc/article/details/19433779/) * * * * * ### 定時計劃:后知后覺 圖 ![](http://cdn.aipin100.cn/18-6-15/11370565.jpg) * * * * * last update:2018-1-16 14:10:56
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看