主頁 > .NET開發 > 小白關于“訊息佇列”處理“秒殺”業務的疑惑!

小白關于“訊息佇列”處理“秒殺”業務的疑惑!

2020-09-16 12:05:40 .NET開發

最近在學訊息佇列,一些不懂得地方,希望得到各位的解答,謝謝。
各大網站上面寫到訊息佇列的時候,都有一個介紹說,能處理秒殺業務,比如資料庫10個商品,有100個請求過來,先將請求放到佇列里面,然后取佇列里面取10個請求,然后對資料庫進行操作。請求實列圖如下(阿里云復制的)

原文地址:http://www.cnblogs.com/linjiqin/p/5720865.html

“將請求放入佇列”,一度我以為,“佇列”這個東西,可以把一個"http請求"存起來,程式到佇列里面取的時候,取出來的還是一個“http請求”,然后把請求可以回傳給客戶端,我今天嘗試了微軟的msmq,簡單的用了一下,好像佇列只能放字串,我就蒙了,那如果是這樣,那怎么處理這個秒殺??

后面問了一些人,說這個佇列是放請求的資料,不是“請求”。

就上面秒殺的例子,下面有這些疑問:
1、佇列存放資料,假如我一個http請求發過來,服務器把請求資料放到佇列里面,然后我的請求是不是會釋放回傳給客戶端?此時客戶端的狀態或者表現是什么呢?等待中嗎?
2、我資料庫只有10個商品,我把請求的資料都放到佇列里面,那佇列怎么樣通知我的程式,通知他可以開始去取佇列里面的資料呢?
3、我的程式從佇列里取出了資料,處理好了,我怎么樣去通知前端在等待的界面,顯示結果呢??



求各位有經驗的大神,幫我分析一下這些細節,謝謝!


uj5u.com熱心網友回復:

沒有訂,我自己試試。

uj5u.com熱心網友回復:

樓主有思路了嗎

uj5u.com熱心網友回復:

給你寫一個控制臺的demo
using System;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {

        static void Main(string[] args)
        {
            for (var i = 0; i < 30; i++)
                Test(i);
            Console.WriteLine("................按任意鍵結束");
            Console.ReadKey();
        }

        static int 執行中數量;
        static object lockflag = new object();
        static event Action<int> 有任務結束;

        static async void Test(int i)
        {
            lock (lockflag)
            {
                if (執行中數量 >= 3)
                {
                    Action<int> proc = null;
                    proc = new Action<int>(x =>
                    {
                        有任務結束 -= proc;
                        Test(i);
                    });
                    有任務結束 += proc;
                    return;
                }
                else
                    執行中數量++;
            }
            var r = await 加100000(i);
            Console.WriteLine($"輸入:{i}, 輸出:{r}");
            lock (lockflag)
            {
                Console.WriteLine($"--------輸入:{i} 結束后執行中數量為:{執行中數量}");
                執行中數量--;
            }
            if (有任務結束 != null)
                有任務結束(i);
        }

        static Random rnd = new Random();

        static async Task<int> 加100000(int x)
        {
            var d = rnd.Next(3000, 5000);
            await Task.Delay(d);    //模擬耗時操作
            return x + 100000;
        }

    }
}


這里,假設任務是要把輸入的 i 加上 100000 然后輸出,主程式快速發起了30個并發任務,但是此 Test 方法要求最多同時執行3個任務,其它的排隊等候。你可以運行這個例子,然后回答你自己的問題:

當產生了并發任務之后,首先進入第一個 lock 管理區域,此時判斷如果運行的并發任務數量>=3的時候,則向事件監聽佇列注冊監聽,然后就立刻結束了。當事件發生(有其它任務結束),則從事件佇列中首先注銷監聽,然后重新執行本任務(重新開始判斷是否并發任務數量>=3)。否則,它就立刻把并發任務數量++。注意這兩個動作是在 lock 管理范圍中的,同一時間只有一個執行緒能夠執行這個范圍中的代碼,所以這就保證不會產生混亂控制。

15或者20年前,像java的語言比較原始,遠比當初的 .net 更加原始,所有一大堆垃圾java書都是抄襲30年前、40年前的c、unix書籍上的一些概念。所謂“佇列”就是一個比較容易庸俗化的概念。許多程式貌似用了“佇列”這個詞兒給自己貼金,而實際上還是順序阻塞的思路,那些書上的“佇列”就是運行時低效的、代碼繁瑣的東西。所以這里關鍵是用精煉的代碼,用優雅的.net機制來寫高效率的代碼,所以你提的幾個問題很好,你沒有糾結“佇列”這個詞兒而是問到了關鍵的細節上!

uj5u.com熱心網友回復:

讓我們看看真正在 .net 上這個程式應該怎么寫
using System;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {

        static void Main(string[] args)
        {
            for (var i = 0; i < 30; i++)
                Test(i);
            Console.WriteLine("................按任意鍵結束");
            Console.ReadKey();
        }

        static async void Test(int i)
        {
            var r = await 加100000(i);
            Console.WriteLine($"輸入:{i}, 輸出:{r}");
        }

        static Random rnd = new Random();

        static async Task<int> 加100000(int x)
        {
            var d = rnd.Next(3000, 5000);
            await Task.Delay(d);    //模擬耗時操作
            return x + 100000;
        }

    }
}

