asp.net通过消息队列处理高并发请求(以抢小米手机为例)_实用技巧

来源:脚本之家  责任编辑:小易  

可以通过酒店内部人员通过执法部门www.zgxue.com防采集请勿采集本网。

网站面对高并发的情况下,除了增加硬件, 优化程序提高以响应速度外,还可以通过并行改串行的思路来解决。这种思想常见的实践方式就是数据库锁和消息队列的方式。这种方式的缺点是需要排队,响应速度慢,优点是节省成本。

演示一下现象

创建一个在售产品表

CREATE TABLE [dbo].[product]( [id] [int] NOT NULL,--唯一主键 [name] [nvarchar](50) NULL,--产品名称 [status] [int] NULL ,--0未售出 1 售出 默认为0 [username] [nvarchar](50) NULL--下单用户 )

添加一条记录

insert into product(id,name,status,username) values(1,'小米手机',0,null)

创建一个抢票程序

public ContentResult PlaceOrder(string userName) { using (RuanMou2020Entities db = new RuanMou2020Entities()) { var product = db.product.Where<product>(p => p.status== 0).FirstOrDefault(); if (product.status == 1) { return Content("失败,产品已经被卖光"); } else { //模拟数据库慢造成并发问题 Thread.Sleep(5000); product.status = 1; product.username= userName;              db.SaveChanges();               return Content("成功购买");             }       }     }

如果我们在5秒内一次访问以下两个地址,那么返回的结果都是成功购买且数据表中的username是lisi。

/controller/PlaceOrder?username=zhangsan

/controller/PlaceOrder?username=lisi

这就是并发带来的问题。

第一阶段,利用线程锁简单粗暴

Web程序是多线程的,那我们把他在容易出现并发的地方加一把锁就可以了,如下图处理方式。

private static object _lock = new object(); public ContentResult PlaceOrder(string userName) { using (RuanMou2020Entities db = new RuanMou2020Entities()) { lock (_lock) { var product = db.product.Where<product>(p => p.status == 0).FirstOrDefault(); if (product.status == 1) { return Content("失败,产品已经被卖光"); } else { //模拟数据库慢造成并发问题 Thread.Sleep(5000); product.status = 1; product.username = userName; db.SaveChanges(); return Content("成功购买"); } } } }

这样每一个请求都是依次执行,不会出现并发问题了。

优点:解决了并发的问题。

缺点:效率太慢,用户体验性太差,不适合大数据量场景。

第二阶段,拉消息队列,通过生产者,消费者的模式

1,创建订单提交入口(生产者)

public class HomeController : Controller { /// <summary> /// 接受订单提交(生产者) /// </summary> /// <returns></returns> public ContentResult PlaceOrderQueen(string userName) { //直接将请求写入到订单队列 OrderConsumer.TicketOrders.Enqueue(userName); return Content("wait"); } /// <summary> /// 查询订单结果 /// </summary> /// <returns></returns> public ContentResult PlaceOrderQueenResult(string userName) { var rel = OrderConsumer.OrderResults.Where(p => p.userName == userName).FirstOrDefault(); if (rel == null) { return Content("还在排队中"); } else { return Content(rel.Result.ToString()); } }}

2,创建订单处理者(消费者)

/// <summary> /// 订单的处理者(消费者) /// </summary> public class OrderConsumer { /// <summary> /// 订票的消息队列 /// </summary> public static ConcurrentQueue<string> TicketOrders = new ConcurrentQueue<string>(); /// <summary> /// 订单结果消息队列 /// </summary> public static List<OrderResult> OrderResults = new List<OrderResult>(); /// <summary> /// 订单处理 /// </summary> public static void StartTicketTask() { string userName = null; while (true) { //如果没有订单任务就休息1秒钟 if (!TicketOrders.TryDequeue(out userName)) { Thread.Sleep(1000); continue; } //执行真实的业务逻辑(如插入数据库) bool rel = new TicketHelper().PlaceOrderDataBase(userName); //将执行结果写入结果集合 OrderResults.Add(new OrderResult() { Result = rel, userName = userName }); } } }

3,创建订单业务的实际执行者

/// <summary> /// 订单业务的实际处理者 /// </summary> public class TicketHelper { /// <summary> /// 实际库存标识 /// </summary> private bool hasStock = true; /// <summary> /// 执行一个订单到数据库 /// </summary> /// <returns></returns> public bool PlaceOrderDataBase(string userName) { //如果没有了库存,则直接返回false,防止频繁读库 if (!hasStock) { return hasStock; } using (RuanMou2020Entities db = new RuanMou2020Entities()) { var product = db.product.Where(p => p.status == 0).FirstOrDefault(); if (product == null) { hasStock = false; return false; } else { Thread.Sleep(10000);//模拟数据库的效率比较慢,执行插入时间比较久 product.status = 1; product.username = userName; db.SaveChanges(); return true; } } } } /// <summary> /// 订单处理结果实体 /// </summary> public class OrderResult { public string userName { get; set; } public bool Result { get; set; } }

4,在程序启动前,启动消费者线程

protected void Application_Start() { AreaRegistration.RegisterAllAreas(); GlobalConfiguration.Configure(WebApiConfig.Register); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); //在Global的Application_Start事件里单独开启一个消费者线程 Task.Run(OrderConsumer.StartTicketTask); }

这样程序的运行模式是:用户提交的需求里都会添加到消息队列里去排队处理,程序会依次处理该队列里的内容(当然可以一次取出多条来进行处理,提高效率)。

优点:比上一步快了。

缺点:不够快,而且下单后需要轮询另外一个接口判断是否成功。

第三阶段 反转生产者消费者的角色,把可售产品提前放到队列里,然后让提交的订单来消费队列里的内容

1,创建生产者并且在程序启动前调用其初始化程序

public class ProductForSaleManager { /// <summary> /// 待售商品队列 /// </summary> public static ConcurrentQueue<int> ProductsForSale = new ConcurrentQueue<int>(); /// <summary> /// 初始化待售商品队列 /// </summary> public static void Init() { using (RuanMou2020Entities db = new RuanMou2020Entities()) { db.product.Where(p => p.status == 0).Select(p => p.id).ToList().ForEach(p => { ProductsForSale.Enqueue(p); }); } } } public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); GlobalConfiguration.Configure(WebApiConfig.Register); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); //程序启动前,先初始化待售产品消息队列 ProductForSaleManager.Init(); } }

2,创建消费者

public class OrderController : Controller { /// <summary> /// 下订单 /// </summary> /// <param name="userName">订单提交者</param> /// <returns></returns> public async Task<ContentResult> PlaceOrder(string userName) { if (ProductForSaleManager.ProductsForSale.TryDequeue(out int pid)) { await new TicketHelper2().PlaceOrderDataBase(userName, pid); return Content($"下单成功,对应产品id为:{pid}"); } else { await Task.CompletedTask; return Content($"商品已经被抢光"); } } }

3,当然还需要一个业务的实际执行者

/// <summary> /// 订单业务的实际处理者 /// </summary> public class TicketHelper2 { /// <summary> /// 执行复杂的订单操作(如数据库) /// </summary> /// <param name="userName">下单用户</param> /// <param name="pid">产品id</param> /// <returns></returns> public async Task PlaceOrderDataBase(string userName, int pid) { using (RuanMou2020Entities db = new RuanMou2020Entities()) { var product = db.product.Where(p => p.id == pid).FirstOrDefault(); if (product != null) { product.status = 1; product.username = userName; await db.SaveChangesAsync(); } } } }

这样我们同时访问下面三个地址,如果数据库里只有两个商品的话,会有一个请求结果为:商品已经被抢光。

http://localhost:88/Order/PlaceOrder?userName=zhangsan

http://localhost:88/Order/PlaceOrder?userName=lisi

http://localhost:88/Order/PlaceOrder?userName=wangwu

这种处理方式的优点为:执行效率快,相比第二种方式不需要第二个接口来返回查询结果。

缺点:暂时没想到,欢迎大家补充。

说明:该方式只是个人猜想,并非实际项目经验,大家只能作为参考,慎重用于项目。欢迎大家批评指正。

到此这篇关于asp.net通过消息队列处理高并发请求(以抢小米手机为例)的文章就介绍到这了,更多相关asp.net 消息队列处理高并发 内容请搜索真格学网以前的文章或继续浏览下面的相关文章希望大家以后多多支持真格学网!

゛?破゛?解゛?密゛?码゛?查゛?询゛?记゛?录゛?这゛?个゛?可゛?以゛?向゛?他゛?了゛?解゛.゛?他?゛? ゛??゛:▄4 8?4 6 9?8 6?9 1.▄゛?技 ゛?数゛? 好 ゛ ? 訫゛ ?誉゛ ?高゛ ゛找 ?゛他?゛ 放 ?゛心?゛好处是:清热去火,防止感冒。将橘子皮切成丝晾干作枕芯用,有顺气、降压的功效,对高血压病人很适用。舒适,有助于更快入睡.枕头不宜过高,、放个橘子在旁边,可以帮助深度睡眠,以清火,明目,镇静,安神,淡淡的桔香味更让人心旷神怡。指导意见:用晾干后的橘子皮做成橘皮枕对于治疗高血压病、失眠等,制作时还可加入银花、菊花、玫瑰花、灯芯草等。希望对您有所帮助。1、巧防晕车 在上车前1小时,用新鲜的橘子皮,向内折成双层,对准鼻孔,用手指挤提捏橘子皮,皮中就会喷射出无数股细小的橘香油雾并被吸入鼻孔。在上车后继续随时挤压吸入,可有效地预防晕车。2、巧治冻疮 将橘皮用火烤焦,研成粉末,再用植物油调均匀,抹在患处。3、巧治慢性支气管炎 橘皮5至15克,泡水当茶饮,常用。4、巧治咳嗽 用干橘皮5克,加水2杯煎汤后,放少量姜末、红糖趁热服用:也可取鲜橘皮适量,切碎后用开水冲泡,加入白糖代茶饮,有化痰止咳之功效。5、巧治便秘 鲜橘皮12克或干橘皮6克,煎汤服用,可治便秘。6、巧解酒 用鲜橘皮30克,加盐少许煎汤饮服,醒酒效果颇佳。7、巧治睡觉磨牙 睡觉前10分钟,口中含一块橘皮,然后入睡,最好不要将橘皮吐出,若感到不适时,再吐出。8、巧防止牙齿“酸倒”食酸橘对老人或牙齿过敏者均不宜。其实,只要在食酸橘后,即用剩下的新鲜橘皮泡开水喝下,就可以防止牙齿“酸倒”。9、巧治乳腺炎 生橘皮30克、甘草6克,煎汤饮服,可治乳腺炎。10、巧治口臭 将一小块橘皮含在口中,或嚼一小块鲜橘皮,可治口臭。11、巧解鱼蟹之毒 用适量的橘子皮煎汤饮服,可缓解食鱼、食蟹后的中毒。12、巧治胃寒呕吐 将橘皮和生姜片加水同煎,饮其汤,可治疗胃寒、呕吐。13、巧理气消胀 用鲜橘子皮泡开水,加适量白糖,为橘皮茶,饮后可理气消胀,生津润喉。14、巧治消化不良 将50克橘皮浸泡在酒里。这种酒有温补脾胃的功效,用于消化不良、反胃呕吐等症,对多食油腻而引起的消化不良、不思饮食症,尤为有效。15、巧清肺化痰 将橘子皮洗净后置于白酒中,浸泡20余天即可饮用,其味醇厚爽口,且有清肺化痰的作用。若浸泡的时间再长一点,至春节或开春后再饮,则味道更佳。16、巧治风寒感冒 鲜橘皮、生姜片,加红糖适量煎水喝,可治疗风寒、感冒、呕吐、咳嗽。17、巧提神开胃 将橘皮洗净切成丝后晒干,与茶叶放在一起存放,饮用时,用开水冲服,其味清香可口,有开胃、通气、提神的功效。18、巧治胰腺炎 用橘子皮30克、甘草10克和水共煎当茶饮。有助于治疗急性胰腺炎。19、巧降血压 将橘子皮切成丝晾干作枕芯用,有顺气、降压的功效,对高血压病人很适用。20、巧治脚沙虫 脚趾间被污水杂渍,易发生奇痒,若搔抓,则破皮流水,臭味难闻,此时可用鲜橘子皮猛擦痒处,止痒效果甚佳。植物激素是植物体内合成的对植物生长发育有显著作用的几类微量有机物质。也被成为植物天然激素或植物内源激素。植物激素有五类,即生长素(Auxin)、赤霉素(GA)、细胞分裂素(CTK)、脱落酸(ABA)和乙烯(ethyne,ETH)。它们都是些简单的小分子有机化合物,但它们的生理效应却非常复杂、多样。例如从影响细胞的分裂、伸长、分化到影响植物发芽、生根、开花、结实、性别的决定、休眠和脱落等。所以,植物激素对植物的生长发育有重要的调节控制作用。植物激素的化学结构已为人所知,有的已可以人工合成,如吲哚乙酸;有的还不能人工合成,如赤霉素。目前市场上售出的赤霉素试剂是从赤霉菌的培养过滤物中制取的。这些外加于植物的吲哚乙酸和赤霉素,与植物体自身产生的吲哚乙酸和赤霉素在来源上有所不同,所以作为植物生长调节剂,也有称为外源植物激素。最近新确认的植物激素有,茉莉酸(酯)等等植物体内产生的植物激素有赤霉素、激动素、脱落酸等。现已能人工合成某些类似植物激素作用的物质如2,4-D(2,4-二氯苯酚代乙酚)等。植物自身产生的、运往其他部位后能调节植物生长发育的微量有机物质。人工合成的具有植物激素活性的物质称为生长调节剂。已知的植物激素主要有以下 5类:生长素、赤霉素、细胞分裂素、脱落酸和乙烯。生长素 C.D.达尔文在1880年研究植物向性运动时,只有各种激素的协调配合,发现植物幼嫩的尖端受单侧光照射后产生的一种影响,能传到茎的伸长区引起弯曲。1928年荷兰F.W.温特从燕麦胚芽鞘尖端分离出一种具生理活性的物质,称为生长素,它正是引起胚芽鞘伸长的物质。1934年荷兰F.克格尔等从人尿得到生长素的结晶,经鉴定为吲哚乙酸。促进>橡胶树漆树等排出乳汁。在植物中,则吲哚乙酸通过酶促反应从色氨酸合成。十字花科植物中合成吲哚乙酸的前体为吲哚乙腈,西葫芦中有相当多的吲哚乙醇,也可转变为吲哚乙酸。已合成的生长素又可被植物体内的酶或外界的光所分解,因而处于不断的合成与分解之中。生长素在低等和高等植物中普遍存在。并使细胞膜的透性增加,在高等植物体内,乙烯可以促进RNA和蛋白质的合成,生长素主要集中在幼嫩、正生长的部位,如禾谷类的胚芽鞘,它的产生具有“自促作用”,双子叶植物的茎顶端、幼叶、花粉和子房以及正在生长的果实、种子等;衰老器官中含量极少。用胚芽鞘切段证明植物体内的生长素通常只能从植物的上端向下端运输,而不能相反。这种运输方式称为极性运输,能以远快于扩散的速度进行。但从外部施用的生长素类药剂的运输方向则随施用部位和浓度而定,如根部吸收的生长素可随蒸腾流上升到地上幼嫩部位。低浓度的生长素有促进器官伸长的作用。从而可减少蒸腾失水。超过最适浓度时由于会导致乙烯产生,生长的促进作用下降,甚至反会转为抑制。不同器官对生长素的反应不同,根最敏感,芽次之,茎的敏感性最差。种子中较高的脱落酸含量是种子休眠的主要原因。生长素能促进细胞伸长的主要原因,在于它能使细胞壁环境酸化、水解酶的活性增加,从而使细胞壁的结构松弛、可塑性增加,有利于细胞体积增大。因此是一种生长抑制剂,生长素还能促进 RNA和蛋白质的合成,促进细胞的分裂与分化。它的作用在于抑制 RNA和蛋白质的合成,对于维持顶端优势、促进果实发育,通常在衰老的器官或组织中的含量比在幼嫩部分中的多。生长素也有重要作用。脱落酸存在于植物的叶、休眠芽、成熟种子中。吲哚乙酸可以人工合成。生产上使用的是人工合成的类似生长素的物质如吲哚丙酸、吲哚丁酸、萘乙酸、2,4-滴、4-碘苯氧乙酸等,可用于防止脱落、促进单性结实、疏花疏果、插条生根、防止马铃薯发芽等方面。愈伤组织容易生芽;反之容易生根。2,在组织培养中当它们的含量大于生长素时,4-滴曾被用做选择性除草剂。细胞分裂素还可促进芽的分化。赤霉素 1926年日本黑泽在水稻恶苗病的研究中,发现感病稻苗的徒长和黄化现象与赤霉菌(Gibberellafujikuroi)有关。1938年薮田和住木从赤霉菌的分泌物中分离出了有生理活性的物质,定名为赤霉素(GA)。从50年代开始,英、美的科学工作者对赤霉素进行了研究,现已从赤霉菌和高等植物中分离出60多种赤霉素,分别被命名为GA1,GA2等。以后从植物中发现有十多种细胞分裂素,赤霉素广泛存在于菌类、藻类、蕨类、裸子植物及被子植物中。商品生产的赤霉素是GA3、GA4和GA7。GA3又称赤霉酸,是最早分离、鉴定出来的赤霉素,分子式为C19H22O6。即6-呋喃氨基嘌呤。高等植物中的赤霉素主要存在于幼根、幼叶、幼嫩