Python、Java、GoLang 基于 Web 的性能测试

最近一段时间学习了一下 Go 这门语言,其中提到最多的就是 GoLang 的高性能 & 高并发,所以本着没有对比就没有伤害的原则,我准备将其与另外两个我所掌握的语言(Python、Java)进行一个简单的性能对比。

测试环境

我的 MacBook Pro,12个逻辑CPU + 16G内存

测试工具

https://github.com/wg/wrk

wrk -t8 -c100 -d30s --latency http://www.baidu.com

模拟8线程、100个并发,持续30秒的性能测试

实现

以下程序完整源码已放在 GitHub:https://github.com/Panmax/web-benchmark

Python

框架:Flask
容器:Gunicorn
运行环境:Docker

核心代码:

1
2
3
4
5
6
7
8
9
10
11
# -*- coding: utf-8 -*-

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
return "Hello Python!"

if __name__ == '__main__':
app.run()

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
FROM ubuntu:14.04

ADD sources.list /etc/apt/sources.list
ADD pip.conf ~/.pip/pip.conf

# Update OS
# RUN sed -i 's/# \(.*multiverse$\)/\1/g' /etc/apt/sources.list
RUN apt-get update
RUN apt-get -y upgrade

# Install Python
RUN apt-get install -y python-dev python-pip

# Add requirements.txt
ADD requirements.txt /webapp/requirements.txt

# Install gunicorn Python web server
RUN pip install gunicorn==19.6.0
# Install app requirements
RUN pip install -r /webapp/requirements.txt

# Create app directory
ADD . /webapp

# Set the default directory for our environment
ENV HOME /webapp
WORKDIR /webapp

# Expose port 5000 for gunicorn
EXPOSE 5000

ENTRYPOINT ["gunicorn", "-w", "24", "wsgi:app", "-b", "0.0.0.0:5000", "-n", "docker-flask", "--timeout", "45", "--max-requests", "10000"]

这里设置 24 个 worker,因为我的机器有 12 个逻辑CPU

启动命令

1
2
docker build -t panmax/docker-flask-benchmark .
docker run -d --name docker-flask-benchmark --restart=always -p 8081:5000 panmax/docker-flask-benchmark

Java

框架:SpringBoot

容器采用 SpringBoot 的默认 tomcat 容器,不进行其他修改。

核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.jpanj.benchmark;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class BenchmarkApplication {

public static void main(String[] args) {
SpringApplication.run(BenchmarkApplication.class, args);
}

@GetMapping
public String hello() {
return "Hello Java!";
}

}

配置文件

1
2
server:
port: 8082

启动命令

1
2
3
4
./gradlew build -xtest
cd build/libs

java -jar benchmark-0.0.1-SNAPSHOT.jar

GoLang

框架:Gin

不需要配置任何容器

核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"github.com/gin-gonic/gin"
"net/http"
)

func main() {
router := gin.Default()
router.GET("", func(c *gin.Context) {

c.String(http.StatusOK, "Hello GoLang!")
})
router.Run(":8083")
}

启动命令

1
2
go build .
./gin-benchmark

go build 可以直接编译出一个可以执行文件,这个二进制文件可以直接放在其他机器上无需安装任何环境就可以运行起来,甚至可以在 Mac 上编译 Linux / Windows 的可执行文件,在 Linux 上编译 Mac / Windows 的可执行文件,这个特性非常爽。


通过浏览器可以验证以上使用 3 种语言开发的简单 Web 程序已经启起来了:

接下来我们逐个进行性能测试:

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
➜ wrk -t8 -c100 -d30s --latency http://127.0.0.1:8081/
Running 30s test @ http://127.0.0.1:8081/
8 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 21.45ms 12.75ms 237.13ms 85.43%
Req/Sec 332.10 111.19 640.00 71.40%
Latency Distribution
50% 19.20ms
75% 26.01ms
90% 33.23ms
99% 90.03ms
15917 requests in 30.08s, 2.63MB read
Socket errors: connect 0, read 560, write 0, timeout 0
Requests/sec: 529.21
Transfer/sec: 89.41KB

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
➜ wrk -t8 -c100 -d30s --latency http://127.0.0.1:8082/
Running 30s test @ http://127.0.0.1:8082/
8 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 8.22ms 26.99ms 438.58ms 93.31%
Req/Sec 6.93k 3.00k 16.38k 50.11%
Latency Distribution
50% 1.26ms
75% 2.09ms
90% 12.81ms
99% 132.29ms
1631200 requests in 30.06s, 194.75MB read
Requests/sec: 54256.97
Transfer/sec: 6.48MB

GoLang

1
2
3
4
5
6
7
8
9
10
11
12
13
14
➜ wrk -t8 -c100 -d30s --latency http://127.0.0.1:8083/
Running 30s test @ http://127.0.0.1:8083/
8 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.80ms 1.95ms 24.69ms 85.85%
Req/Sec 8.68k 786.97 11.03k 65.72%
Latency Distribution
50% 1.36ms
75% 2.68ms
90% 4.32ms
99% 8.42ms
2078830 requests in 30.10s, 257.73MB read
Requests/sec: 69064.35
Transfer/sec: 8.56MB

可以看到,在每秒请求数量(Requests/sec),也就是并发能力方面,测试结果为:

  • Python: 529.21
  • Java: 54256.97
  • GoLang: 69064.35

线程平均延迟(Thread Stats - Avg - Latency)的测试结果为:

  • Python: 21.45ms
  • Java: 8.22ms
  • GoLang: 1.80ms

可以看出,Go 在性能方面甩出 Python 几十条街是没有问题的,比 Java 的性能确实也好很多。

最后说明一下,这个测试可能存在不严谨性,但是我所采用的部署方案是大部分公司或者程序员最常使用的方式,也能在一定程度上说明问题。