首先我们来定义这样一个场景

商店有10种商品,每种商品有100件库存。现在有20万人来抢购这些商品。

OK,那么问题来了。要怎样保证商品不会超卖……(要知道可能会出现20个人同时买A商品(或者更糟糕,毕竟后边20万的大军,随时可能把商店变成废墟),怎样保证A商品的数量绝对安全)

按照大部分系统的解决方案是这样的

  收到请求放入队列,然后对队列顺序处理,这样就避免了系统被瞬间挤爆而且不会超卖。

这种处理方式装换成现实场景是这样的:客户到商店先领号,不管买什么商品,都要排队,然后一个一个买,直到所有的处理完

这个是不是弱爆了………………

这个解决方案也就相当于一个售卖窗口,大家在那排队买,你能受得了吗?

 

先看看现实商店怎样解决的(存在即合理):客户太多就加窗口呗,多雇员工,粗暴又简单的解决了问题(当然大家还是要排队,但是不是一个队了,缓解了压力提高了速度哦,老板赚到了更多的钱)

Orleans闪亮登场…………

首先我要多开几台服务器来处理客户的请求,怎样分配呢,要知道我的商品库存数量必须保证安全,如果几台服务器操作一个商品那我们要想办法做到对象的绝对同步(joab开始也是这样想的,后来我才知道是我想多了),要知道加的服务器处理数据同步的消耗实在太大得不偿失啊(线程之间的数据安全使用线程锁我们都闲消耗大,这个夸服务器就更别说了)……

换个思路:加几台服务器,每台服务器买不同的商品,例如:1号服务器卖a/b两种商品,2号服务器卖c/d两种商品…………以此类推,问题解决了……

客户消息说买a商品,直接到1号服务器排队,买c商品就去2号服务器排队,(当然这里服务器也要多线程,一样的解决原理,a商品x线程排队,b商品y线程排队)

好了,从场景到解决办法都出来了,现在要实现:

照例我们开始搭建环境(事例我就简单三层了,现实项目大家自己根据项目自己发挥啊)

访问关系:

Orleans.Samples.HostSilo就是个控制台应用程序,用于启动Orleans服务(Silo的启动)也就相当于售货的窗口,不同服务器启动Orleans.Samples.HostSilo来处理排队的请求(配置我就先不贴出来了,很多地方有)

Orleans.Samples.Grains你可以理解为商品,它在需要在窗口售卖

Orleans.Samples.StorageProvider这个怎么说呢,首先Orleans.Samples.Grains是运行在服务端的而且可以是有状态的,我们怎么来管理他的状态,StorageProvider就对Grain的状态做了扩展(本例我就那这个状态来做商品数据的读写,并且对商品扣库存时也是直接对本Grain的state进行操作)

其它的几个我就不讲了大家一看就知道是什么了。

关键代码

一、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();}}}

前边说过了Grain是有状态的,我定义了GoodsStorgeProvider管理商品的状态,商品的读取我是直接从数据库读出然后赋值个它的State,那么知道这个Grain被释放,这个State将一直存在,并且唯一,写入我就直接对商品的Stock进行了赋值并且保存到数据库(售卖商品,变更的就只有商品的数量)

二、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 + ":购买商品--" + this.State.GoodsName + "    " + count + "个");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("购买完成");return await Task.FromResult(true);}else{Console.WriteLine("库存不足--剩余库存:" + this.State.Stock);return await Task.FromResult(false);}}}

我们有10种商品所以也就是会有10个Grain的实例保存在服务端,具体哪个Grain的实例代码那种商品我们可以根据商品编号来划分,GoodsInfoGrain继承自IGoodsInfoGrain,IGoodsInfoGrain继承自IGrainWithStringKey,IGrainWithStringKey的实例化需要一个string类型的key,我们就用商品的编号作为这个Grain实例的Key

这里我指定此Grain的StorageProvider为GoodsStorgeProvider,那么当Grain被实例化的时候GoodsStorgeProvider也被实例化并且执行ReadStateAsync,那么这个商品就在服务端存在了,不用每次去数据库读而是一直存在服务端

这里我们服务端是不需要特意人为的进行排队处理,Grain的实例我们可以理解为是线程安全的(微软并不是使用线程锁来做的这样做太浪费资源,有兴趣的鞋童可以研究下源码,这对你编程水平的提高很有作用)所以不会出现对象被同时调用,而是顺序调用。

客户端调用:

       var grain = GrainClient.GrainFactory.GetGrain<IGoodsInfoGrain>(goods.GoodsNo);bool result = grain.BuyGoods(count, buyerUser).Result;if (result){Addmsg(buyerUser + "--购买商品" + goods.GoodsName + "    " + count + "个");}else{Addmsg(buyerUser + "--购买商品" + goods.GoodsName + "    库存不足");}

大家可以看到,GrainClient.GrainFactory.GetGrain<IGoodsInfoGrain>(goods.GoodsNo)就是告诉服务端需要用哪个grain执行我的操作,然后使用这个grain去调用BuyGoods方法购买商品不需要告诉服务端商品的编号,只需要买几个,购买人是谁就可以了,因为grain在实例化(当然还是那句话,Grain是有状态的不需要每次实例化,)时就已经定了它是哪种商品。

OK,源码地址:https://github.com/zhuqingbo/Orleans.Samples

今天举例的这个场景是有破绽的,例如:有20万人都是来买一种商品的,那么就意味着只有一个服务器忙到死,但是其他的服务器都是空闲的,就像我商场雇了100个销售人员,只有一个人在卖东西其他销售都没事,顾客要排队很久…………这个是不允许出现的!!!我们应该怎么解决?这个解决办法我会在下次的事例中和大家分享,大家不妨在留言中提出一些自己的解决办法,我们一起研究研究

