国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - ASP.NET教程 - asp.net通過消息隊列處理高并發請求(以搶小米手機為例)

asp.net通過消息隊列處理高并發請求(以搶小米手機為例)

2020-06-28 14:40chenxizhaolu ASP.NET教程

這篇文章主要介紹了asp.net通過消息隊列處理高并發請求(以搶小米手機為例),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

網站面對高并發的情況下,除了增加硬件, 優化程序提高以響應速度外,還可以通過并行改串行的思路來解決。這種思想常見的實踐方式就是數據庫鎖和消息隊列的方式。這種方式的缺點是需要排隊,響應速度慢,優點是節省成本。

演示一下現象

創建一個在售產品表

?
1
2
3
4
5
6
CREATE TABLE [dbo].[product](
  [id] [int] NOT NULL,--唯一主鍵
  [name] [nvarchar](50) NULL,--產品名稱
  [status] [int] NULL ,--0未售出 1 售出 默認為0
  [username] [nvarchar](50) NULL--下單用戶
 )

添加一條記錄

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

創建一個搶票程序

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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程序是多線程的,那我們把他在容易出現并發的地方加一把鎖就可以了,如下圖處理方式。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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,創建訂單提交入口(生產者)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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,創建訂單處理者(消費者)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/// <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,創建訂單業務的實際執行者

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/// <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,在程序啟動前,啟動消費者線程

?
1
2
3
4
5
6
7
8
9
10
11
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,創建生產者并且在程序啟動前調用其初始化程序

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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,創建消費者

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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,當然還需要一個業務的實際執行者

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/// <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 消息隊列處理高并發 內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://www.cnblogs.com/chenxizhaolu/p/12543376.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产在线精品一区二区三区 | 一呦二呦三呦国产精品 | 国产精品99久久免费观看 | 中文字幕永久第一页 | 国产精品久久久久久久 | 天天干天天操 | 日韩a∨精品日韩在线观看 国产高清视频在线观看 | 黄色综合| 九色91九色porny永久 | 色婷婷综合久久久中文字幕 | 国产一区二区高清在线 | 欧美日韩中文在线观看 | 黄色视屏免费在线观看 | 亚洲一区中文字幕在线观看 | 精品一区二区在线观看 | 在线免费观看激情视频 | 亚洲精品福利在线 | 在线你懂得 | 成人一区二区三区久久精品嫩草 | 国产精品日韩精品 | 国产精品三级久久久久久电影 | 精久久 | 国内久久精品 | 91麻豆精品国产91久久久更新资源速度超快 | 色狠狠综合天天综合综合 | 亚洲人人看 | 伊人伊人伊人 | 国产精品久久久久久久久久久久久久 | 久久久久久久久久久久久av | 色综合天天综合网国产成人网 | 国产精品美女久久久久久不卡 | 黄色av网站免费看 | 欧美在线观看一区 | 亚洲国产精品99久久久久久久久 | av成人一区二区 | 黄色tv在线观看 | 日本视频免费高清一本18 | 成人影音 | 精品亚洲一区二区三区 | 亚洲精品久久久久久久久久久 | 欧美色欧美亚洲另类七区 |