Linux中國

論 HTTP 性能,Go 與 .NET Core 一爭雌雄

朋友們,你們好!

近來,我聽到了大量的關於新出的 .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

軟體

兩個應用程序都通過請求路徑 「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:mygopathsrcgithub.comkatarasiris_benchmarksnetcore-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 ClarkScott 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
                &apos;Microsoft.AspNetCore.Mvc.ViewFeatures.ITempDataDictionaryFactory&apos; 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:mygopathsrcgithub.comkatarasiris_benchmarksnetcore-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 之間的性能差異,它還包含一個會話存儲管理基準!

via: https://hackernoon.com/go-vs-net-core-in-terms-of-http-performance-7535a61b67b8

作者:Gerasimos Maropoulos 譯者:runningwater 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的電子郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國