为了账号安全,请及时绑定邮箱和手机立即绑定

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

标签:
Go

朋友们,你们好!


近来,我听到了大量的关于新出的 .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 之间的性能差异,它还包含一个会话存储管理基准!

编译自:https://hackernoon.com/go-vs-net-core-in-terms-of-http-performance-7535a61b67b8作者: Gerasimos Maropoulos
原创:LCTT https://linux.cn/article-8935-1.html译者: runningwater

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消