TranslateProject/published/201710/20170818 Go vs .NET Core in terms of HTTP performance.md
2017-11-01 12:48:48 +08:00

703 lines
20 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

论 HTTP 性能Go 与 .NET Core 一争雌雄
============================================================
![](https://cdn-images-1.medium.com/max/1200/1*60T60jSMO5yxUqJQmLZa4A.png)
朋友们,你们好!
近来,我听到了大量的关于新出的 .NET Core 和其性能的讨论,尤其在 Web 服务方面的讨论更甚。
因为是新出的,我不想立马就比较两个不同的东西,所以我耐心等待,想等发布更稳定的版本后再进行。
本周一8 月 14 日),微软[发布 .NET Core 2.0 版本][7],因此,我准备开始。您们认为呢?
如前面所提的,我们会比较它们相同的东西,比如应用程序、预期响应及运行时的稳定性,所以我们不会把像对 JSON 或者 XML 的编码、解码这些烦多的事情加入比较游戏中来,仅仅只会使用简单的文本消息。为了公平起见,我们会分别使用 Go 和 .NET Core 的 [MVC 架构模式][8]。
### 参赛选手
[Go][9] (或称 Golang 是一种[快速增长][10]的开源编程语言,旨在构建出简单、快捷和稳定可靠的应用软件。
用于支持 Go 语言的 MVC web 框架并不多,还好我们找到了 Iris ,可胜任此工作。
[Iris][11] 支持 Go 语言的快速、简单和高效的微型 Web 框架。它为您的下一代网站、API 或分布式应用程序奠定了精美的表现方式和易于使用的基础。
[C#][12] 是一种通用的、面向对象的编程语言。其开发团队由 [Anders Hejlsberg][13] 领导。
[.NET Core][14] 跨平台,可以在极少时间内开发出高性能的应用程序。
可从 [https://golang.org/dl][15] 下载 Go ,从 [https://www.microsoft.com/net/core][16] 下载 .NET Core。
在下载和安装好这些软件后,还需要为 Go 安装 Iris。安装很简单仅仅只需要打开终端然后执行如下语句
```
go get -u github.com/kataras/iris
```
### 基准
#### 硬件
* 处理器: Intel(R) Core(TM) i74710HQ CPU @ 2.50GHz 2.50GHz
* 内存: 8.00 GB
#### 软件
* 操作系统: 微软 Windows [10.0.15063 版本] 电源计划设置为“高性能”
* HTTP 基准工具: [https://github.com/codesenberg/bombardier][1] 使用最新的 1.1 版本。
* .NET Core [https://www.microsoft.com/net/core][2] 使用最新的 2.0 版本。
* Iris [https://github.com/kataras/iris][3] 使用基于 [Go 1.8.3][4] 构建的最新 8.3 版本。
两个应用程序都通过请求路径 “api/values/{id}” 返回文本“值”。
##### .NET Core MVC
![](https://cdn-images-1.medium.com/max/1600/1*v2VJL3-I3bLyuehntuqfng.png)
Logo 由 [Pablo Iglesias][5] 设计。
可以使用 `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
![](https://cdn-images-1.medium.com/max/1600/1*zf4BjL-7MQNJGikw5E-iNA.png)
Logo 由 [Santosh Anand][6] 设计。
源代码:
```
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)
}
view raw
```
```
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
```
想通过图片来理解的人,我也把我的屏幕截屏出来了!
请点击[这儿][23]可以看到这些屏幕快照。
#### 总结
* 完成 `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] [24] 和 [Scott Hanselman] [25]在此 [tweet 评论] [26]上指出,.NET Core `Startup.cs` 文件中 `services.AddMvc();` 这行可以替换为 `services.AddMvcCore();`。我听从他们的意见,修改代码,重新运行基准,该文章的 .NET Core 应用程序的基准输出已经修改。
@topdawgevh @shanselman 他们也在使用 `AddMvc()` 而不是 `AddMvcCore()` ...,难道都不包含中间件?
 —  @clarkis117
@clarkis117 @topdawgevh Cool @MakisMaropoulos @ben_a_adams @davidfowl 我们来看看。认真学习下怎么使用更简单的性能默认值。
 —  @shanselman
@shanselman @clarkis117 @topdawgevh @ben_a_adams @davidfowl @shanselman @ben_a_adams @davidfowl 谢谢您们的反馈意见。我已经修改,更新了结果,没什么不同。对其它的建议,我非常欢迎。
 —  @MakisMaropoulos
> 它有点稍微的不同但相差不大(从 8.61MB/s 到 8.91MB/s
想要了解跟 `services.AddMvc()` 标准比较结果的,可以点击[这儿][27]。
### 想再多了解点儿吗?
我们再制定一个基准,产生 `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。
### 接下来呢?
[这里][32]有上面所示的源代码,请下载下来,在您本地以同样的基准运行,然后把运行结果在这儿给大家分享。
想添加 Go 或 C# .net core WEB 服务框架到列表的朋友请向[这个仓库][33]的 `_benchmarks ` 目录推送 PR。
我也需要亲自感谢下 [dev.to][34] 团队,感谢把我的这篇文章分享到他们的 Twitter 账户。
感谢大家真心反馈,玩得开心!
#### 更新 : 2017 年 8 月 21 ,周一
很多人联系我,希望看到一个基于 .NET Core 的较低级别 Kestrel 的基准测试文章。
因此我完成了,请点击下面的[链接][35]来了解 Kestrel 和 Iris 之间的性能差异,它还包含一个会话存储管理基准!
--------------------------------------------------------------------------------
via: https://hackernoon.com/go-vs-net-core-in-terms-of-http-performance-7535a61b67b8
作者:[Gerasimos Maropoulos][a]
译者:[runningwater](https://github.com/runningwater)
校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]:https://hackernoon.com/@kataras?source=post_header_lockup
[1]:https://github.com/codesenberg/bombardier
[2]:https://www.microsoft.com/net/core
[3]:https://github.com/kataras/iris
[4]:https://golang.org/
[5]:https://github.com/campusMVP/dotnetCoreLogoPack
[6]:https://github.com/santoshanand
[7]:https://blogs.msdn.microsoft.com/dotnet/2017/08/14/announcing-net-core-2-0/
[8]:https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
[9]:https://golang.org/
[10]:http://www.tiobe.com/tiobe-index/
[11]:http://iris-go.com/
[12]:https://en.wikipedia.org/wiki/C_Sharp_%28programming_language%29
[13]:https://twitter.com/ahejlsberg
[14]:https://www.microsoft.com/net/
[15]:https://golang.org/dl
[16]:https://www.microsoft.com/net/core
[17]:http://localhost:5000/
[18]:http://localhost:5000/api/values/5
[19]:http://localhost:5000/api/values/5
[20]:http://localhost:5000/
[21]:http://localhost:5000/api/values/5
[22]:http://localhost:5000/api/values/5
[23]:https://github.com/kataras/iris/tree/master/_benchmarks/screens
[24]:https://twitter.com/clarkis117
[25]:https://twitter.com/shanselman
[26]:https://twitter.com/shanselman/status/899005786826788865
[27]:https://github.com/kataras/iris/blob/master/_benchmarks/screens/5m_requests_netcore-mvc.png
[28]:http://localhost:5000/
[29]:http://localhost:5000/
[30]:http://localhost:5000/
[31]:http://localhost:5000/
[32]:https://github.com/kataras/iris/tree/master/_benchmarks
[33]:https://github.com/kataras/iris
[34]:https://dev.to/kataras/go-vsnet-core-in-terms-of-http-performance
[35]:https://medium.com/@kataras/iris-go-vs-net-core-kestrel-in-terms-of-http-performance-806195dc93d5