天天看點

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

論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 或分布式應用程式奠定了精美的表現方式和易于使用的基礎。

.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

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

可以使用 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 Microsoft.AspNetCore.Builder; 

using Microsoft.Extensions.DependencyInjection; 

using Microsoft.Extensions.Options; 

    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 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

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

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 

Application started. Press CTRL+C to shut down. 

 5000000 / 5000000 [======================================================] 100.00% 47s 

  Reqs/sec    105643.81    7687.79     122564 

  Latency        1.18ms   366.55us    22.01ms 

  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 使用的模闆

namespace netcore_mvc_templates.Models 

    public class ErrorViewModel 

        public string Title { get; set; } 

        public int Code { get; set; } 

 using System; 

using System.Diagnostics; 

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."; 

        public IActionResult Contact() 

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

        public IActionResult Error() 

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

namespace netcore_mvc_templates 

            /*  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(); 

            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 

Content root path: C:\mygopath\src\github.com\kataras\iris\_benchmarks\netcore-mvc-templates 

運作 HTTP 基準工具:

Bombarding http://localhost:5000 with 1000000 requests using 125 connections 

 1000000 / 1000000 [====================================================] 100.00% 1m20s 

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 使用的模闆

type AboutController struct{ mvc.Controller } 

func (c *AboutController) Get() { 

    c.Data["Title"] = "About" 

    c.Data["Message"] = "Your application description page." 

    c.Tmpl = "about.html" 

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 

type IndexController struct{ mvc.Controller } 

func (c *IndexController) Get() { 

    c.Data["Title"] = "Home Page" 

    c.Tmpl = "index.html" 

    "github.com/kataras/iris/_benchmarks/iris-mvc-templates/controllers" 

    "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" 

    app.Configure(configure) 

    app.Controller("/", new(controllers.IndexController)) 

    app.Controller("/about", new(controllers.AboutController)) 

    app.Controller("/contact", new(controllers.ContactController)) 

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 { 

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 

https://github.com/kataras/iris/tree/master/_benchmarks/iris-mvc-templates 

*/ 

運作 Go 服務項目:

$ cd iris-mvc-templates 

 1000000 / 1000000 [======================================================] 100.00% 37s 

 Reqs/sec 26656.76 1944.73 31188 

 Latency 4.69ms 1.20ms 22.52ms 

 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 之間的性能差異,它還包含一個會話存儲管理基準! 

原文釋出時間為:2017-10-10 

本文作者:Gerasimos Maropoulos

本文來自雲栖社群合作夥伴“51CTO”,了解相關資訊可以關注。

繼續閱讀