针对 Ocelot 网关的性能测试

一、背景

目前我们项目是采用的 Ocelot 作为 API 网关,并且在其基础上结合 IdentityServer4 开发了一套 API 开放平台。由于部分项目是基于 ABP 框架进行开发的,接口的平均 QPS 基本是在 2K~3K /S 左右 (E3 1231 16G)。采用 Ocelot 进行请求转发之后,前端反馈接口调用速度变慢了,也没有太过在意,以为是项目接口的问题,一直在接口上面尝试进行优化。

极限优化接口后仍然没有显著改善,故针对 Ocelot 的性能进行压力测试,得到的结果也是让我比较惊讶。

二、准备工作

2.1 测试项目准备

首先新建了一个解决方案,其名字为 OcelotStudy ,其下面有三个项目,分别是两个 API 项目和一个网关项目。

1203160-20190219151927182-652947288.png

网关项目编写:

OcelotStudy 项目引入 Ocelot 的 NuGet 包。

1203160-20190219151939634-313671963.png

OcelotStudy 项目的 Program.cs 文件当中显式指定我们网关的监听端口。

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace OcelotStudy
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    // 指定监听端口为 5000
                    webBuilder.UseStartup<Startup>()
                        .UseKestrel(x=>x.ListenAnyIP(5000));
                });
    }
}

Startup.cs 类当中注入 Ocelot 的服务,并应用 Ocelot 的中间件。

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;

namespace OcelotStudy
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            // 禁用日志的控制台输出,防止由于线程同步造成的性能损失
            services.AddLogging(op => op.ClearProviders());
            services.AddMvc();
            services.AddOcelot(new ConfigurationBuilder().AddJsonFile("Ocelot.json").Build());
        }

        public async void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            await app.UseOcelot();
            app.UseMvc();
        }
    }
}

OcelotStudy 项目下建立 Ocelot.json 文件,内容如下。

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/{everything}",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "API 1 服务器IP",
          "Port": 6000
        },
        {
          "Host": "API 2 服务器IP",
          "Port": 7000
        }
      ],
      "UpstreamPathTemplate": "/{everything}",
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      }
    }
  ],
  "GlobalConfiguration": {
    
  }
}

测试项目的编写:

两个测试项目的监听端口分别为 60007000 ,都建立一个 ValuesController 控制器,返回一个字符串用于输出当前请求的 API 服务器信息。

ApiService01 的文件信息:

using Microsoft.AspNetCore.Mvc;

namespace ApiService01.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        // GET api/values
        [HttpGet]
        public ActionResult<string> Get()
        {
            return "当前请求的 API 接口是 1 号服务器。";
        }

        // GET api/values/5
        [HttpGet("{id}")]
        public ActionResult<string> Get(int id)
        {
            return "value";
        }

        // POST api/values
        [HttpPost]
        public void Post([FromBody] string value)
        {
        }

        // PUT api/values/5
        [HttpPut("{id}")]
        public void Put(int id, [FromBody] string value)
        {
        }

        // DELETE api/values/5
        [HttpDelete("{id}")]
        public void Delete(int id)
        {
        }
    }
}

ApiService02 的文件信息:

using Microsoft.AspNetCore.Mvc;

namespace ApiService02.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        // GET api/values
        [HttpGet]
        public ActionResult<string> Get()
        {
            return "当前请求的 API 接口是 2 号服务器。";
        }

        // GET api/values/5
        [HttpGet("{id}")]
        public ActionResult<string> Get(int id)
        {
            return "value";
        }

        // POST api/values
        [HttpPost]
        public void Post([FromBody] string value)
        {
        }

        // PUT api/values/5
        [HttpPut("{id}")]
        public void Put(int id, [FromBody] string value)
        {
        }

        // DELETE api/values/5
        [HttpDelete("{id}")]
        public void Delete(int id)
        {
        }
    }
}

他们两个的 Startup.csProgram.cs 文件内容基本一致,区别只是监听的端口分别是 60007000 而已。

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace ApiService02
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                    webBuilder.UseKestrel(x => x.ListenAnyIP(6000)); // 或者 7000
                });
    }
}
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace ApiService02
{
    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.AddLogging(op => op.ClearProviders());
            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.UseRouting(routes => { routes.MapApplication(); });
        }
    }
}

以上三个项目都采用 Release 版本进行发布。

dotnet publish -c Release

ApiService01 部署在单独的 E3 1231 v3 16G DDR3 服务器。

ApiService02 部署在单独的 i3-7100 16G DDR4 服务器。

OcelotStudy 部署在单独的 E3 1231 v3 16G DDR3 服务器。

三、开始测试

这里我使用的是 WRK 来进行压力测试,OcelotStudy 网关项目的 IP 地址为 172.31.61.41:5000 ,故使用以下命令进行测试。

./wrk -t 10 -c 10000 -d 20s --latency --timeout 3s "http://172.31.61.41:5000/values"

测试结果:

1203160-20190219151953192-702502837.png

我将 ApiService01 项目放在网关的服务器,直接调用 ApiService01 的接口,其压力测试情况。

1203160-20190219152003124-785012670.gif

四、结语

最后 Ocelot 的 QPS 结果为:3461.53

直接请求 API 接口的 QPS 结果为:38874.50

这样的结果让我感到很意外,不知道是由于 Ocelot 实现机制的原因,还是我的使用方法不对。这样的性能测试结果数据对于 API 网关来说确实不太好看,但也希望今后 Ocelot 能够继续努力。

如果大家对于我的测试方式有疑问的话,可以在评论区指出,我将按照你所提供的方法再次进行测试。(PS: 我也不想换啊,多希望是我测错了)

五、原生 API、Ocelot、Kong API 性能比较

针对于评论区各位朋友所提出的建议,以及我最近针对 Ocelot 的再次测试,整理出来一份测试结果的表格。以下结果均是单节点进行部署,如果使用 LB + API 集群的话是可以有效提升吞吐量。

使用的网关测试时间并发数总请求QPS超时数量平均响应
没有使用网关1 分钟15000249554141518.071592115.98 ms
Kong API1 分钟1500069014111478.0643997860.31 ms
Ocelot1 分钟150002776274618.9817951.7 s
没有使用网关1 分钟5000253010742111.440115.48 ms
Kong API1 分钟500086644914418.253090383.75 ms
Ocelot1 分钟50003072265113.0978932.19 ms
没有使用网关10 分钟80001308096421797.98103364.97 ms
Kong API10 分钟800048096138014.7305503636.41 ms
Ocelot10 分钟800025584314263.3421371.84 s

Ocelot 与 Kong 均部署在一台 32G 12C 3.0Ghz 的 CentOS 服务器上,API 1 与 API 2 部署在 8C 16G 3.0 Ghz 的服务器上,所有环境都是基于 Docker CE 进行部署。

另外针对于 Kong API 不知道是我使用方式不对还是什么情况,其网络吞吐量经常波动,如下图,还请不吝赐教。

1203160-20190221164848516-2039236415.png

网关服务器:

1203160-20190221164859864-1566092746.png

API 1 与 API 2 服务器:

1203160-20190221164909222-892827356.png

1203160-20190221164919192-580046330.png

转载于:https://www.cnblogs.com/myzony/p/10401298.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值