基本http服务性能测试(Python vs Golang)
最近学习Golang,总想体验下并发到底有多叼,必我大 python强势多少。 学习了官方教程的http 服务,用性能测试工具wrk测试了下,发现结果很令人惊讶~
wrk可以参考我的博客,有基本用法说明:http://blog.yuanzhaoyi.cn/2018/01/12/test.html
测试命令:wrk -t10 -d1m -c200 http://127.0.0.1:8080
含义:10线程,并发200链接,持续1分钟
http服务均返回基本的: "Hello World",应该不会有IO阻塞
Python 标准库BaseHTTPRequestHandler实现:
from http.server import BaseHTTPRequestHandler from urllib import parse class GetHandler(BaseHTTPRequestHandler): def do_GET(self): message = "Hello World" self.send_response(200) self.end_headers() self.wfile.write(message.encode('utf-8')) if __name__ == '__main__': from http.server import HTTPServer server = HTTPServer(('localhost', 8080), GetHandler) print('Starting server, use <Ctrl-C> to stop') server.serve_forever()
结果:每秒响应数量只有282个,测试时间越长会越低
因为是但进程,单线程,这个数据应该不错了,虽然GIL在io阻塞会释放线程,但也有一点性能消耗
Running 1m test @ http://127.0.0.1:8080 10 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 2.05ms 6.73ms 265.58ms 98.90% Req/Sec 107.11 103.19 1.05k 84.08% 16959 requests in 1.00m, 1.65MB read Socket errors: connect 0, read 19024, write 59, timeout 0 Requests/sec: 282.21 Transfer/sec: 28.11KB
异步框架为了方便,我们先用基于twisted的事件循环的tornado:
import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") if __name__ == "__main__": application = tornado.web.Application([ (r"/", MainHandler), ]) application.listen(8080) tornado.ioloop.IOLoop.current().start()
结果:每秒响应数量有1300多个,明显好很多
Running 1m test @ http://127.0.0.1:8080 10 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 147.44ms 45.17ms 467.54ms 86.25% Req/Sec 141.40 57.52 202.00 65.17% 81818 requests in 1.00m, 16.15MB read Socket errors: connect 0, read 1, write 0, timeout 0 Requests/sec: 1361.25 Transfer/sec: 275.17KB
Python3开始支持原生的协程来处理事件循环,虽然tornado也支持,但为了方便,直接用号称最快的sanic测试吧
from sanic import Sanic from sanic.response import json app = Sanic() @app.route("/") async def test(request): return json({"hello": "world"}) if __name__ == "__main__": app.run(host="0.0.0.0", debug=False, port=8080)
结果:每秒响应数量达到4400多了,看起来很不错了
Running 1m test @ http://127.0.0.1:8080 10 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 45.59ms 16.91ms 255.88ms 71.70% Req/Sec 443.64 111.85 0.89k 68.56% 264956 requests in 1.00m, 32.09MB read Socket errors: connect 0, read 67, write 0, timeout 0 Requests/sec: 4408.87 Transfer/sec: 546.80KB
最近学习了GoLang,就基于官方指南的http服务进行了基本测试:
package main import ( "fmt" "log" "net/http" ) type Hello struct{} func (h Hello) ServeHTTP( w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello World!") } func main() { h := Hello{} err := http.ListenAndServe("localhost:8080", h) if err != nil { log.Fatal(err) } }
结果也是让我大开眼界:每秒响应数量达到了35365,和python服务都不是一个量级
Running 1m test @ http://127.0.0.1:8080 10 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 7.26ms 9.46ms 210.93ms 93.36% Req/Sec 3.56k 1.10k 19.98k 74.96% 2125366 requests in 1.00m, 261.47MB read Requests/sec: 35365.98 Transfer/sec: 4.35MB
简单总结下,测试中,内存都没有明显增长,必定没有什么操作,仅仅返回一个字符串。但是GoLang的CPU使用率明显增长,Sanic服务也有差不多的增长,其余CPU使用率增长幅度不大,但Python的服务应该都只用了CPU都一核心。但当CPU占比差不多的时候,GoLang的响应能力明显要更胜一筹。具体原因思考,除了CPU的核心使用区别之外,即真正并行的实现外,好像也没什么了。Python的异步服务和GoLang的服务应该都基于事件循环实现了协程的调度,当然实现方法肯定有很大的不同,具体还要继续学习了。不过GoLang天生并发的支持,的确对此优化的很不错。
这几个测试都是基于好奇,比较简单也不够严谨,但我觉得可以说明一些区别。如果发现什么问题,欢迎留言。