您现在的位置是:亿华云 > 应用开发

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

亿华云2025-10-02 09:03:02【应用开发】2人已围观

简介朋友们,你们好!近来,我听到了大量的关于新出的 .NET Core 和其性能的讨论,尤其在 Web 服务方面的讨论更甚。因为是新出的,我不想立马就比较两个不同的东西,所以我耐心等待,想等发布更稳定的版

 

朋友们,性雄你们好!

近来,争雌我听到了大量的性雄关于新出的 .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。安装很简单,仅仅只需要打开终端,然后执行如下语句:

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 请求方法的 返回“值”。

源代码:

using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace netcore_mvc {      public class Program     {          public static void Main(string[] args)         {              BuildWebHost(args).Run();         }         public static IWebHost BuildWebHost(string[] args) =>             WebHost.CreateDefaultBuilder(args)                 .UseStartup<Startup>()                 .Build();     } }  using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace netcore_mvc {      public class Startup     {          public Startup(IConfiguration configuration)         {              Configuration = configuration;         }         public IConfiguration Configuration {  get; }         // This method gets called by the runtime. Use this method to add services to the container.         public void ConfigureServices(IServiceCollection services)         {              services.AddMvcCore();         }         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.         public void Configure(IApplicationBuilder app, IHostingEnvironment env)         {              app.UseMvc();         }     } }  using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; namespace netcore_mvc.Controllers {      // ValuesController is the equivalent     // `ValuesController` of the Iris 8.3 mvc application.     [Route("api/[controller]")]     public class ValuesController : Controller     {          // Get handles "GET" requests to "api/values/{ id}".         [HttpGet("{ id}")]         public string Get(int id)         {              return "value";         }         // Put handles "PUT" requests to "api/values/{ id}".         [HttpPut("{ id}")]         public void Put(int id, [FromBody]string value)         {          }         // Delete handles "DELETE" requests to "api/values/{ id}".         [HttpDelete("{ id}")]         public void Delete(int id)         {          }     } } 

运行 .NET Core web 服务项目:

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

运行和定位 HTTP 基准工具:

$ bombardier -c 125 -n 5000000 http://localhost:5000/api/values/5 Bombarding http://localhost:5000/api/values/5 with 5000000 requests using 125 connections  5000000 / 5000000 [=====================================================] 100.00% 2m3s Done! Statistics        Avg      Stdev        Max   Reqs/sec     40226.03    8724.30     161919   Latency        3.09ms     1.40ms   169.12ms   HTTP codes:     1xx - 0, 2xx - 5000000, 3xx - 0, 4xx - 0, 5xx - 0     others - 0   Throughput:     8.91MB/s 

Iris MVC

Logo 由 Santosh Anand 设计。

源代码:

package main import (     "github.com/kataras/iris"     "github.com/kataras/iris/_benchmarks/iris-mvc/controllers" ) func main() {      app := iris.New()     app.Controller("/api/values/{ id}", new(controllers.ValuesController))     app.Run(iris.Addr(":5000"), iris.WithoutVersionChecker) }  package controllers import "github.com/kataras/iris/mvc" // ValuesController is the equivalent // `ValuesController` of the .net core 2.0 mvc application. type ValuesController struct {      mvc.Controller } // Get handles "GET" requests to "api/values/{ id}". func (vc *ValuesController) Get() {      // id,_ := vc.Params.GetInt("id")     vc.Ctx.WriteString("value") } // Put handles "PUT" requests to "api/values/{ id}". func (vc *ValuesController) Put() { } // Delete handles "DELETE" requests to "api/values/{ id}". func (vc *ValuesController) Delete() { } 

运行 Go web 服务项目:

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

运行和定位 HTTP 基准工具:

$ bombardier -c 125 -n 5000000 http://localhost:5000/api/values/5 Bombarding http://localhost:5000/api/values/5 with 5000000 requests using 125 connections  5000000 / 5000000 [======================================================] 100.00% 47s Done! Statistics        Avg      Stdev        Max   Reqs/sec    105643.81    7687.79     122564   Latency        1.18ms   366.55us    22.01ms   HTTP codes:     1xx - 0, 2xx - 5000000, 3xx - 0, 4xx - 0, 5xx - 0     others - 0   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 使用的模板

using System; namespace netcore_mvc_templates.Models {      public class ErrorViewModel     {          public string Title {  get; set; }         public int Code {  get; set; }     } }   using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using netcore_mvc_templates.Models; namespace netcore_mvc_templates.Controllers {      public class HomeController : Controller     {          public IActionResult Index()         {              return View();         }         public IActionResult About()         {              ViewData["Message"] = "Your application description page.";             return View();         }         public IActionResult Contact()         {              ViewData["Message"] = "Your contact page.";             return View();         }         public IActionResult Error()         {              return View(new ErrorViewModel {  Title = "Error", Code = 500});         }     } }  using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace netcore_mvc_templates {      public class Program     {          public static void Main(string[] args)         {              BuildWebHost(args).Run();         }         public static IWebHost BuildWebHost(string[] args) =>             WebHost.CreateDefaultBuilder(args)                 .UseStartup<Startup>()                 .Build();     } }  using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace netcore_mvc_templates {      public class Startup     {          public Startup(IConfiguration configuration)         {              Configuration = configuration;         }         public IConfiguration Configuration {  get; }         // This method gets called by the runtime. Use this method to add services to the container.         public void ConfigureServices(IServiceCollection services)         {              /*  An unhandled exception was thrown by the application.                 System.InvalidOperationException: No service for type                 Microsoft.AspNetCore.Mvc.ViewFeatures.ITempDataDictionaryFactory has been registered.                 Solution: Use AddMvc() instead of AddMvcCore() in Startup.cs and it will work.             */             // services.AddMvcCore();             services.AddMvc();         }         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.         public void Configure(IApplicationBuilder app, IHostingEnvironment env)         {              app.UseStaticFiles();             app.UseMvc(routes =>             {                  routes.MapRoute(                     name: "default",                     template: "{ controller=Home}/{ action=Index}/{ id?}");             });         }     } }  /* wwwroot/css wwwroot/images wwwroot/js wwwroot/lib wwwroot/favicon.ico Views/Shared/_Layout.cshtml Views/Shared/Error.cshtml Views/Home/About.cshtml Views/Home/Contact.cshtml Views/Home/Index.cshtml These files are quite long to be shown in this article but you can view them at:  https://github.com/kataras/iris/tree/master/_benchmarks/netcore-mvc-templates 

运行 .NET Core 服务项目:

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

运行 HTTP 基准工具:

Bombarding http://localhost:5000 with 1000000 requests using 125 connections  1000000 / 1000000 [====================================================] 100.00% 1m20s Done! Statistics Avg Stdev Max  Reqs/sec 11738.60 7741.36 125887  Latency 10.10ms 22.10ms 1.97s  HTTP codes:  1xx — 0, 2xx — 1000000, 3xx — 0, 4xx — 0, 5xx — 0  others — 0  Throughput: 89.03MB/s 

Iris MVC 使用的模板

package controllers import "github.com/kataras/iris/mvc" type AboutController struct{  mvc.Controller } func (c *AboutController) Get() {      c.Data["Title"] = "About"     c.Data["Message"] = "Your application description page."     c.Tmpl = "about.html" }  package controllers import "github.com/kataras/iris/mvc" type ContactController struct{  mvc.Controller } func (c *ContactController) Get() {      c.Data["Title"] = "Contact"     c.Data["Message"] = "Your contact page."     c.Tmpl = "contact.html" }  package models // HTTPError a silly structure to keep our error page data. type HTTPError struct {      Title string     Code  int }  package controllers import "github.com/kataras/iris/mvc" type IndexController struct{  mvc.Controller } func (c *IndexController) Get() {      c.Data["Title"] = "Home Page"     c.Tmpl = "index.html" }  package main import (     "github.com/kataras/iris/_benchmarks/iris-mvc-templates/controllers"     "github.com/kataras/iris"     "github.com/kataras/iris/context" ) const (     // templatesDir is the exactly the same path that .NET Core is using for its templates,     // in order to reduce the size in the repository.     // Change the "C\\mygopath" to your own GOPATH.     templatesDir = "C:\\mygopath\\src\\github.com\\kataras\\iris\\_benchmarks\\netcore-mvc-templates\\wwwroot" ) func main() {      app := iris.New()     app.Configure(configure)     app.Controller("/", new(controllers.IndexController))     app.Controller("/about", new(controllers.AboutController))     app.Controller("/contact", new(controllers.ContactController))     app.Run(iris.Addr(":5000"), iris.WithoutVersionChecker) } func configure(app *iris.Application) {      app.RegisterView(iris.HTML("./views", ".html").Layout("shared/layout.html"))     app.StaticWeb("/public", templatesDir)     app.OnAnyErrorCode(onError) } type err struct {      Title string     Code  int } func onError(ctx context.Context) {      ctx.ViewData("", err{ "Error", ctx.GetStatusCode()})     ctx.View("shared/error.html") }  /* ../netcore-mvc-templates/wwwroot/css ../netcore-mvc-templates/wwwroot/images ../netcore-mvc-templates/wwwroot/js ../netcore-mvc-templates/wwwroot/lib ../netcore-mvc-templates/wwwroot/favicon.ico views/shared/layout.html views/shared/error.html views/about.html views/contact.html views/index.html These files are quite long to be shown in this article but you can view them at:  https://github.com/kataras/iris/tree/master/_benchmarks/iris-mvc-templates */ 

运行 Go 服务项目:

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

运行 HTTP 基准工具:

Bombarding http://localhost:5000 with 1000000 requests using 125 connections  1000000 / 1000000 [======================================================] 100.00% 37s Done! Statistics Avg Stdev Max  Reqs/sec 26656.76 1944.73 31188  Latency 4.69ms 1.20ms 22.52ms  HTTP codes:  1xx — 0, 2xx — 1000000, 3xx — 0, 4xx — 0, 5xx — 0  others — 0  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 之间的性能差异,它还包含一个会话存储管理基准! 

很赞哦!(98)