因為 .net 系統執行緒池會自動化地根據當前系統資源情況而調度——延遲——發起并發任務,所以你根本不需要自己寫什么控制任務的代碼,根本不用什么“佇列”。因為 .net 系統執行緒池本身就是佇列,而且它本身就把執行緒執行任務所需要的記憶體中的狀態物件資料維持著(根本不需要什么“字串)。

因此,實際上根本不用扯到什么“佇列”,在 .net 上你用涉及到系統執行緒池的各種高級別的框架(例如PLinq)來編程,你基本上就不用考慮什么佇列了。糾結佇列時我們可以看作是 java 程式員或者一些 c 語言初學者,這些人才會特別喜歡底層的概念。

uj5u.com熱心網友回復:

我上面先說了自己管理“佇列”的控制機制,然后說明作為一個 .net 程式員基本上是并不用考慮佇列的,那么最后一個解答就是,其實有些人所說的另外一種“佇列”并非真正佇列,而是人家微軟(或者IBM等等)過去開發的通訊網關程式。人家允許一個字串(比如說一個 .net 物件的 json 序列化結果)從一臺機器傳送到另外一臺機器,目標機器順序讀取這些字串。

那么你的目標機器讀取了資料,就好像上面我寫的 Test(i) 一樣,你不過就是執行“決議字串(str)”來判斷這個 json 是不是一個任務描述、如果是就耗時處理它。所以這個時候所謂的“佇列”實際上是指一個已經發布出來的成熟的用于字串“流”的通訊網關框架(例如 MSMQ)概念,這個時候就更加需要清醒,不要糾結什么“佇列”概念了!

uj5u.com熱心網友回復:

業務邏輯是其實就是:用戶開始秒殺,如果現在服務器處理的請求過多,就將他轉到錯誤頁面,不處理秒殺的業務邏輯。
解決的問題應該是:當用戶訪問量大的時候可能出現網站崩潰,或者并發問題,比如只有30個商品,結果賣出去31個,庫存為-1。
文中說的佇列只是判斷“處理的請求過多”這個邏輯的一種手段。
至于這個手段好不好,我沒用過,不評論。

uj5u.com熱心網友回復:

比如說有20種商品、一共有10萬件秒殺貨物,那么你頂多能夠對排到10萬以后的用戶請求才能說“不處理”,并不能隨便就根據自己的想象的物理機器的數量來說“不處理秒殺業務邏輯”。

uj5u.com熱心網友回復:

自己的想象的物理機器的數量    -->   自己的想象的物理機器的內部資源數量

內部資源是個綜合指標,所以幾十年前的作者只能粗劣地用一個自己想當然地“并發數”來控制這個指標,而且還是靜態的數字。而 .net 系統執行緒池能夠動態管理執行緒調度,一開始會延遲一點時間來避免同時啟動過多任務,所以通常不用自己發明什么佇列機制。

uj5u.com熱心網友回復:

秒殺時,根本不減庫存。

例如秒殺商品10萬件,那么把這些單品放到高速記憶體中,每接受一個秒殺請求就減掉一個單品,但是整個管理資訊系統、庫存賬,那是以后才要減的,甚至可能是20秒鐘或者20分鐘——用戶已經被導航到支付甚至是發歡訓節——之后才處理的。

這里的問題是秒殺的任務處理調度機制,其實并不直接設計秒殺操作所涉及的資料結構問題。只是順便說一下。

uj5u.com熱心網友回復:

佇列是快取資料,不是快取請求,
petshop的案例有個佇列處理訂單的資料,將訂單放到佇列里,然后異步逐個處理訂單

uj5u.com熱心網友回復:

秒殺就相當于你到醫院看病時先要掛個號,所以它跟后續的什么減庫存之類的操作是脫離開的。秒殺服務器上只要關系“放多少號”就行了。這決定了秒殺其實也非常簡單。

關于“請求、資料”的區別,這個其實是文字游戲。任何請求都是資料,關鍵是你要知道這里是哪一種深度的資料,并且知道資料的保存跟監聽和調度它的專用框架緊密相關(不可能脫離開程序而只談資料),那么你才接觸實質。

uj5u.com熱心網友回復:

秒殺非常簡單。之所以最初的國營軟體公司做的12306網站總是崩潰,那是體制問題、人的問題,最常見地就是那些人滿腦子只有“增刪改查”而并不區分到底是哪一種東西的增刪改查,就好像只關心搬磚而不關心建筑,結果就是做東西總是推倒重來。所以我們首先強調需求分析,強調流程和狀態設計技術,而不失強調底層編程手藝。

uj5u.com熱心網友回復:

舉這里的例子,這里的
 event Action<int> 有任務結束
