.net异步性能测试(包括ASP.NET MVC WebAPI异步方法)

发表于:2017-9-28 16:45

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:cooling2016    来源:51Testing软件测试网采编

  首先,建一个 ASP.NET MVC WebAPI项目,在默认的控制器 values里面,增加两个方法:
   // GET api/values?sleepTime=10
          [HttpGet]
          public async Task<string> ExecuteAIO(int sleepTime)
          {
              await Task.Delay(sleepTime);
              return  "Hello world,"+ sleepTime;
          }
          [HttpGet]
          // GET api/values?sleepTime2=10
          public string ExecuteBIO(int sleepTime2)
          {
              System.Threading.Thread.Sleep(sleepTime2);
              return "Hello world," + sleepTime2;
          }
  然后,建立一个控制台程序,来测试这个web API:
   class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine("按任意键开始测试 WebAPI:http://localhost:62219/api/values?sleepTime={int}");
              Console.Write("请输入线程数:");
              int threadNum = 100;
              int.TryParse(Console.ReadLine(), out threadNum);
              while (Test(threadNum)) ;
              Console.ReadLine();
              Console.ReadLine();
          }
          private static bool Test(int TaskNumber)
          {
              Console.Write("请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:");
              string input = Console.ReadLine();
              int SleepTime = 50;
              if (!int.TryParse(input, out SleepTime))
                  return false;
              HttpClient client = new HttpClient();
              client.BaseAddress = new Uri("http://localhost:62219/");
              var result = client.GetStringAsync("api/values?sleepTime=" + input).Result;
              Console.WriteLine("Result:{0}", result);
              //int TaskNumber = 1000;
              Console.WriteLine("{0}次 BIO(同步)测试(睡眠{1} 毫秒):", TaskNumber, SleepTime);
              System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
              sw.Start();
              Task[] taskArr = new Task[TaskNumber];
              for (int i = 0; i < TaskNumber; i++)
              {
                  Task task = client.GetStringAsync("api/values?sleepTime2=" + SleepTime);
                  taskArr[i] = task;
              }
              Task.WaitAll(taskArr);
              sw.Stop();
              double useTime1 = sw.Elapsed.TotalSeconds;
              Console.WriteLine("耗时(秒):{0},QPS:{1,10:f2}", useTime1, TaskNumber/useTime1);
              sw.Reset();
              Console.WriteLine("{0}次 AIO(异步)测试(睡眠{1} 毫秒):", TaskNumber, SleepTime);
              sw.Start();
              for (int i = 0; i < TaskNumber; i++)
              {
                  Task task = client.GetStringAsync("api/values?sleepTime=" + SleepTime);
                  taskArr[i] = task;
              }
              Task.WaitAll(taskArr);
              sw.Stop();
              double useTime2 = sw.Elapsed.TotalSeconds;
              Console.WriteLine("耗时(秒):{0},QPS:{1,10:f2}", useTime2, TaskNumber / useTime2);
              return true;
          }
      }
  其实主要是下面几行代码:
  HttpClient client = new HttpClient();
  client.BaseAddress = new Uri("http://localhost:62219/");
  var result = client.GetStringAsync("api/values?sleepTime=" + input).Result;
  注意,你可能需要使用Nuget添加下面这个包:
  Microsoft.AspNet.WebApi.Client
  最后,运行这个测试,结果如下:
  按任意键开始测试 WebAPI:http://localhost:62219/api/values?sleepTime={int}
  请输入线程数:1000
  请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:10
  Result:"Hello world,10"
  1000次 BIO(同步)测试(睡眠10 毫秒):
  耗时(秒):1.2860545,QPS:    777.57
  1000次 AIO(异步)测试(睡眠10 毫秒):
  耗时(秒):0.4895946,QPS:   2042.51
  请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:100
  Result:"Hello world,100"
  1000次 BIO(同步)测试(睡眠100 毫秒):
  耗时(秒):8.2769307,QPS:    120.82
  1000次 AIO(异步)测试(睡眠100 毫秒):
  耗时(秒):0.5435111,QPS:   1839.89
  本来想尝试测试10000个线程,但报错了。
  上面的测试结果,QPS并不高,但由于使用的是IISExpress,不同的Web服务器软件性能不相同,所以还得对比下进程内QPS结果,于是新建一个控制台程序,代码如下:
   class Program
      {
          static void Main(string[] args)
          {
              Console.WriteLine("按任意键开始测试 ");
              Console.Write("请输入线程数:");
              int threadNum = 100;
              int.TryParse(Console.ReadLine(), out threadNum);
              while (Test(threadNum)) ;
              Console.ReadLine();
              Console.ReadLine();
          }
          private static bool Test(int TaskNumber)
          {
              Console.Write("请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:");
              string input = Console.ReadLine();
              int SleepTime = 50;
              if (!int.TryParse(input, out SleepTime))
                  return false;
              var result = ExecuteAIO(SleepTime).Result;
              Console.WriteLine("Result:{0}", result);
              //int TaskNumber = 1000;
              Console.WriteLine("{0}次 BIO(同步)测试(睡眠{1} 毫秒):", TaskNumber, SleepTime);
              System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
              sw.Start();
              Task[] taskArr = new Task[TaskNumber];
              for (int i = 0; i < TaskNumber; i++)
              {
                  Task task = Task.Run<string>(()=> ExecuteBIO(SleepTime));
                  taskArr[i] = task;
              }
              Task.WaitAll(taskArr);
              sw.Stop();
              double useTime1 = sw.Elapsed.TotalSeconds;
              Console.WriteLine("耗时(秒):{0},QPS:{1,10:f2}", useTime1, TaskNumber / useTime1);
              sw.Reset();
              Console.WriteLine("{0}次 AIO(异步)测试(睡眠{1} 毫秒):", TaskNumber, SleepTime);
              sw.Start();
              for (int i = 0; i < TaskNumber; i++)
              {
                  Task task = ExecuteAIO(SleepTime);
                  taskArr[i] = task;
              }
              Task.WaitAll(taskArr);
              sw.Stop();
              double useTime2 = sw.Elapsed.TotalSeconds;
              Console.WriteLine("耗时(秒):{0},QPS:{1,10:f2}", useTime2, TaskNumber / useTime2);
              return true;
          }
          public static async Task<string> ExecuteAIO(int sleepTime)
          {
              await Task.Delay(sleepTime);
              return "Hello world," + sleepTime;
          }
          public static string ExecuteBIO(int sleepTime2)
          {
              System.Threading.Thread.Sleep(sleepTime2);
              //不能在非异步方法里面使用 Task.Delay,否则可能死锁
              //Task.Delay(sleepTime2).Wait();
              return "Hello world," + sleepTime2;
          }
      }
  注意,关键代码只有下面两个方法:
   public static async Task<string> ExecuteAIO(int sleepTime)
          {
              await Task.Delay(sleepTime);
              return "Hello world," + sleepTime;
          }
          public static string ExecuteBIO(int sleepTime2)
          {
              System.Threading.Thread.Sleep(sleepTime2);
              //不能在非异步方法里面使用 Task.Delay,否则可能死锁
              //Task.Delay(sleepTime2).Wait();
              return "Hello world," + sleepTime2;
          }
  这两个方法跟WebAPI的测试方法代码是一样的,但是调用代码稍微不同:
  同步调用:
   Task[] taskArr = new Task[TaskNumber];
              for (int i = 0; i < TaskNumber; i++)
              {
                  Task task = Task.Run<string>(()=> ExecuteBIO(SleepTime));
                  taskArr[i] = task;
              }
              Task.WaitAll(taskArr);
  异步调用:
    for (int i = 0; i < TaskNumber; i++)
              {
                  Task task = ExecuteAIO(SleepTime);
                  taskArr[i] = task;
              }
              Task.WaitAll(taskArr);
  可见,这里测试的时候,同步和异步调用,客户端代码都是使用的多线程,主要的区别就是异步方法使用了 async/await 语句。
  下面是非Web的进程内异步多线程和同步多线程的结果:
    请输入线程数:1000
  请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:10
  Result:Hello world,10
  1000次 BIO(同步)测试(睡眠10 毫秒):
  耗时(秒):1.3031966,QPS:    767.34
  1000次 AIO(异步)测试(睡眠10 毫秒):
  耗时(秒):0.026441,QPS:  37820.05
  请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:100
  Result:Hello world,100
  1000次 BIO(同步)测试(睡眠100 毫秒):
  耗时(秒):9.8502858,QPS:    101.52
  1000次 AIO(异步)测试(睡眠100 毫秒):
  耗时(秒):0.1149469,QPS:   8699.67
  请输入线程数:10000
  请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:10
  Result:Hello world,10
  10000次 BIO(同步)测试(睡眠10 毫秒):
  耗时(秒):7.7966125,QPS:   1282.61
  10000次 AIO(异步)测试(睡眠10 毫秒):
  耗时(秒):0.083922,QPS: 119158.27
  请输入此API方法的睡眠时间(毫秒),输入非数字内容退出:100
  Result:Hello world,100
  10000次 BIO(同步)测试(睡眠100 毫秒):
  耗时(秒):34.3646036,QPS:    291.00
  10000次 AIO(异步)测试(睡眠100 毫秒):
  耗时(秒):0.1721833,QPS:  58077.64
  结果表示,.NET程序开启10000个任务(不是10000个原生线程,需要考虑线程池线程),异步方法的QPS超过了10万,而同步方法只有1000多点,性能差距还是很大的。
  注:以上测试结果的测试环境是 
  Intel i7-4790K CPU,4核8线程,内存 16GB,Win10 企业版
  总结:
  不论是普通程序还是Web程序,使用异步多线程,可以极大的提高系统的吞吐量。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号