针对 Ocelot 网关的性能测试
一、背景
目前我们项目是采用的 ocelot 作为 api 网关,并且在其基础上结合 identityserver4 开发了一套 api 开放平台。由于部分项目是基于 abp 框架进行开发的,接口的平均 qps 基本是在 2k~3k /s 左右 (e3 1231 16g)。采用 ocelot 进行请求转发之后,前端反馈接口调用速度变慢了,也没有太过在意,以为是项目接口的问题,一直在接口上面尝试进行优化。
极限优化接口后仍然没有显著改善,故针对 ocelot 的性能进行压力测试,得到的结果也是让我比较惊讶。
二、准备工作
2.1 测试项目准备
首先新建了一个解决方案,其名字为 ocelotstudy
,其下面有三个项目,分别是两个 api 项目和一个网关项目。
网关项目编写:
为 ocelotstudy
项目引入 ocelot 的 nuget 包。
在 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": "localhost", "port": 6000 }, { "host": "localhost", "port": 7000 } ], "upstreampathtemplate": "/{everything}", "upstreamhttpmethod": [ "get", "post" ], "loadbalanceroptions": { "type": "roundrobin" } } ], "globalconfiguration": { // "baseurl": "https://api.yilezhu.cn" } }
测试项目的编写:
两个测试项目的监听端口分别为 6000
与 7000
,都建立一个 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.cs
与 program.cs
文件内容基本一致,区别只是监听的端口分别是 6000
和 7000
而已。
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"
测试结果:
我将 apiservice01 项目放在网关的服务器,直接调用 apiservice01 的接口,其压力测试情况。
四、结语
最后 ocelot 的 qps 结果为:3461.53
直接请求 api 接口的 qps 结果为:38874.50
这样的结果让我感到很意外,不知道是由于 ocelot 实现机制的原因,还是我的使用方法不对。这样的性能测试结果数据对于 api 网关来说确实不太好看,但也希望今后 ocelot 能够继续努力。
如果大家对于我的测试方式有疑问的话,可以在评论区指出,我将按照你所提供的方法再次进行测试。(ps: 我也不想换啊,多希望是我测错了)
上一篇: [.NET] 使用ValidationContext快速进行模型资料的验证
下一篇: 家里团年