转载于:https://www.cnblogs.com/joab/p/5657851.html

Orleans初战(用分布式解决高并发购物场景)相关推荐

  1. 每秒上千订单场景下的分布式锁高并发优化实践!

    本文授权转自石杉的架构笔记 背景引入 首先,我们一起来看看这个问题的背景? 前段时间有个朋友在外面面试,然后有一天找我聊说:有一个国内不错的电商公司,面试官给他出了一个场景题: 假如下单时,用分布式锁 ...

  2. 你分得清分布式、高并发与多线程吗?

    当提起这三个词的时候,是不是很多人都认为分布式=高并发=多线程? 当面试官问到高并发系统可以采用哪些手段来解决,或者被问到分布式系统如何解决一致性的问题,是不是一脸懵逼? 确实,在一开始接触的时候,不 ...

  3. 读数据库遇到空就进行不下去_如何解决高并发场景下缓存+数据库双写不一致问题?...

    推荐阅读: 一只Tom猫:手撕分布式技术:限流.通讯.缓存,全部一锅端走送给你!​zhuanlan.zhihu.com 一只Tom猫:MySQL复习:20道常见面试题(含答案)+21条MySQL性能调 ...

  4. 分布式、高并发、多线程,到底有什么区别?

    当提起这三个词的时候,是不是很多人都认为分布式=高并发=多线程? 当面试官问到高并发系统可以采用哪些手段来解决,或者被问到分布式系统如何解决一致性的问题,是不是一脸懵逼? 确实,在一开始接触的时候,不 ...

  5. 分布式机器学习_京东出来的java工程师,竟然不知道分布式、高并发、多线程的区别?...

    当提起这三个词的时候,是不是很多人都认为分布式=高并发=多线程? 当面试官问到高并发系统可以采用哪些手段来解决,或者被问到分布式系统如何解决一致性的问题,是不是一脸懵逼? 确实,在一开始接触的时候,不 ...

  6. PHP中如何解决高并发

    PHP中如何解决高并发 1:硬件方面 普通的一个p4的服务器每天最多能支持大约10万左右的IP,如果访问量超过10W那么需要专用的服务器才能解决,如果硬件不给力 软件怎么优化都是于事无补的.主要影响服 ...

  7. Java架构-每秒上千订单场景下的分布式锁高并发优化实践!

    "上一篇文章我们聊了聊Redisson这个开源框架对Redis分布式锁的实现原理,如果有不了解的兄弟可以看一下:<拜托,面试请不要再问我Redis分布式锁实现原理>. 今天就给大 ...

  8. 限流是解决高并发大流量的一种方案,至少是可以保证应用的可用性

    # 限流算法 推荐微信公众号:[矿洞程序员]文章由高端社区fameLink联合创始人陶德与我及其他社区大佬联合发表.关注[矿洞程序员]可获得大咖陶德的私人微信. 限流是解决高并发大流量的一种方案,至少 ...

  9. 分布式、高并发、高性能场景(抢购、秒杀、抢票、限时竞答)数据一致性解决方案...

    技术指标: PV(Page View, 页面浏览量)在千万级别 QPS(Query Per Second, 每秒处理请求数)在百万级别 数据量在千亿级别 接口响应速度不能超过150毫秒 用户提交请求到 ...

最新文章

  1. 系统架构师学习笔记_第五章(下)_连载
  2. java下拉框查询_[Java教程]jQuery实现联动下拉列表查询框
  3. flink的savepoint实验-scala
  4. 5行代码可实现5倍Scikit-Learn参数调整的更快速度
  5. 一个普通人,想改变命运,最靠谱的3种方式
  6. CF1399E1 Weights Division (easy version)
  7. 计算机网络试题及答案(史上最全)
  8. R语言|plot和par函数绘图详解,绘图区域设置 颜色设置 绘图后修改及图像输出
  9. join and list删除 and set集合 and 深浅拷贝
  10. Java matlab车牌识别,车牌识别matlab实现(蓝色车牌和新能源车牌)
  11. kodu_Kodu教您的孩子直观地编程自己的视频游戏
  12. caffe 损失函数
  13. 抓rtmp推流地址_在浏览器中实现RTMP推流
  14. 手机软件测试英语,手机软件测试,mobile phone software testing,音标,读音,翻译,英文例句,英语词典...
  15. Linux系统运行C语言编译的二进制文件报错:Segmentation fault
  16. python资讯_python学习-WEB资讯专栏-DMOZ中文网站分类目录-免费收录各类优秀网站的中文网站目录....
  17. Word美化代码块样式
  18. Stm32MP157-Linux(Ubuntu)——Ubuntu入门
  19. 一款纯粹的在线视频App,基于Material Design + MVP + RxJava + Retrofit + Realm
  20. “我们没有竞争对手”专访Splunk中国区总经理严立忠

热门文章

  1. SpringBoot+MySQL+MyBatis+Shiro+AdminLTE
  2. poj2253 Frogger dijkstra
  3. JS 时间转化为几分钟前 几小时前 几天前
  4. Java数字签名——RSA算法
  5. sell02 展现层编写
  6. MongoDB学习笔记(一:常见问题汇总)
  7. jQuery 遍历 - closest() 方法
  8. BZOJ 3223: Tyvj 1729 文艺平衡树(splay)
  9. 《OpenGL超级宝典第5版》学习笔记(一)—— 第一个OpenGL程序
  10. Linux 命令 查看监听端口