原创Linux中国12-11 06:29

摘要: 如前面所提的,我们会比较它们相同的东西,比如应用程序、预期响应及运行时的稳定性,所以我们不会把像对 JSON 或者 XML 的编码、解码这些烦多的事情加入比较游戏中来,仅仅只会使用简单的文本消息。

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


本文导航
编译自 | 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。安装很简单,仅仅只需要打开终端,然后执行如下语句:

  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[3] 构建的最新 8.3 版本。

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

.NET Core MVC

Logo 由 Pablo Iglesias[13] 设计。

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

源代码:

  1. using System;

  2. using System.Collections.Generic;

  3. using System.IO;

  4. using System.Linq;

  5. using System.Threading.Tasks;

  6. using Microsoft.AspNetCore;

  7. using Microsoft.AspNetCore.Hosting;

  8. using Microsoft.Extensions.Configuration;

  9. using Microsoft.Extensions.Logging;

  10. namespace netcore_mvc

  11. {

  12.    public class Program

  13.    {

  14.        public static void Main(string[] args)

  15.        {

  16.            BuildWebHost(args).Run();

  17.        }

  18.        public static IWebHost BuildWebHost(string[] args) =>

  19.            WebHost.CreateDefaultBuilder(args)

  20.                .UseStartup<Startup>()

  21.                .Build();

  22.    }

  23. }

  1. using System;

  2. using System.Collections.Generic;

  3. using System.Linq;

  4. using System.Threading.Tasks;

  5. using Microsoft.AspNetCore.Builder;

  6. using Microsoft.AspNetCore.Hosting;

  7. using Microsoft.Extensions.Configuration;

  8. using Microsoft.Extensions.DependencyInjection;

  9. using Microsoft.Extensions.Logging;

  10. using Microsoft.Extensions.Options;

  11. namespace netcore_mvc

  12. {

  13.    public class Startup

  14.    {

  15.        public Startup(IConfiguration configuration)

  16.        {

  17.            Configuration = configuration;

  18.        }

  19.        public IConfiguration Configuration { get; }

  20.        // This method gets called by the runtime. Use this method to add services to the container.

  21.        public void ConfigureServices(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.        public void Configure(IApplicationBuilder app, IHostingEnvironment env)

  27.        {

  28.            app.UseMvc();

  29.        }

  30.    }

  31. }

  1. using System;

  2. using System.Collections.Generic;

  3. using System.Linq;

  4. using System.Threading.Tasks;

  5. using Microsoft.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.    public class ValuesController : Controller

  12.    {

  13.        // Get handles "GET" requests to "api/values/{id}".

  14.        [HttpGet("{id}")]

  15.        public string Get(int id)

  16.        {

  17.            return "value";

  18.        }

  19.        // Put handles "PUT" requests to "api/values/{id}".

  20.        [HttpPut("{id}")]

  21.        public void Put(int id, [FromBody]string value)

  22.        {

  23.        }

  24.        // Delete handles "DELETE" requests to "api/values/{id}".

  25.        [HttpDelete("{id}")]

  26.        public void Delete(int id)

  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. 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% 2m3s

  4. Done!

  5. Statistics        Avg      Stdev        Max

  6.  Reqs/sec     40226.03    8724.30     161919

  7.  Latency        3.09ms     1.40ms   169.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 ValuesController struct {

  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. Statistics        Avg      Stdev        Max

  6.  Reqs/sec    105643.81    7687.79     122564

  7.  Latency        1.18ms   366.55us    22.01ms

  8.  HTTP codes:

  9.    1xx - 0, 2xx - 5000000, 3xx - 0, 4xx - 0, 5xx - 0

  10.    others - 0

  11.  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)

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

想再多了解点儿吗?

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

.NET Core MVC 使用的模板

  1. using System;

  2. namespace netcore_mvc_templates.Models

  3. {

  4.    public class ErrorViewModel

  5.    {

  6.        public string Title { get; set; }

  7.        public int Code { get; set; }

  8.    }

  9. }

  1. using System;

  2. using System.Collections.Generic;

  3. using System.Diagnostics;

  4. using System.Linq;

  5. using System.Threading.Tasks;

  6. using Microsoft.AspNetCore.Mvc;

  7. using netcore_mvc_templates.Models;

  8. namespace netcore_mvc_templates.Controllers

  9. {

  10.    public class HomeController : Controller

  11.    {

  12.        public IActionResult Index()

  13.        {

  14.            return View();

  15.        }

  16.        public IActionResult About()

  17.        {

  18.            ViewData["Message"] = "Your application description page.";

  19.            return View();

  20.        }

  21.        public IActionResult Contact()

  22.        {

  23.            ViewData["Message"] = "Your contact page.";

  24.            return View();

  25.        }

  26.        public IActionResult Error()

  27.        {

  28.            return View(new ErrorViewModel { Title = "Error", Code = 500});

  29.        }

  30.    }

  31. }

  1. using System;

  2. using System.Collections.Generic;

  3. using System.IO;

  4. using System.Linq;

  5. using System.Threading.Tasks;

  6. using Microsoft.AspNetCore;

  7. using Microsoft.AspNetCore.Hosting;

  8. using Microsoft.Extensions.Configuration;

  9. using Microsoft.Extensions.Logging;

  10. namespace netcore_mvc_templates

  11. {

  12.    public class Program

  13.    {

  14.        public static void Main(string[] args)

  15.        {

  16.            BuildWebHost(args).Run();

  17.        }

  18.        public static IWebHost BuildWebHost(string[] args) =>

  19.            WebHost.CreateDefaultBuilder(args)

  20.                .UseStartup<Startup>()

  21.                .Build();

  22.    }

  23. }

  1. using System;

  2. using System.Collections.Generic;

  3. using System.Linq;

  4. using System.Threading.Tasks;

  5. using Microsoft.AspNetCore.Builder;

  6. using Microsoft.AspNetCore.Hosting;

  7. using Microsoft.Extensions.Configuration;

  8. using Microsoft.Extensions.DependencyInjection;

  9. namespace netcore_mvc_templates

  10. {

  11.    public class Startup

  12.    {

  13.        public Startup(IConfiguration configuration)

  14.        {

  15.            Configuration = configuration;

  16.        }

  17.        public IConfiguration Configuration { get; }

  18.        // This method gets called by the runtime. Use this method to add services to the container.

  19.        public void ConfigureServices(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.        public void Configure(IApplicationBuilder app, IHostingEnvironment env)

  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. Press Ctrl+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. Statistics Avg Stdev Max

  5. Reqs/sec 11738.60 7741.36 125887

  6. Latency 10.10ms 22.10ms 1.97s

  7. HTTP codes:

  8. 1xx 0, 2xx 1000000, 3xx 0, 4xx 0, 5xx 0

  9. others 0

  10. Throughput: 89.03MB/s

Iris MVC 使用的模板

  1. package controllers

  2. import "github.com/kataras/iris/mvc"

  3. type AboutController struct{ 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 ContactController struct{ 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 HTTPError struct {

  4.    Title string

  5.    Code  int

  6. }

  1. package controllers

  2. import "github.com/kataras/iris/mvc"

  3. type IndexController struct{ 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.    Code  int

  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. Statistics Avg Stdev Max

  5. Reqs/sec 26656.76 1944.73 31188

  6. Latency 4.69ms 1.20ms 22.52ms

  7. HTTP codes:

  8. 1xx 0, 2xx 1000000, 3xx 0, 4xx 0, 5xx 0

  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。

接下来呢?

这里[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

作者:Gerasimos Maropoulos[24] 译者:runningwater 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

LCTT 译者
runningwater
共计翻译:51 篇
贡献时间:1227 天

推荐文章

< 左右滑动查看相关文章 >

点击图片、输入文章 ID 或识别二维码直达