宣告了一個多播委托串列,它是不是資料?.net 執行緒池機制本身所管理的內部資料結構是不是資料?

唯一可以挑出來點兒的,也就是說這兩個資料(多播委托、執行緒池內部機制)沒有將這些資料序列化/反序列化到你自己的檔案中而已。人家的現成的管理機制高速地管理大量排隊資訊,能不能只想著自己發明那些簡單的博客上的最底層的簡單“佇列”概念而不認識 .net 框架中許多高級別的自動化流水處理任務調度的功能。

uj5u.com熱心網友回復:

比如說我們可以把代碼
            if (有任務結束 != null)
                有任務結束(i);
改為代碼
            if (有任務結束 != null)
            {
                var cnt = 0;
                foreach (Action<int> proc in 有任務結束.GetInvocationList())
                {
                    proc(i);
                    if (++cnt >= 3)
                        break;
                }
            }
這里僅僅通知最多3個宿主,而不是把事件通知給所有的監聽宿主。

這里編程時就是圍繞“資料的”,但是這是什么資料?這種資料是不是佇列?這是高級的資料概念,是解決實際應用的各種實用框架,不是重復一個簡單的資料結構。

uj5u.com熱心網友回復:

參考 13 樓 以專業開發人員為伍 的回復:
舉這里的例子,這里的
 event Action<int> 有任務結束
宣告了一個多播委托串列,它是不是資料?.net 執行緒池機制本身所管理的內部資料結構是不是資料?

唯一可以挑出來點兒的,也就是說這兩個資料(多播委托、執行緒池內部機制)沒有將這些資料序列化/反序列化到你自己的檔案中而已。人家的現成的管理機制高速地管理大量排隊資訊,能不能只想著自己發明那些簡單的博客上的最底層的簡單“佇列”概念而不認識 .net 框架中許多高級別的自動化流水處理任務調度的功能。


好了 說了這么多 我就想知道佇列訊息異步處理后成功后 怎么告訴用戶秒殺成功

uj5u.com熱心網友回復:

C# 的webapi,本身可以用async關鍵字的 ,這意味著本身語法機制上你就可以await 一個任務結束。
so,那還要我們怎么說呢?

如果說其他語言呢?其他語言說,十年前用ajax重繪或者等待服務器端轉向到結果頁(比如早期網銀系統,你先轉到他那邊,他處理完畢再轉向到你這邊),當然現在則直接用websocket等待推送通知。

ps:秒殺是瞬時的,根本就不存在什么異步處理。原理上就是一個令牌桶,有牌子就進,沒牌子就扔。

uj5u.com熱心網友回復:

對于秒殺,用佇列只是策略問題。而且還是一個并不是很好的策略
實際上秒殺動作本身,應該是令牌桶限流策略,而非佇列策略。

秒殺動作后續付款動作倒是可以丟佇列里玩,這個意味著后續動作可以是分步/分布的

uj5u.com熱心網友回復:

我今天嘗試了微軟的msmq,簡單的用了一下,好像佇列只能放字串,我就蒙了,那如果是這樣,那怎么處理這個秒殺??

后面問了一些人,說這個佇列是放請求的資料,不是“請求”。
----------------------------------------------------------------------------------
沒用過msmq ,所以百度了一下概念。按照我的理解是在一臺機器上存盤資料,多臺服務器可以共享/共同使用這個佇列,個人認為用這個做秒殺理論上是可以,但感覺麻煩些,而且只能在windows上使用。這種佇列蠻多的例如redis。


就上面秒殺的例子,下面有這些疑問:
1、佇列存放資料,假如我一個http請求發過來,服務器把請求資料放到佇列里面,然后我的請求是不是會釋放回傳給客戶端?此時客戶端的狀態或者表現是什么呢?等待中嗎?
----------------------------------------------------------------------------
請求肯定會釋放的,客戶端根據服務器的邏輯顯示,比如你想讓他成功就顯示成功,你要是想等待,做成等待也可以,假裝很多人在強。


2、我資料庫只有10個商品,我把請求的資料都放到佇列里面,那佇列怎么樣通知我的程式,通知他可以開始去取佇列里面的資料呢?
-----------------------------------------------------------------------------------------------------------------------------------------
頁面總量顯示資料庫,剩余數顯示佇列剩余數,佇列一般無法通知到客戶端,需要客戶端主動請求佇列剩余數。


3、我的程式從佇列里取出了資料,處理好了,我怎么樣去通知前端在等待的界面,顯示結果呢??
------------------------------------------------------------------------------------------------------
有一些第三方 ws.js 庫支持傳統的請求和ws ,只要分別寫代碼就好,復雜是一定的,有經驗的前端開發一般都會寫

uj5u.com熱心網友回復:

。。。。
思維這么僵化,看了腦瓜疼。。。

uj5u.com熱心網友回復:

說的都是啥?

uj5u.com熱心網友回復:

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/56855.html

標籤:非技術區

上一篇:hex字串轉成ascII字串

下一篇:C#跳轉到Form2時Form1不顯示

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more