ASP.NET Core vs Go: Hiệu suất HTTP của ai tốt hơn

Trong bài viết này, chúng tôi sẽ so sánh hiệu suất HTTP của ASP.NET Core và Go. Để đạt được sự so sánh công bằng, chúng tôi sẽ sử dụng mô hình kiến ​​trúc MVC trên cả ASP.NET Core và Go.

Điều kiện tiên quyết

Go (hay Golang): là một ngôn ngữ lập trình mã nguồn mở đang phát triển nhanh chóng được thiết kế để xây dựng phần mềm đơn giản, nhanh chóng và đáng tin cậy.

Không có nhiều web framework cho Go có hỗ trợ MVC nhưng may mắn cho chúng tôi là Iris đã làm được công việc đó.

Iris: Một micro web framework nhanh chóng, đơn giản và hiệu quả cho Go. Nó cung cấp một nền tảng tuyệt vời và dễ sử dụng cho trang web, API hoặc ứng dụng được phân phối tiếp theo của bạn.

C#: là một ngôn ngữ lập trình hướng đối tượng, đa mục đích chung. Nhóm phát triển của nó được dẫn dắt bởi Anders Hejlsberg.

.NET Core: Phát triển các ứng dụng hiệu suất cao trong thời gian ngắn hơn, trên mọi nền tảng.

Tải xuống Go từ https://golang.org/dl và .NET Core từ https://www.microsoft.com/net/core.

Sau khi bạn tải xuống và cài đặt chúng, bạn sẽ cần cài đặt Iris cho Go. Cài đặt rất dễ dàng, chỉ cần mở thiết bị đầu cuối (cửa sổ dòng lệnh) của bạn và gõ lệnh:

go get -u github.com/kataras/iris

Benchmark

Hardware

  • Processor: Intel(R) Core(TM) i7–4710HQ CPU @ 2.50GHz 2.50GHz
  • RAM: 8.00 GB

Software

Tiêu chí đánh giá

  • Thời gian để hoàn thành 5.000.000 request - càng nhỏ càng tốt.
  • Số lượng request / giây - càng lớn càng tốt.
  • Độ trễ - càng nhỏ càng tốt.
  • Thông lượng - càng lớn càng tốt.

Benchmark cho API

Cả hai ứng dụng ASP.NET Core và Go sẽ chỉ trả về dữ liệu dạng text khi gọi vào API url “api/values/{id}”.

ASP.NET Core Web API

Được tạo bằng cách sử dụng lệnh dotnet new webapi. Mẫu webapi sẽ tạo mã API sẵn cho bạn.

Mã nguồn của API như sau:

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)
        {
        }
    }
}

Chạy ASP.NET Core web server:

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

Kết quả benchmark:

$ 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

Go với Iris MVC

Mã nguồn của API như sau:

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() {}

Chạy Go web server:

$ cd iris-mvc
$ go run main.go
Now listening on: http://localhost:5000
Application started. Press CTRL+C to shut down.

Kết quả benchmark:

$ 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

Kết quả benchmark

ASP.NET Core Web API:

  • Chạy trong 2 phút 3 giây.
  • Xử lý 40.226,03 yêu cầu mỗi giây.
  • Độ trễ trung bình là 3,09ms và độ trễ tối đa là 169,12ms.
  • Thông lượng là 8,91 MB/s.

Go với Iris MVC:

  • Chạy trong cho 47 giây.
  • Phục vụ 105.643,81 yêu cầu mỗi giây.
  • Độ trễ trung bình là 1,18ms và độ trễ tối đa là 22,01ms.
  • Thông lượng là 19,65 MB/s.

Benchmark cho Web MVC

Trong phần này chúng ta sẽ tiến hành benchmark cho web MVC.

ASP.NET Core MVC

Mã nguồn của Web MVC như sau:

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)
        {
            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?}");
            });
        }
    }
}
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;

namespace netcore_mvc_templates.Models
{
    public class ErrorViewModel
    {
        public string Title { get; set; }
        public int Code { get; set; }
    }
}

Chạy ASP.NET Core web server:

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

Kết quả benchmark:

$ bombardier -c 125 -n 1000000 http://localhost:5000/home/error
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

Go với Iris MVC

Mã nguồn của Web MVC như sau:

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

Chạy ASP.NET Core web server:

$ cd iris-mvc-templates
$ go run main.go
Now listening on: http://localhost:5000
Application started. Press CTRL+C to shut down.

Kết quả benchmark:

$ bombardier -c 125 -n 1000000 http://localhost:5000/home/error
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

Kết quả benchmark

ASP.NET Core MVC:

  • Chạy trong 1 phút 20 giây.
  • Xử lý 11.738,60 yêu cầu mỗi giây.
  • Độ trễ trung bình là 10,10ms và độ trễ tối đa là 1,97s.
  • Thông lượng là 89,03 MB/s.

Go với Iris MVC:

  • Chạy trong cho 37 giây.
  • Phục vụ 26.656,76 yêu cầu mỗi giây.
  • Độ trễ trung bình là 4,69ms và độ trễ tối đa là 22,52ms.
  • Thông lượng là 192,51 MB/s.
ASP.NET Core MVCASP.NET CoreASP.NET Core Web APIGo
Bài Viết Liên Quan:
ASP.NET Core Dependency Injection: Best Practice, Mẹo và Thủ Thuật
Trung Nguyen 10/10/2020
ASP.NET Core Dependency Injection: Best Practice, Mẹo và Thủ Thuật

Trong bài viết này, tôi sẽ chia sẻ các best practice, mẹo và thủ thuật về việc sử dụng Dependency Injection (DI) trong ứng dụng ASP.NET Core.

View Layout, View Start, View Imports trong ASP.NET Core
Trung Nguyen 10/04/2020
View Layout, View Start, View Imports trong ASP.NET Core

View Layout, View Start, View Imports là gì? Lợi ích và các sử dụng View Layout, View Start, View Imports trong ASP.NET Core.

View trong ASP.NET Core
Trung Nguyen 10/04/2020
View trong ASP.NET Core

Tất tần tật về View, Razor View, Layout View, View Start, View Import, Tag Helpers trong ASP.NET Core MVC.

ActionResult trong ASP.NET Core
Trung Nguyen 10/04/2020
ActionResult trong ASP.NET Core

ActionResult trong ASP.NET Core là gì? Có những loại ActionResult nào? Làm sao để sử dụng ActionResult trong ASP.NET Core.