论 HTTP 性能,Go 与 .NET Core 一争雌雄

Linux大全评论862 views阅读模式

朋友们,你们好!

近来,我听到了大量的关于新出的 .NET Core 和其性能的讨论,尤其在 Web 服务方面的讨论更甚。

因为是新出的,我不想立马就比较两个不同的东西,所以我耐心等待,想等发布更稳定的版本后再进行。

本周一(8 月 14 日),微软发布 .NET Core 2.0 版本,因此,我准备开始。您们认为呢?

如前面所提的,我们会比较它们相同的东西,比如应用程序、预期响应及运行时的稳定性,所以我们不会把像对 JSON 或者 XML 的编码、解码这些烦多的事情加入比较游戏中来,仅仅只会使用简单的文本消息。为了公平起见,我们会分别使用 Go 和 .NET Core 的 MVC 架构模式。

 

参赛选手

Go (或称 Golang): 是一种快速增长的开源编程语言,旨在构建出简单、快捷和稳定可靠的应用软件。

用于支持 Go 语言的 MVC web 框架并不多,还好我们找到了 Iris ,可胜任此工作。

Iris: 支持 Go 语言的快速、简单和高效的微型 Web 框架。它为您的下一代网站、API 或分布式应用程序奠定了精美的表现方式和易于使用的基础。

C#: 是一种通用的、面向对象的编程语言。其开发团队由 Anders Hejlsberg 领导。

.NET Core: 跨平台,可以在极少时间内开发出高性能的应用程序。

可从 https://golang.org/dl 下载 Go ,从 https://www.microsoft.com/net/core 下载 .NET Core。

在下载和安装好这些软件后,还需要为 Go 安装 Iris。安装很简单,仅仅只需要打开终端,然后执行如下语句:

  1. go get-u github.com/kataras/iris

 

基准

 

硬件

  • 处理器: Intel(R) Core(TM) i7–4710HQ CPU @ 2.50GHz 2.50GHz
  • 内存: 8.00 GB

 

软件

  • 操作系统: 微软 Windows [10.0.15063 版本], 电源计划设置为“高性能”
  • HTTP 基准工具: https://github.com/codesenberg/bombardier, 使用最新的 1.1 版本。
  • .NET Core: https://www.microsoft.com/net/core, 使用最新的 2.0 版本。
  • Iris: https://github.com/kataras/iris, 使用基于 Go 1.8.3 构建的最新 8.3 版本。

两个应用程序都通过请求路径 “api/values/{id}” 返回文本“值”。

 

.NET Core MVC

Logo 由 Pablo Iglesias 设计。

可以使用 dotnet new webapi 命令创建项目,其 webapi 模板会为您生成代码,代码包含 GET 请求方法的 返回“值”

源代码:

  1. usingSystem;
  2. usingSystem.Collections.Generic;
  3. usingSystem.IO;
  4. usingSystem.Linq;
  5. usingSystem.Threading.Tasks;
  6. usingMicrosoft.AspNetCore;
  7. usingMicrosoft.AspNetCore.Hosting;
  8. usingMicrosoft.Extensions.Configuration;
  9. usingMicrosoft.Extensions.Logging;
  10. namespace netcore_mvc
  11. {
  12. publicclassProgram
  13. {
  14. publicstaticvoidMain(string[] args)
  15. {
  16. BuildWebHost(args).Run();
  17. }
  18. publicstaticIWebHostBuildWebHost(string[] args)=>
  19. WebHost.CreateDefaultBuilder(args)
  20. .UseStartup<Startup>()
  21. .Build();
  22. }
  23. }
  1. usingSystem;
  2. usingSystem.Collections.Generic;
  3. usingSystem.Linq;
  4. usingSystem.Threading.Tasks;
  5. usingMicrosoft.AspNetCore.Builder;
  6. usingMicrosoft.AspNetCore.Hosting;
  7. usingMicrosoft.Extensions.Configuration;
  8. usingMicrosoft.Extensions.DependencyInjection;
  9. usingMicrosoft.Extensions.Logging;
  10. usingMicrosoft.Extensions.Options;
  11. namespace netcore_mvc
  12. {
  13. publicclassStartup
  14. {
  15. publicStartup(IConfiguration configuration)
  16. {
  17. Configuration= configuration;
  18. }
  19. publicIConfigurationConfiguration{get;}
  20. // This method gets called by the runtime. Use this method to add services to the container.
  21. publicvoidConfigureServices(IServiceCollection services)
  22. {
  23. services.AddMvcCore();
  24. }
  25. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  26. publicvoidConfigure(IApplicationBuilder app,IHostingEnvironmentenv)
  27. {
  28. app.UseMvc();
  29. }
  30. }
  31. }
  1. usingSystem;
  2. usingSystem.Collections.Generic;
  3. usingSystem.Linq;
  4. usingSystem.Threading.Tasks;
  5. usingMicrosoft.AspNetCore.Mvc;
  6. namespace netcore_mvc.Controllers
  7. {
  8. // ValuesController is the equivalent
  9. // `ValuesController` of the Iris 8.3 mvc application.
  10. [Route("api/[controller]")]
  11. publicclassValuesController:Controller
  12. {
  13. // Get handles "GET" requests to "api/values/{id}".
  14. [HttpGet("{id}")]
  15. public string Get(intid)
  16. {
  17. return"value";
  18. }
  19. // Put handles "PUT" requests to "api/values/{id}".
  20. [HttpPut("{id}")]
  21. publicvoidPut(intid,[FromBody]string value)
  22. {
  23. }
  24. // Delete handles "DELETE" requests to "api/values/{id}".
  25. [HttpDelete("{id}")]
  26. publicvoidDelete(intid)
  27. {
  28. }
  29. }
  30. }

