最近对modern c++ http server--cinatra做了一点性能优化,优化之后做了性能测试,顺便也对比了其它一些常用的http服务器的性能,选择了一些常用的http服务器:java(netty和tomcat), go(beego), .net core3.1, rust(actix-web)的http server。想看看cinatra和其他http server的性能相比如何。
测试机器
CPU: 6 核心 Intel(R) Core(TM) i5-9400F CPU @ 2.90GHz
内存: 16G
系统:ubuntu18.04
测试方法
发送一个GET请求,返回Hello, World!字符串,类似于:
请求
GET /plaintext HTTP/1.1
Host: server
Connection: keep-alive
响应
HTTP/1.1 200 OK
Content-Length: 13
Content-Type: text/plain; charset=UTF-8
Server: Example
Date: Thu, 09 Jan 2020 14:14:30 GMT
Hello, World!
测试工具
测试工具选用的wrk,wrk是一个性能优异的http压测工具,很多产品都使用它作为http压测工具,相比ab来说,wrk性能更高,能做充分的压力测试,而且支持的特性更多。
测试方法
wrk命令行
wrk --latency -d10 -c200 --timeout 8 -t 6 http://127.0.0.1:8090/plaintext
命令行解释
-c200: 启动200个连接
-t6: 开启6个线程做压力测试
-d10: 压测持续10s
--timeout 8: 连接超时时间8s
示例
Running 10s test @ http://127.0.0.1:8090/plaintext
6 threads and 1024 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 6.22ms 6.29ms 97.86ms 87.97%
Req/Sec 32.44k 4.98k 60.79k 72.22%
Latency Distribution
50% 4.53ms
75% 7.82ms
90% 13.77ms
99% 31.08ms
1932505 requests in 10.07s, 235.90MB read
Requests/sec: 191933.19
Transfer/sec: 23.43MB
该测试的结果 qps为191933.19, 测试三次取最高的qps.
测试结果
测试方法、测试工具都准备好之后就开始做压力测试了,下面是压力测试的结果:
可以看到cinatra的性能是最高的,达到了52w, 这就到顶了吗?远没有,让我们再突破一下,看下一组测试数据:
可以看到支持了http pipeline之后cinatra性能增加了10倍以上达到了670w+。
再来看看测试命令行:
wrk --latency -d10 -c200 --timeout 8 -t 6 http://127.0.0.1:8090/plaintext -s pipeline.lua -- 32
pipeline是http1.1的特性,可以将多个request请求合并在一起发送,服务端按照请求顺序返回给用户,减少了请求次数和实现了结果合并发送从而大幅提高了性能。
测试结论
cinatra作为一个跨平台的http库的性能还是很优异的,它是基于asio开发的,也间接证明了asio的性能也是很优异的,asio会在c++23中进入标准库。
附测试源码
node.js
const Koa = require('koa')
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
router.get('/test', async(ctx, next) => {
ctx.body = 'hello world';
ctx.status = 200;
next();
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(8081, () => {
console.log("listen 6601")
})
java netty和tomcat
package com.octopus.websocket.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
@RestController
public class TestController {
@RequestMapping("/")
Mono hello(){
return Mono.just("hello c++");
}
}
go(beego)
package main
import("github.com/astaxie/beego")
type MainController struct {
beego.Controller
}
func(this*MainController)Get(){
this.Ctx.WriteString("hello world")
}
func main(){
beego.BConfig.Listen.HTTPPort =8082
beego.Router("/",&MainController{})
beego.Run()
}
.net-core3.1
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace dotnet_performanceTest {
public class Startup {
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices (IServiceCollection services) {
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure (IApplicationBuilder app, IWebHostEnvironment env) {
app.UseRouting ();
app.UseEndpoints (endpoints => {
endpoints.MapGet ("/", async context => {
await context.Response.WriteAsync ("Hello World!");
});
});
}
}
}
rust(actix-web)
use actix_web::{ web, App, HttpRequest, HttpServer};
fn index(req: HttpRequest) -> &'static str {
"Hello world!"
}
//#[actix_rt::main]
fn main() -> () {
HttpServer::new(|| {
App::new()
.service(web::resource("/").to(index))
})
.bind("0.0.0.0:8083").unwrap()
.run();
}
c++ cinatra
#include
#include
using namespace cinatra;
int main() {
http_server server(std::thread::hardware_concurrency());
bool r = server.listen("0.0.0.0", "8090");
if (!r) {
std::cout << "listen failed\n";
return -1;
}
server.set_http_handler("/plaintext", [](request& req, response& res) {
res.set_status_and_content(status_type::ok, "Hello, World!", res_content_type::string);
});
server.run();
return 0;
}