2017-10-06 11:15:49 +08:00
|
|
|
|
论 HTTP 性能,Go 与 .NET Core 一争雌雄
|
2017-08-27 10:39:57 +08:00
|
|
|
|
============================================================
|
|
|
|
|
|
|
|
|
|
![](https://cdn-images-1.medium.com/max/1200/1*60T60jSMO5yxUqJQmLZa4A.png)
|
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
朋友们,你们好!
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
近来,我听到了大量的关于新出的 .NET Core 和其性能的讨论,尤其在 Web 服务方面的讨论更甚。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
因为是新出的,我不想立马就比较两个不同的东西,所以我耐心等待,想等发布更稳定的版本后再进行。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
本周一(8 月 14 日),微软[发布 .NET Core 2.0 版本][7],因此,我准备开始。您们认为呢?
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
如前面所提的,我们会比较它们相同的东西,比如应用程序、预期响应及运行时的稳定性,所以我们不会把像对 JSON 或者 XML 的编码、解码这些烦多的事情加入比较游戏中来,仅仅只会使用简单的文本消息。为了公平起见,我们会分别使用 Go 和 .NET Core 的 [MVC 架构模式][8]。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
### 参赛选手
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
[Go][9] (或称 Golang): 是一种[快速增长][10]的开源编程语言,旨在构建出简单、快捷和稳定可靠的应用软件。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
用于支持 Go 语言的 MVC web 框架并不多,还好我们找到了 Iris ,可胜任此工作。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
[Iris][11]: 支持 Go 语言的快速、简单和高效的微型 Web 框架。它为您的下一代网站、API 或分布式应用程序奠定了精美的表现方式和易于使用的基础。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
[C#][12]: 是一种通用的、面向对象的编程语言。其开发团队由 [Anders Hejlsberg][13] 领导。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
[.NET Core][14]: 跨平台,可以在极少时间内开发出高性能的应用程序。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
可从 [https://golang.org/dl][15] 下载 Go ,从 [https://www.microsoft.com/net/core][16] 下载 .NET Core。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
在下载和安装好这些软件后,还需要为 Go 安装 Iris。安装很简单,仅仅只需要打开终端,然后执行如下语句:
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
go get -u github.com/kataras/iris
|
|
|
|
|
```
|
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
### 基准
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
#### 硬件
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
* 处理器: Intel(R) Core(TM) i7–4710HQ CPU @ 2.50GHz 2.50GHz
|
|
|
|
|
* 内存: 8.00 GB
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
#### 软件
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
* 操作系统: 微软 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 版本。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
两个应用程序都通过请求路径 “api/values/{id}” 返回文本“值”。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
##### .NET Core MVC
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
![](https://cdn-images-1.medium.com/max/1600/1*v2VJL3-I3bLyuehntuqfng.png)
|
2017-10-06 11:15:49 +08:00
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
Logo 由 [Pablo Iglesias][5] 设计。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
可以使用 `dotnet new webapi` 命令创建项目,其 `webapi` 模板会为您生成代码,代码包含 `GET` 请求方法的 `返回“值”`。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
源代码:
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
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)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
运行 .NET Core web 服务项目:
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
$ 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.
|
|
|
|
|
```
|
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
运行和定位 HTTP 基准工具:
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
$ 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
|
|
|
|
|
```
|
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
##### Iris MVC
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
![](https://cdn-images-1.medium.com/max/1600/1*zf4BjL-7MQNJGikw5E-iNA.png)
|
2017-10-06 11:15:49 +08:00
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
Logo 由 [Santosh Anand][6] 设计。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
源代码:
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
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() {}
|
|
|
|
|
```
|
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
运行 Go web 服务项目:
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
$ cd iris-mvc
|
|
|
|
|
$ go run main.go
|
|
|
|
|
Now listening on: http://localhost:5000
|
|
|
|
|
Application started. Press CTRL+C to shut down.
|
|
|
|
|
```
|
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
运行和定位 HTTP 基准工具:
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
$ 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
|
|
|
|
|
```
|
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
想通过图片来理解的人,我也把我的屏幕截屏出来了!
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
请点击[这儿][23]可以看到这些屏幕快照。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
#### 总结
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
* 完成 `5000000 个请求`的时间 - 越短越好。
|
|
|
|
|
* 请求次数/每秒 - 越大越好。
|
|
|
|
|
* 等待时间 — 越短越好。
|
|
|
|
|
* 吞吐量 — 越大越好。
|
|
|
|
|
* 内存使用 — 越小越好。
|
|
|
|
|
* LOC (代码行数) — 越少越好。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
.NET Core MVC 应用程序,使用 86 行代码,运行 2 分钟 8 秒,每秒接纳 39311.56 个请求,平均 3.19ms 等待,最大时到 229.73ms,内存使用大约为 126MB(不包括 dotnet 框架)。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
Iris MVC 应用程序,使用 27 行代码,运行 47 秒,每秒接纳 105643.71 个请求,平均 1.18ms 等待,最大时到 22.01ms,内存使用大约为 12MB。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
> 还有另外一个模板的基准,滚动到底部。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
**2017 年 8 月 20 号更新**
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
[Josh Clark] [24] 和 [Scott Hanselman] [25]在此 [tweet 评论] [26]上指出,.NET Core `Startup.cs` 文件中 `services.AddMvc();` 这行可以替换为 `services.AddMvcCore();`。我听从他们的意见,修改代码,重新运行基准,该文章的 .NET Core 应用程序的基准输出已经修改。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
@topdawgevh @shanselman 他们也在使用 `AddMvc()` 而不是 `AddMvcCore()` ...,难道都不包含中间件?
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
— @clarkis117
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-24 23:55:52 +08:00
|
|
|
|
@clarkis117 @topdawgevh Cool @MakisMaropoulos @ben_a_adams @davidfowl 我们来看看。认真学习下怎么使用更简单的性能默认值。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
— @shanselman
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-28 23:02:24 +08:00
|
|
|
|
@shanselman @clarkis117 @topdawgevh @ben_a_adams @davidfowl @shanselman @ben_a_adams @davidfowl 谢谢您们的反馈意见。我已经修改,更新了结果,没什么不同。对其它的建议,我非常欢迎。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
— @MakisMaropoulos
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
> 它有点稍微的不同但相差不大(从 8.61MB/s 到 8.91MB/s)
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-28 23:02:24 +08:00
|
|
|
|
想要了解跟 `services.AddMvc()` 标准比较结果的,可以点击[这儿][27]。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-28 23:02:24 +08:00
|
|
|
|
### 想再多了解点儿吗?
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-28 23:02:24 +08:00
|
|
|
|
我们再制定一个基准,产生 `1000000 次请求`,这次会通过视图引擎由模板生成 `HTML` 页面。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-28 23:02:24 +08:00
|
|
|
|
#### .NET Core MVC 使用的模板
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
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
|
2017-09-28 23:02:24 +08:00
|
|
|
|
|
2017-08-27 10:39:57 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
运行 .NET Core 服务项目:
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
$ 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.
|
|
|
|
|
```
|
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
运行 HTTP 基准工具:
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
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
|
|
|
|
|
```
|
|
|
|
|
|
2017-09-28 23:02:24 +08:00
|
|
|
|
#### Iris MVC 使用的模板
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
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
|
|
|
|
|
*/
|
2017-09-28 23:02:24 +08:00
|
|
|
|
|
2017-08-27 10:39:57 +08:00
|
|
|
|
```
|
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
运行 Go 服务项目:
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
$ cd iris-mvc-templates
|
|
|
|
|
$ go run main.go
|
|
|
|
|
Now listening on: http://localhost:5000
|
|
|
|
|
Application started. Press CTRL+C to shut down.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
运行 HTTP 基准工具:
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
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
|
|
|
|
|
```
|
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
#### 总结
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-28 23:02:24 +08:00
|
|
|
|
* 完成 `1000000 个请求`的时间 - 越短越好。
|
|
|
|
|
* 请求次数/每秒 - 越大越好。
|
|
|
|
|
* 等待时间 — 越短越好。
|
|
|
|
|
* 内存使用 — 越小越好。
|
|
|
|
|
* 吞吐量 — 越大越好。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
.NET Core MVC 模板应用程序,运行 1 分钟 20 秒,每秒接纳 11738.60 个请求,同时每秒生成 89.03M 页面,平均 10.10ms 等待,最大时到 1.97s,内存使用大约为 193MB(不包括 dotnet 框架)。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
Iris MVC 模板应用程序,运行 37 秒,每秒接纳 26656.76 个请求,同时每秒生成 192.51M 页面,平均 1.18ms 等待,最大时到 22.52ms,内存使用大约为 17MB。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-28 23:02:24 +08:00
|
|
|
|
### 接下来呢?
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-28 23:02:24 +08:00
|
|
|
|
[这里][32]有上面所示的源代码,请下载下来,在您本地以同样的基准运行,然后把运行结果在这儿给大家分享。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-28 23:02:24 +08:00
|
|
|
|
想添加 Go 或 C# .net core WEB 服务框架到列表的朋友请向[这个仓库][33]的 `_benchmarks ` 目录推送 PR。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-28 23:02:24 +08:00
|
|
|
|
我也需要亲自感谢下 [dev.to][34] 团队,感谢把我的这篇文章分享到他们的 Twitter 账户。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
|
2017-09-28 23:02:24 +08:00
|
|
|
|
感谢大家真心反馈,玩得开心!
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-28 23:02:24 +08:00
|
|
|
|
#### 更新 : 2017 年 8 月 21 ,周一
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-09-28 23:02:24 +08:00
|
|
|
|
很多人联系我,希望看到一个基于 .NET Core 的较低级别 Kestrel 的基准测试文章。
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
因此我完成了,请点击下面的[链接][35]来了解 Kestrel 和 Iris 之间的性能差异,它还包含一个会话存储管理基准!
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
via: https://hackernoon.com/go-vs-net-core-in-terms-of-http-performance-7535a61b67b8
|
|
|
|
|
|
2017-10-06 11:15:49 +08:00
|
|
|
|
作者:[Gerasimos Maropoulos][a]
|
2017-09-24 23:55:52 +08:00
|
|
|
|
译者:[runningwater](https://github.com/runningwater)
|
2017-10-06 11:15:49 +08:00
|
|
|
|
校对:[wxy](https://github.com/wxy)
|
2017-08-27 10:39:57 +08:00
|
|
|
|
|
|
|
|
|
本文由 [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
|
2017-10-06 11:15:49 +08:00
|
|
|
|
[35]:https://medium.com/@kataras/iris-go-vs-net-core-kestrel-in-terms-of-http-performance-806195dc93d5
|