运行 .NET Core web 服务项目:

  1. $ cd netcore-mvc
  2. $ dotnet run -c Release
  3. Hosting environment:Production
  4. Content root path: C:\mygopath\src\github.com\kataras\iris\_benchmarks\netcore-mvc
  5. Now listening on: http://localhost:5000
  6. Application started.PressCtrl+C to shut down.

运行和定位 HTTP 基准工具:

  1. $ bombardier -c 125-n 5000000 http://localhost:5000/api/values/5
  2. Bombarding http://localhost:5000/api/values/5 with 5000000 requests using 125 connections
  3. 5000000/5000000[=====================================================]100.00%2m3s
  4. Done!
  5. StatisticsAvgStdevMax
  6. Reqs/sec 40226.038724.30161919
  7. Latency3.09ms1.40ms169.12ms
  8. HTTP codes:
  9. 1xx-0,2xx-5000000,3xx-0,4xx-0,5xx-0
  10. others -0
  11. Throughput:8.91MB/s

 

Iris MVC

Logo 由 Santosh Anand 设计。

源代码:

  1. package main
  2. import(
  3. "github.com/kataras/iris"
  4. "github.com/kataras/iris/_benchmarks/iris-mvc/controllers"
  5. )
  6. func main(){
  7. app := iris.New()
  8. app.Controller("/api/values/{id}",new(controllers.ValuesController))
  9. app.Run(iris.Addr(":5000"), iris.WithoutVersionChecker)
  10. }
  1. package controllers
  2. import"github.com/kataras/iris/mvc"
  3. // ValuesController is the equivalent
  4. // `ValuesController` of the .net core 2.0 mvc application.
  5. type ValuesControllerstruct{
  6. mvc.Controller
  7. }
  8. // Get handles "GET" requests to "api/values/{id}".
  9. func (vc *ValuesController)Get(){
  10. // id,_ := vc.Params.GetInt("id")
  11. vc.Ctx.WriteString("value")
  12. }
  13. // Put handles "PUT" requests to "api/values/{id}".
  14. func (vc *ValuesController)Put(){}
  15. // Delete handles "DELETE" requests to "api/values/{id}".
  16. func (vc *ValuesController)Delete(){}

运行 Go web 服务项目:

  1. $ cd iris-mvc
  2. $ go run main.go
  3. Now listening on: http://localhost:5000
  4. Application started.Press CTRL+C to shut down.

运行和定位 HTTP 基准工具:

  1. $ bombardier -c 125-n 5000000 http://localhost:5000/api/values/5
  2. Bombarding http://localhost:5000/api/values/5 with 5000000 requests using 125 connections
  3. 5000000/5000000[======================================================]100.00%47s
  4. Done!
  5. StatisticsAvgStdevMax
  6. Reqs/sec 105643.817687.79122564
  7. Latency1.18ms366.55us22.01ms
  8. HTTP codes:
  9. 1xx-0,2xx-5000000,3xx-0,4xx-0,5xx-0
  10. others -0
  11. Throughput:19.65MB/s

