Orleans初战(用分布式解决高并发购物场景)
首先我們來(lái)定義這樣一個(gè)場(chǎng)景:
? ? 商店有10種商品,每種商品有100件庫(kù)存。現(xiàn)在有20萬(wàn)人來(lái)?yè)屬?gòu)這些商品。
OK,那么問(wèn)題來(lái)了。要怎樣保證商品不會(huì)超賣……(要知道可能會(huì)出現(xiàn)20個(gè)人同時(shí)買(mǎi)A商品(或者更糟糕,畢竟后邊20萬(wàn)的大軍,隨時(shí)可能把商店變成廢墟),怎樣保證A商品的數(shù)量絕對(duì)安全)
?
按照大部分系統(tǒng)的解決方案是這樣的:
收到請(qǐng)求放入隊(duì)列,然后對(duì)隊(duì)列順序處理,這樣就避免了系統(tǒng)被瞬間擠爆而且不會(huì)超賣。
這種處理方式裝換成現(xiàn)實(shí)場(chǎng)景是這樣的:客戶到商店先領(lǐng)號(hào),不管買(mǎi)什么商品,都要排隊(duì),然后一個(gè)一個(gè)買(mǎi),直到所有的處理完。
這個(gè)是不是弱爆了………………
這個(gè)解決方案也就相當(dāng)于一個(gè)售賣窗口,大家在那排隊(duì)買(mǎi),你能受得了嗎?
?
先看看現(xiàn)實(shí)商店怎樣解決的(存在即合理):客戶太多就加窗口唄,多雇員工,粗暴又簡(jiǎn)單的解決了問(wèn)題(當(dāng)然大家還是要排隊(duì),但是不是一個(gè)隊(duì)了,緩解了壓力提高了速度哦,老板賺到了更多的錢(qián))
Orleans閃亮登場(chǎng)…………
首先我要多開(kāi)幾臺(tái)服務(wù)器來(lái)處理客戶的請(qǐng)求,怎樣分配呢,要知道我的商品庫(kù)存數(shù)量必須保證安全,如果幾臺(tái)服務(wù)器操作一個(gè)商品那我們要想辦法做到對(duì)象的絕對(duì)同步(joab開(kāi)始也是這樣想的,后來(lái)我才知道是我想多了),要知道加的服務(wù)器處理數(shù)據(jù)同步的消耗實(shí)在太大得不償失啊(線程之間的數(shù)據(jù)安全使用線程鎖我們都閑消耗大,這個(gè)夸服務(wù)器就更別說(shuō)了)……
換個(gè)思路:加幾臺(tái)服務(wù)器,每臺(tái)服務(wù)器買(mǎi)不同的商品,例如:1號(hào)服務(wù)器賣a/b兩種商品,2號(hào)服務(wù)器賣c/d兩種商品…………以此類推,問(wèn)題解決了……
客戶消息說(shuō)買(mǎi)a商品,直接到1號(hào)服務(wù)器排隊(duì),買(mǎi)c商品就去2號(hào)服務(wù)器排隊(duì),(當(dāng)然這里服務(wù)器也要多線程,一樣的解決原理,a商品x線程排隊(duì),b商品y線程排隊(duì))
?
?
?
好了,從場(chǎng)景到解決辦法都出來(lái)了,現(xiàn)在要實(shí)現(xiàn):
照例我們開(kāi)始搭建環(huán)境(事例我就簡(jiǎn)單三層了,現(xiàn)實(shí)項(xiàng)目大家自己根據(jù)項(xiàng)目自己發(fā)揮啊)
?
訪問(wèn)關(guān)系:
?
Orleans.Samples.HostSilo就是個(gè)控制臺(tái)應(yīng)用程序,用于啟動(dòng)Orleans服務(wù)(Silo的啟動(dòng))也就相當(dāng)于售貨的窗口,不同服務(wù)器啟動(dòng)Orleans.Samples.HostSilo來(lái)處理排隊(duì)的請(qǐng)求(配置我就先不貼出來(lái)了,很多地方有)
Orleans.Samples.Grains你可以理解為商品,它在需要在窗口售賣
Orleans.Samples.StorageProvider這個(gè)怎么說(shuō)呢,首先Orleans.Samples.Grains是運(yùn)行在服務(wù)端的而且可以是有狀態(tài)的,我們?cè)趺磥?lái)管理他的狀態(tài),StorageProvider就對(duì)Grain的狀態(tài)做了擴(kuò)展(本例我就那這個(gè)狀態(tài)來(lái)做商品數(shù)據(jù)的讀寫(xiě),并且對(duì)商品扣庫(kù)存時(shí)也是直接對(duì)本Grain的state進(jìn)行操作)
?其它的幾個(gè)我就不講了大家一看就知道是什么了。
?
關(guān)鍵代碼
一、GoodsStorgeProvider
public class GoodsStorgeProvider : IStorageProvider{public Logger Log{get; set;}public string Name{get; set;}public Task ClearStateAsync(string grainType, GrainReference grainReference, IGrainState grainState){return TaskDone.Done;}public Task Close(){return TaskDone.Done;}public Task Init(string name, IProviderRuntime providerRuntime, IProviderConfiguration config){this.Name = nameof(GoodsStorgeProvider);this.Log = providerRuntime.GetLogger(this.Name);return TaskDone.Done;}public async Task ReadStateAsync(string grainType, GrainReference grainReference, IGrainState grainState){Console.WriteLine("獲取商品信息");var goodsNo = grainReference.GetPrimaryKeyString();using (var context = EntityContext.Factory()){grainState.State = context.GoodsInfo.AsNoTracking().FirstOrDefault(o => o.GoodsNo.Equals(goodsNo));}await TaskDone.Done;}public async Task WriteStateAsync(string grainType, GrainReference grainReference, IGrainState grainState){var model = grainState.State as GoodsInfo;using (var context = EntityContext.Factory()){var entity = context.GoodsInfo.FirstOrDefault(o => o.GoodsNo.Equals(model.GoodsNo));entity.Stock = model.Stock;await context.SaveChangesAsync();}}}?前邊說(shuō)過(guò)了Grain是有狀態(tài)的,我定義了GoodsStorgeProvider管理商品的狀態(tài),商品的讀取我是直接從數(shù)據(jù)庫(kù)讀出然后賦值個(gè)它的State,那么知道這個(gè)Grain被釋放,這個(gè)State將一直存在,并且唯一,寫(xiě)入我就直接對(duì)商品的Stock進(jìn)行了賦值并且保存到數(shù)據(jù)庫(kù)(售賣商品,變更的就只有商品的數(shù)量)
?
二、GoodsInfoGrain
[StorageProvider(ProviderName = "GoodsStorgeProvider")]public class GoodsInfoGrain : Grain<GoodsInfo>, IGoodsInfoGrain{public Task<List<GoodsInfo>> GetAllGoods(){using (var context = EntityContext.Factory()){return Task.FromResult(context.GoodsInfo.AsNoTracking().ToList());}}public async Task<bool> BuyGoods(int count, string buyerUser){Console.WriteLine(buyerUser + ":購(gòu)買(mǎi)商品--" + this.State.GoodsName + " " + count + "個(gè)");if (count>0 && this.State.Stock >= count){this.State.Stock -= count;OrdersInfo ordersInfo = new OrdersInfo();ordersInfo.OrderNo = Guid.NewGuid().ToString("n");ordersInfo.BuyCount = count;ordersInfo.BuyerNo = buyerUser;ordersInfo.GoodsNo = this.State.GoodsNo;ordersInfo.InTime = DateTime.Now;using (var context = EntityContext.Factory()){context.OrdersInfo.Add(ordersInfo);await context.SaveChangesAsync();}await this.WriteStateAsync();Console.WriteLine("購(gòu)買(mǎi)完成");return await Task.FromResult(true);}else{Console.WriteLine("庫(kù)存不足--剩余庫(kù)存:" + this.State.Stock);return await Task.FromResult(false);}}}我們有10種商品所以也就是會(huì)有10個(gè)Grain的實(shí)例保存在服務(wù)端,具體哪個(gè)Grain的實(shí)例代碼那種商品我們可以根據(jù)商品編號(hào)來(lái)劃分,GoodsInfoGrain繼承自IGoodsInfoGrain,IGoodsInfoGrain繼承自IGrainWithStringKey,IGrainWithStringKey的實(shí)例化需要一個(gè)string類型的key,我們就用商品的編號(hào)作為這個(gè)Grain實(shí)例的Key
這里我指定此Grain的StorageProvider為GoodsStorgeProvider,那么當(dāng)Grain被實(shí)例化的時(shí)候GoodsStorgeProvider也被實(shí)例化并且執(zhí)行ReadStateAsync,那么這個(gè)商品就在服務(wù)端存在了,不用每次去數(shù)據(jù)庫(kù)讀而是一直存在服務(wù)端
?
這里我們服務(wù)端是不需要特意人為的進(jìn)行排隊(duì)處理,Grain的實(shí)例我們可以理解為是線程安全的(微軟并不是使用線程鎖來(lái)做的這樣做太浪費(fèi)資源,有興趣的鞋童可以研究下源碼,這對(duì)你編程水平的提高很有作用)所以不會(huì)出現(xiàn)對(duì)象被同時(shí)調(diào)用,而是順序調(diào)用。
客戶端調(diào)用:
var grain = GrainClient.GrainFactory.GetGrain<IGoodsInfoGrain>(goods.GoodsNo);bool result = grain.BuyGoods(count, buyerUser).Result;if (result){Addmsg(buyerUser + "--購(gòu)買(mǎi)商品" + goods.GoodsName + " " + count + "個(gè)");}else{Addmsg(buyerUser + "--購(gòu)買(mǎi)商品" + goods.GoodsName + " 庫(kù)存不足");}大家可以看到,GrainClient.GrainFactory.GetGrain<IGoodsInfoGrain>(goods.GoodsNo)就是告訴服務(wù)端需要用哪個(gè)grain執(zhí)行我的操作,然后使用這個(gè)grain去調(diào)用BuyGoods方法購(gòu)買(mǎi)商品不需要告訴服務(wù)端商品的編號(hào),只需要買(mǎi)幾個(gè),購(gòu)買(mǎi)人是誰(shuí)就可以了,因?yàn)間rain在實(shí)例化(當(dāng)然還是那句話,Grain是有狀態(tài)的不需要每次實(shí)例化,)時(shí)就已經(jīng)定了它是哪種商品。
?
?
OK,源碼地址:https://github.com/zhuqingbo/Orleans.Samples
?今天舉例的這個(gè)場(chǎng)景是有破綻的,例如:有20萬(wàn)人都是來(lái)買(mǎi)一種商品的,那么就意味著只有一個(gè)服務(wù)器忙到死,但是其他的服務(wù)器都是空閑的,就像我商場(chǎng)雇了100個(gè)銷售人員,只有一個(gè)人在賣東西其他銷售都沒(méi)事,顧客要排隊(duì)很久…………這個(gè)是不允許出現(xiàn)的!!!我們應(yīng)該怎么解決?這個(gè)解決辦法我會(huì)在下次的事例中和大家分享,大家不妨在留言中提出一些自己的解決辦法,我們一起研究研究
?
轉(zhuǎn)載于:https://www.cnblogs.com/joab/p/5657851.html
總結(jié)
以上是生活随笔為你收集整理的Orleans初战(用分布式解决高并发购物场景)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 军校提前批截止到几号?
- 下一篇: 世界征服者4古德里安升到元帅要多少勋章