最近在學訊息佇列,一些不懂得地方,希望得到各位的解答,謝謝。
各大網站上面寫到訊息佇列的時候,都有一個介紹說,能處理秒殺業務,比如資料庫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熱心網友回復:
給你寫一個控制臺的demousing 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)這里僅僅通知最多3個宿主,而不是把事件通知給所有的監聽宿主。
{
var cnt = 0;
foreach (Action<int> proc in 有任務結束.GetInvocationList())
{
proc(i);
if (++cnt >= 3)
break;
}
}
這里編程時就是圍繞“資料的”,但是這是什么資料?這種資料是不是佇列?這是高級的資料概念,是解決實際應用的各種實用框架,不是重復一個簡單的資料結構。
uj5u.com熱心網友回復:
好了 說了這么多 我就想知道佇列訊息異步處理后成功后 怎么告訴用戶秒殺成功
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字串