想通过图片来理解的人,我也把我的屏幕截屏出来了!

请点击这儿可以看到这些屏幕快照。

 

总结

  • 完成 5000000 个请求的时间 - 越短越好。
  • 请求次数/每秒 - 越大越好。
  • 等待时间 — 越短越好。
  • 吞吐量 — 越大越好。
  • 内存使用 — 越小越好。
  • LOC (代码行数) — 越少越好。

.NET Core MVC 应用程序,使用 86 行代码,运行 2 分钟 8 秒,每秒接纳 39311.56 个请求,平均 3.19ms 等待,最大时到 229.73ms,内存使用大约为 126MB(不包括 dotnet 框架)。

Iris MVC 应用程序,使用 27 行代码,运行 47 秒,每秒接纳 105643.71 个请求,平均 1.18ms 等待,最大时到 22.01ms,内存使用大约为 12MB。

还有另外一个模板的基准,滚动到底部。

2017 年 8 月 20 号更新

Josh Clark 和 Scott Hanselman在此 tweet 评论上指出,.NET Core Startup.cs 文件中 services.AddMvc(); 这行可以替换为 services.AddMvcCore();。我听从他们的意见,修改代码,重新运行基准,该文章的 .NET Core 应用程序的基准输出已经修改。

@topdawgevh @shanselman 他们也在使用 AddMvc() 而不是 AddMvcCore() ...,难道都不包含中间件?

 —  @clarkis117

@clarkis117 @topdawgevh Cool @MakisMaropoulos @benaadams @davidfowl 我们来看看。认真学习下怎么使用更简单的性能默认值。

 —  @shanselman

@shanselman @clarkis117 @topdawgevh @benaadams @davidfowl @shanselman @benaadams @davidfowl 谢谢您们的反馈意见。我已经修改,更新了结果,没什么不同。对其它的建议,我非常欢迎。

 —  @MakisMaropoulos

它有点稍微的不同但相差不大(从 8.61MB/s 到 8.91MB/s)

想要了解跟 services.AddMvc() 标准比较结果的,可以点击这儿。

 

想再多了解点儿吗?

我们再制定一个基准,产生 1000000 次请求,这次会通过视图引擎由模板生成 HTML 页面。

 

