编译自 | https://hackernoon.com/go-vs-net-core-in-terms-of-http-performance-7535a61b67b8
作者 | Gerasimos Maropoulos
译者 | runningwater
朋友们,你们好!
近来,我听到了大量的关于新出的 .NET Core 和其性能的讨论,尤其在 Web 服务方面的讨论更甚。
因为是新出的,我不想立马就比较两个不同的东西,所以我耐心等待,想等发布更稳定的版本后再进行。
本周一(8 月 14 日),微软发布 .NET Core 2.0 版本[1],因此,我准备开始。您们认为呢?
如前面所提的,我们会比较它们相同的东西,比如应用程序、预期响应及运行时的稳定性,所以我们不会把像对 JSON 或者 XML 的编码、解码这些烦多的事情加入比较游戏中来,仅仅只会使用简单的文本消息。为了公平起见,我们会分别使用 Go 和 .NET Core 的 MVC 架构模式[2]。
参赛选手
Go[3](或称 Golang): 是一种快速增长[4]的开源编程语言,旨在构建出简单、快捷和稳定可靠的应用软件。
用于支持 Go 语言的 MVC web 框架并不多,还好我们找到了 Iris ,可胜任此工作。
Iris[5]: 支持 Go 语言的快速、简单和高效的微型 Web 框架。它为您的下一代网站、API 或分布式应用程序奠定了精美的表现方式和易于使用的基础。
C#[6]: 是一种通用的、面向对象的编程语言。其开发团队由 Anders Hejlsberg[7]领导。
.NET Core[8]: 跨平台,可以在极少时间内开发出高性能的应用程序。
可从 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[3]构建的最新 8.3 版本。
两个应用程序都通过请求路径 “api/values/{id}” 返回文本“值”。
.NET Core MVC
Logo 由 Pablo Iglesias[13]设计。
可以使用 dotnet new webapi 命令创建项目,其 webapi 模板会为您生成代码,代码包含 GET 请求方法的 返回“值”。
源代码:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.IO;
usingSystem.Linq;
usingSystem.Threading.Tasks;
usingMicrosoft.AspNetCore;
usingMicrosoft.AspNetCore.Hosting;
usingMicrosoft.Extensions.Configuration;
usingMicrosoft.Extensions.Logging;
namespacenetcore_mvc
{
publicclassProgram
{
publicstaticvoidMain(string[]args)
{
BuildWebHost(args).Run();
}
publicstaticIWebHostBuildWebHost(string[]args)=>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Threading.Tasks;
usingMicrosoft.AspNetCore.Builder;
usingMicrosoft.AspNetCore.Hosting;
usingMicrosoft.Extensions.Configuration;
usingMicrosoft.Extensions.DependencyInjection;
usingMicrosoft.Extensions.Logging;
usingMicrosoft.Extensions.Options;
namespacenetcore_mvc
{
publicclassStartup
{
publicStartup(IConfigurationconfiguration)
{
Configuration=configuration;
}
publicIConfigurationConfiguration{get;}
// This method gets called by the runtime. Use this method to add services to the container.
publicvoidConfigureServices(IServiceCollectionservices)
{
services.AddMvcCore();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
publicvoidConfigure(IApplicationBuilderapp,IHostingEnvironmentenv)
{
app.UseMvc();
}
}
}
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Threading.Tasks;
usingMicrosoft.AspNetCore.Mvc;
namespacenetcore_mvc.Controllers
{
// ValuesController is the equivalent
// `ValuesController` of the Iris 8.3 mvc application.
[Route("api/[controller]")]
publicclassValuesController:Controller
{
// Get handles "GET" requests to "api/values/{id}".
[HttpGet("{id}")]
publicstring Get(intid)
{
return"value";
}
// Put handles "PUT" requests to "api/values/{id}".
[HttpPut("{id}")]
publicvoidPut(intid,[FromBody]string value)
{
}
// Delete handles "DELETE" requests to "api/values/{id}".
[HttpDelete("{id}")]
publicvoidDelete(intid)
{
}
}
}
运行 .NET Core web 服务项目:
$ cdnetcore-mvc
$ dotnet run -c Release
Hostingenvironment:Production
Contentroot path:C:mygopathsrcgithub.comkatarasiris_benchmarksnetcore-mvc
Nowlistening on:http://localhost:5000
Applicationstarted.PressCtrl+C to shut down.
运行和定位 HTTP 基准工具:
$ bombardier -c 125-n 5000000http://localhost:5000/api/values/5
Bombardinghttp://localhost:5000/api/values/5 with 5000000 requests using 125 connections
5000000/5000000[=====================================================]100.00%2m3s
Done!
StatisticsAvgStdevMax
Reqs/sec 40226.038724.30161919
Latency3.09ms1.40ms169.12ms
HTTP codes:
1xx-0,2xx-5000000,3xx-0,4xx-0,5xx-0
others -0
Throughput:8.91MB/s
Iris MVC
Logo 由 Santosh Anand设计。
源代码:
packagemain
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)
}
packagecontrollers
import"github.com/kataras/iris/mvc"
// ValuesController is the equivalent
// `ValuesController` of the .net core 2.0 mvc application.
type ValuesControllerstruct{
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 服务项目:
$ cdiris-mvc
$ go run main.go
Nowlistening on:http://localhost:5000
Applicationstarted.PressCTRL+C to shut down.
运行和定位 HTTP 基准工具:
$ bombardier -c 125-n 5000000http://localhost:5000/api/values/5
Bombardinghttp://localhost:5000/api/values/5 with 5000000 requests using 125 connections
5000000/5000000[======================================================]100.00%47s
Done!
StatisticsAvgStdevMax
Reqs/sec 105643.817687.79122564
Latency1.18ms366.55us22.01ms
HTTP codes:
1xx-0,2xx-5000000,3xx-0,4xx-0,5xx-0
others -0
Throughput:19.65MB/s
想通过图片来理解的人,我也把我的屏幕截屏出来了!
请点击这儿[15]可以看到这些屏幕快照。
总结
◈ 完成 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[16]和 Scott Hanselman[17]在此 tweet 评论[18]上指出,.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)
它有点稍微的不同但相差不大(从 8.61MB/s 到 8.91MB/s)
想要了解跟 services.AddMvc() 标准比较结果的,可以点击这儿[19]。
想再多了解点儿吗?
我们再制定一个基准,产生 1000000 次请求,这次会通过视图引擎由模板生成 HTML 页面。
.NET Core MVC 使用的模板
usingSystem;
namespacenetcore_mvc_templates.Models
{
publicclassErrorViewModel
{
publicstring Title{get;set;}
publicintCode{get;set;}
}
}
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Diagnostics;
usingSystem.Linq;
usingSystem.Threading.Tasks;
usingMicrosoft.AspNetCore.Mvc;
usingnetcore_mvc_templates.Models;
namespacenetcore_mvc_templates.Controllers
{
publicclassHomeController:Controller
{
publicIActionResultIndex()
{
returnView();
}
publicIActionResultAbout()
{
ViewData["Message"]="Your application deion page.";
returnView();
}
publicIActionResultContact()
{
ViewData["Message"]="Your contact page.";
returnView();
}
publicIActionResultError()
{
returnView(newErrorViewModel{Title="Error",Code=500});
}
}
}
usingSystem;
usingSystem.Collections.Generic;
usingSystem.IO;
usingSystem.Linq;
usingSystem.Threading.Tasks;
usingMicrosoft.AspNetCore;
usingMicrosoft.AspNetCore.Hosting;
usingMicrosoft.Extensions.Configuration;
usingMicrosoft.Extensions.Logging;
namespacenetcore_mvc_templates
{
publicclassProgram
{
publicstaticvoidMain(string[]args)
{
BuildWebHost(args).Run();
}
publicstaticIWebHostBuildWebHost(string[]args)=>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Threading.Tasks;
usingMicrosoft.AspNetCore.Builder;
usingMicrosoft.AspNetCore.Hosting;
usingMicrosoft.Extensions.Configuration;
usingMicrosoft.Extensions.DependencyInjection;
namespacenetcore_mvc_templates
{
publicclassStartup
{
publicStartup(IConfigurationconfiguration)
{
Configuration=configuration;
}
publicIConfigurationConfiguration{get;}
// This method gets called by the runtime. Use this method to add services to the container.
publicvoidConfigureServices(IServiceCollectionservices)
{
/* 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.
publicvoidConfigure(IApplicationBuilderapp,IHostingEnvironmentenv)
{
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 服务项目:
$ cdnetcore-mvc-templates
$ dotnet run -c Release
Hostingenvironment:Production
Contentroot path:C:mygopathsrcgithub.comkatarasiris_benchmarksnetcore-mvc-templates
Nowlistening on:http://localhost:5000
Applicationstarted.PressCtrl+C to shut down.
运行 HTTP 基准工具:
Bombardinghttp://localhost:5000 with 1000000 requests using 125 connections
1000000/1000000[====================================================]100.00%1m20s
Done!
StatisticsAvgStdevMax
Reqs/sec 11738.607741.36125887
Latency10.10ms22.10ms1.97s
HTTP codes:
1xx—0,2xx—1000000,3xx—0,4xx—0,5xx—0
others —0
Throughput:89.03MB/s
Iris MVC 使用的模板
packagecontrollers
import"github.com/kataras/iris/mvc"
type AboutControllerstruct{mvc.Controller}
func (c *AboutController)Get(){
c.Data["Title"]="About"
c.Data["Message"]="Your application deion page."
c.Tmpl="about.html"
}
packagecontrollers
import"github.com/kataras/iris/mvc"
type ContactControllerstruct{mvc.Controller}
func (c *ContactController)Get(){
c.Data["Title"]="Contact"
c.Data["Message"]="Your contact page."
c.Tmpl="contact.html"
}
packagemodels
// HTTPError a silly structure to keep our error page data.
type HTTPErrorstruct{
Titlestring
Codeint
}
packagecontrollers
import"github.com/kataras/iris/mvc"
type IndexControllerstruct{mvc.Controller}
func (c *IndexController)Get(){
c.Data["Title"]="Home Page"
c.Tmpl="index.html"
}
packagemain
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 "Cmygopath" to your own GOPATH.
templatesDir ="C:mygopathsrcgithub.comkatarasiris_benchmarksnetcore-mvc-templateswwwroot"
)
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()
}
type err struct{
Titlestring
Codeint
}
func (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 服务项目:
$ cdiris-mvc-templates
$ go run main.go
Nowlistening on:http://localhost:5000
Applicationstarted.PressCTRL+C to shut down.
运行 HTTP 基准工具:
Bombardinghttp://localhost:5000 with 1000000 requests using 125 connections
1000000/1000000[======================================================]100.00%37s
Done!
StatisticsAvgStdevMax
Reqs/sec 26656.761944.7331188
Latency4.69ms1.20ms22.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。
接下来呢?
这里[20]有上面所示的源代码,请下载下来,在您本地以同样的基准运行,然后把运行结果在这儿给大家分享。
想添加 Go 或 C# .net core WEB 服务框架到列表的朋友请向这个仓库[12]的 _benchmarks 目录推送 PR。
我也需要亲自感谢下 dev.to[21]团队,感谢把我的这篇文章分享到他们的 Twitter 账户。
感谢大家真心反馈,玩得开心!
更新 : 2017 年 8 月 21 ,周一
很多人联系我,希望看到一个基于 .NET Core 的较低级别 Kestrel 的基准测试文章。
因此我完成了,请点击下面的链接[22]来了解 Kestrel 和 Iris 之间的性能差异,它还包含一个会话存储管理基准!
via: https://hackernoon.com/go-vs-net-core-in-terms-of-http-performance-7535a61b67b8
本文由 LCTT原创编译,Linux中国
runningwater
51篇