Java&Go 三种 HTTP 客户端性能测试

语言: CN / TW / HK

在学完 Golang语言HTTP客户端实践 Go语言HTTPServer开发的六种实现 之后,我自然开始了 Java&Go 两种语言的 HTTP 客户端性能测试。

之前在写 10万QPS,K6、Gatling和FunTester终极对决! 这个文章以及 单机12万QPS——FunTester复仇记 的时候,都是把 CPU 跑满了,为了达到 12 万 QPS,我把所有除了统计以外的代码都删除了,这次就不搞这么极端了。而且在我自己初测的时候发现笔记本电脑最多只能跑到 80%CPU,不知道是不是 macOS 限制了,再说消耗自己电脑,我也是心疼。

服务端

服务端依旧采取 moco_FunTester 框架的 moco 服务,代码如下:

class Share extends MocoServer {
static void main(String[] args) { def util = new ArgsUtil(args) def server = getServerNoLog(util.getIntOrdefault(0,12345)) server.response("Have Fun ~ Tester !")// server.response(delay(textRes("Have Fun ~ Tester !"),10)) def run = run(server) waitForKey("fan") run.stop() }}

复制代码

本次测试两种服务状态,一种无延迟 HTTP 服务,另外一种是低延迟(5ms 以及 10ms),总计三种 HTTP 服务。由于 Go 语言 HTTP 的库自带了 HTTP 服务开发功能,后面我会再写一篇文章,对比一下三种 HTTP 服务的性能:Java netty、Go(net/http)以及 Go(/valyala/fasthttp)。

测试用例

FunTester

FunTester 测试框架用的是 Java HttpClient,对 HttpClient API 做了封装,然后配合 FunTester 性能测试框架完成本次测试。实测中封装和框架对性能影响可忽略。

class HttpClientTest extends FunLibrary {
static final String uri = "http://localhost:12345/test/fun" static final HttpRequestBase get = FunLibrary.getHttpGet(uri)
static final int thread = 10 static final int times = 10000
public static void main(String[] args) { RUNUP_TIME = 0 def tester = new FunTester() new Concurrent(tester, thread, DEFAULT_STRING).start() }
private static class FunTester extends FixedThread<HttpRequestBase> {
FunTester() { super(get, times, true) }
@Override protected void doing() throws Exception { FunLibrary.executeOnly(get) }
@Override FixedThread clone() { return new FunTester() } }
}

复制代码

Go(net/http)

这里我写了一个测试方法,使用了 Go 语言的协程和 chan 知识点,比较粗糙,但能用。代码时有改动,后台回复 git 可以获取多个项目的 git 仓库地址,包含本项目。

var key bool = false
const ( url = "http://localhost:8001/test/fun" thread = 20 times = 10000)
func TestPer(t *testing.T) { get := funtester.Get(url, nil) c := make(chan int)
start := time.Now().UnixMilli() for i := 0; i < thread; i++ { go func() { sum := 0 for i := 0; i < times; i++ { if key { break } funtester.Response(get) sum++ } key = true c <- sum }() } total := 0 for i := 0; i < thread; i++ { num := <-c total += num } end := time.Now().UnixMilli() diff := end - start log.Printf("总耗时: %f", float64(diff)/1000)
log.Printf("请求总数: %d", total) log.Printf("QPS: %f", float64(total)/float64(diff)*1000.0)
}

复制代码

Go(/valyala/fasthttp)

与 net/http 类似,不同之处是/valyala/fasthttp 无法使用同一个对象进行压测。所以每次都要创建一个对象,但是实测居然效率更高,fasthttp 对象池果然牛。而且传说中 10 倍于 net/http,着实有点吹牛了。

var key bool = false
const ( url = "http://localhost:8001/test/fun" thread = 20 times = 10000)
func TestPerFast(t *testing.T) { c := make(chan int) start := time.Now().UnixMilli() for i := 0; i < thread; i++ { go func() { sum := 0 for i := 0; i < times; i++ { if key { break } get := funtester.FastGet(url, nil) funtester.FastResponse(get) sum++
} key = true c <- sum }() } total := 0 for i := 0; i < thread; i++ { num := <-c total += num } end := time.Now().UnixMilli() diff := end - start //total := thread * times log.Printf("总耗时: %f", float64(diff)/1000)
log.Printf("请求总数: %d", total) log.Printf("QPS: %f", float64(total)/float64(diff)*1000.0)
}

复制代码

测试

无延迟服务

截止到此,CPU 已经基本满荷运行,实际 80%,不知道是不是 macOS 的限制,现在 CPU 只能跑到 80%左右。

实际测试结果非常明显,总体 CPU 指标, FunTester/valyala/fasthttp 相差不多, /valyala/fasthttp 在 CPU 方面有些许优势,但是在内存上,简直无法理解。见鬼了一样。相比之下 net/http 逊色很多,在低并发的时候除了内存表现较好意外,CPU 和 QPS 均低于 FunTester/valyala/fasthttp ,但是在 10 线程的情况下,CPU 跑满,内存直线飙升,着实无法理解。等我再深入学习之后,估计能明白这个问题了。

延迟 5ms 服务

结论跟无延迟服务差不多。总体讲 /valyala/fasthttp > FunTester > net/http 。即使 Go 语言加成, net/http 除了内存以外,其他两项指标均不如 Java 写的 FunTester。

结论

/valyala/fasthttp 真心牛逼,建议使用 Go 语言进行 HTTP 性能测试的,直接跳过 net/http

PS:下一次我将测试三种 HTTP 服务端的性能,敬请期待。

「欢迎关注 FunTester,Have Fun ~ Tester !」