.NET Core MVC 使用的模板

  1. usingSystem;
  2. namespace netcore_mvc_templates.Models
  3. {
  4. publicclassErrorViewModel
  5. {
  6. public string Title{get;set;}
  7. publicintCode{get;set;}
  8. }
  9. }
  1. usingSystem;
  2. usingSystem.Collections.Generic;
  3. usingSystem.Diagnostics;
  4. usingSystem.Linq;
  5. usingSystem.Threading.Tasks;
  6. usingMicrosoft.AspNetCore.Mvc;
  7. using netcore_mvc_templates.Models;
  8. namespace netcore_mvc_templates.Controllers
  9. {
  10. publicclassHomeController:Controller
  11. {
  12. publicIActionResultIndex()
  13. {
  14. returnView();
  15. }
  16. publicIActionResultAbout()
  17. {
  18. ViewData["Message"]="Your application description page.";
  19. returnView();
  20. }
  21. publicIActionResultContact()
  22. {
  23. ViewData["Message"]="Your contact page.";
  24. returnView();
  25. }
  26. publicIActionResultError()
  27. {
  28. returnView(newErrorViewModel{Title="Error",Code=500});
  29. }
  30. }
  31. }
  1. usingSystem;
  2. usingSystem.Collections.Generic;
  3. usingSystem.IO;
  4. usingSystem.Linq;
  5. usingSystem.Threading.Tasks;
  6. usingMicrosoft.AspNetCore;
  7. usingMicrosoft.AspNetCore.Hosting;
  8. usingMicrosoft.Extensions.Configuration;
  9. usingMicrosoft.Extensions.Logging;
  10. namespace netcore_mvc_templates
  11. {
  12. publicclassProgram
  13. {
  14. publicstaticvoidMain(string[] args)
  15. {
  16. BuildWebHost(args).Run();
  17. }
  18. publicstaticIWebHostBuildWebHost(string[] args)=>
  19. WebHost.CreateDefaultBuilder(args)
  20. .UseStartup<Startup>()
  21. .Build();
  22. }
  23. }
  1. usingSystem;
  2. usingSystem.Collections.Generic;
  3. usingSystem.Linq;
  4. usingSystem.Threading.Tasks;
  5. usingMicrosoft.AspNetCore.Builder;
  6. usingMicrosoft.AspNetCore.Hosting;
  7. usingMicrosoft.Extensions.Configuration;
  8. usingMicrosoft.Extensions.DependencyInjection;
  9. namespace netcore_mvc_templates
  10. {
  11. publicclassStartup
  12. {
  13. publicStartup(IConfiguration configuration)
  14. {
  15. Configuration= configuration;
  16. }
  17. publicIConfigurationConfiguration{get;}
  18. // This method gets called by the runtime. Use this method to add services to the container.
  19. publicvoidConfigureServices(IServiceCollection services)
  20. {
  21. /* An unhandled exception was thrown by the application.
  22. System.InvalidOperationException: No service for type
  23. 'Microsoft.AspNetCore.Mvc.ViewFeatures.ITempDataDictionaryFactory' has been registered.
  24. Solution: Use AddMvc() instead of AddMvcCore() in Startup.cs and it will work.
  25. */
  26. // services.AddMvcCore();
  27. services.AddMvc();
  28. }
  29. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  30. publicvoidConfigure(IApplicationBuilder app,IHostingEnvironmentenv)
  31. {
  32. app.UseStaticFiles();
  33. app.UseMvc(routes =>
  34. {
  35. routes.MapRoute(
  36. name:"default",
  37. template:"{controller=Home}/{action=Index}/{id?}");
  38. });
  39. }
  40. }
  41. }
  1. /*
  2. wwwroot/css
  3. wwwroot/images
  4. wwwroot/js
  5. wwwroot/lib
  6. wwwroot/favicon.ico
  7. Views/Shared/_Layout.cshtml
  8. Views/Shared/Error.cshtml
  9. Views/Home/About.cshtml
  10. Views/Home/Contact.cshtml
  11. Views/Home/Index.cshtml
  12. These files are quite long to be shown in this article but you can view them at:
  13. https://github.com/kataras/iris/tree/master/_benchmarks/netcore-mvc-templates

运行 .NET Core 服务项目:

  1. $ cd netcore-mvc-templates
  2. $ dotnet run -c Release
  3. Hosting environment:Production
  4. Content root path: C:\mygopath\src\github.com\kataras\iris\_benchmarks\netcore-mvc-templates
  5. Now listening on: http://localhost:5000
  6. Application started.PressCtrl+C to shut down.

运行 HTTP 基准工具:

  1. Bombarding http://localhost:5000 with 1000000 requests using 125 connections
  2. 1000000/1000000[====================================================]100.00%1m20s
  3. Done!
  4. StatisticsAvgStdevMax
  5. Reqs/sec 11738.607741.36125887
  6. Latency10.10ms22.10ms1.97s
  7. HTTP codes:
  8. 1xx0,2xx1000000,3xx0,4xx0,5xx0
  9. others 0
  10. Throughput:89.03MB/s

 

Iris MVC 使用的模板

  1. package controllers
  2. import"github.com/kataras/iris/mvc"
  3. type AboutControllerstruct{ mvc.Controller}
  4. func (c *AboutController)Get(){
  5. c.Data["Title"]="About"
  6. c.Data["Message"]="Your application description page."
  7. c.Tmpl="about.html"
  8. }
  1. package controllers
  2. import"github.com/kataras/iris/mvc"
  3. type ContactControllerstruct{ mvc.Controller}
  4. func (c *ContactController)Get(){
  5. c.Data["Title"]="Contact"
  6. c.Data["Message"]="Your contact page."
  7. c.Tmpl="contact.html"
  8. }
  1. package models
  2. // HTTPError a silly structure to keep our error page data.
  3. type HTTPErrorstruct{
  4. Title string
  5. Codeint
  6. }
  1. package controllers
  2. import"github.com/kataras/iris/mvc"
  3. type IndexControllerstruct{ mvc.Controller}
  4. func (c *IndexController)Get(){
  5. c.Data["Title"]="Home Page"
  6. c.Tmpl="index.html"
  7. }
  1. package main
  2. import(
  3. "github.com/kataras/iris/_benchmarks/iris-mvc-templates/controllers"
  4. "github.com/kataras/iris"
  5. "github.com/kataras/iris/context"
  6. )
  7. const(
  8. // templatesDir is the exactly the same path that .NET Core is using for its templates,
  9. // in order to reduce the size in the repository.
  10. // Change the "C\\mygopath" to your own GOPATH.
  11. templatesDir ="C:\\mygopath\\src\\github.com\\kataras\\iris\\_benchmarks\\netcore-mvc-templates\\wwwroot"
  12. )
  13. func main(){
  14. app := iris.New()
  15. app.Configure(configure)
  16. app.Controller("/",new(controllers.IndexController))
  17. app.Controller("/about",new(controllers.AboutController))
  18. app.Controller("/contact",new(controllers.ContactController))
  19. app.Run(iris.Addr(":5000"), iris.WithoutVersionChecker)
  20. }
  21. func configure(app *iris.Application){
  22. app.RegisterView(iris.HTML("./views",".html").Layout("shared/layout.html"))
  23. app.StaticWeb("/public", templatesDir)
  24. app.OnAnyErrorCode(onError)
  25. }
  26. type err struct{
  27. Title string
  28. Codeint
  29. }
  30. func onError(ctx context.Context){
  31. ctx.ViewData("", err{"Error", ctx.GetStatusCode()})
  32. ctx.View("shared/error.html")
  33. }
  1. /*
  2. ../netcore-mvc-templates/wwwroot/css
  3. ../netcore-mvc-templates/wwwroot/images
  4. ../netcore-mvc-templates/wwwroot/js
  5. ../netcore-mvc-templates/wwwroot/lib
  6. ../netcore-mvc-templates/wwwroot/favicon.ico
  7. views/shared/layout.html
  8. views/shared/error.html
  9. views/about.html
  10. views/contact.html
  11. views/index.html
  12. These files are quite long to be shown in this article but you can view them at:
  13. https://github.com/kataras/iris/tree/master/_benchmarks/iris-mvc-templates
  14. */

运行 Go 服务项目:

  1. $ cd iris-mvc-templates
  2. $ go run main.go
  3. Now listening on: http://localhost:5000
  4. Application started.Press CTRL+C to shut down.

运行 HTTP 基准工具:

  1. Bombarding http://localhost:5000 with 1000000 requests using 125 connections
  2. 1000000/1000000[======================================================]100.00%37s
  3. Done!
  4. StatisticsAvgStdevMax
  5. Reqs/sec 26656.761944.7331188
  6. Latency4.69ms1.20ms22.52ms
  7. HTTP codes:
  8. 1xx0,2xx1000000,3xx0,4xx0,5xx0
  9. others 0
  10. Throughput:192.51MB/s

 

总结

  • 完成 1000000 个请求的时间 - 越短越好。
  • 请求次数/每秒 - 越大越好。
  • 等待时间 — 越短越好。
  • 内存使用 — 越小越好。
  • 吞吐量 — 越大越好。

.NET Core MVC 模板应用程序,运行 1 分钟 20 秒,每秒接纳 11738.60 个请求,同时每秒生成 89.03M 页面,平均 10.10ms 等待,最大时到 1.97s,内存使用大约为 193MB(不包括 dotnet 框架)。

Iris MVC 模板应用程序,运行 37 秒,每秒接纳 26656.76 个请求,同时每秒生成 192.51M 页面,平均 1.18ms 等待,最大时到 22.52ms,内存使用大约为 17MB。

 

接下来呢?

这里有上面所示的源代码,请下载下来,在您本地以同样的基准运行,然后把运行结果在这儿给大家分享。

想添加 Go 或 C# .net core WEB 服务框架到列表的朋友请向这个仓库的 _benchmarks 目录推送 PR。

我也需要亲自感谢下 dev.to 团队,感谢把我的这篇文章分享到他们的 Twitter 账户。

感谢大家真心反馈,玩得开心!

 

更新 : 2017 年 8 月 21 ,周一

很多人联系我,希望看到一个基于 .NET Core 的较低级别 Kestrel 的基准测试文章。

因此我完成了,请点击下面的链接来了解 Kestrel 和 Iris 之间的性能差异,它还包含一个会话存储管理基准!

via: https://hackernoon.com/go-vs-net-core-in-terms-of-http-performance-7535a61b67b8

作者:Gerasimos Maropoulos 译者:runningwater 校对:wxy

企鹅博客
  • 本文由 发表于 2020年8月15日 02:21:38
  • 转载请务必保留本文链接:https://www.qieseo.com/169159.html

发表评论