关于上一版本
昨天完成了nodejs版本的图片服务器staticserv
之后,当时没有对请求的类型做判断,这只是我自己使用,如果是做成通用模块,需要考虑防范不合适的请求。之前,
首先考虑的是Go版本的服务器,只不过自己没有弄清go的资源地址都是相对于项目的GOPATH的地址,还是使用老方法\a\b\c.txt
去读取文件,结果都无法读到文件,所以写了nodejs版本。今天学习了path/filepath
模块之后,终于掌握
了正确写法。比如要读取”./a/b.txt”,需要这么写:1
2absPath:_:=filepath.Abs("./a/b.txt")
bytes,err:=ioutil.ReadFile(absPath)
Go版本的主要功能
- 自定义资源文件位置
主要是通过flag
模块解析src参数来实现,比如1
2
3
4
5./serv -src /tmp
请求URL:
http://xxx/123.jpg
最终服务器查找的文件地址:
/tmp/123.jpg
测试中对于”//“这种错误也能兼容,因为它使用的是Join转化路径,而不是拼接字符串。
关键代码如下:1
2relativePath := req.URL.Path
filename, err := filepath.Abs(filepath.Join(*curDir, relativePath))
- 判断请求类型
这部分主要有忽略”favicon.ico”请求、判断路径是否正确和文件是否存在1
2
3
4//忽略favicon请求:
if relativePath == "/favicon.ico" {
return
}
路径是否正确直接判断上文中的err就好了,这里就不表述。实验时发现请求的是目录,服务
器立马输出错误,然后退出了。这怎么行,一点都不健壮,所以要判断是不是文件:1
2
3
4
5
6
7
8
9
10
11
12fileinfo, e := os.Stat(filename)
//文件不存在
if os.IsNotExist(e) {
log.Println(filename, " is not exists.")
res.WriteHeader(404)
return
}
//不是文件,返回500错误
if fileinfo.IsDir() {
res.WriteHeader(500)
return
}
- 判断文件类型并读取、发送文据
判断文件类型主要是为了响应时的content-type
,对于不是图片的请求,直接返回了text/plain
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15extname := filepath.Ext(reqfile)
switch extname {
case ".jpg", ".jpeg", ".jpe", ".jfif":
return "image/jpeg"
case ".png":
return "image/png"
case ".ico":
return "image/x-icon"
case ".gif":
return "image/gif"
case ".tif", ".tiff":
return "image/jiff"
default:
return "text/plain"
}
发送数据就比较简单了,为了不在传递参数时拷贝,我用了拷贝指针的办法:1
2
3
4
5var data []byte
//读取文件
ReadFileFromPath(&data, filename)
res.WriteHeader(200)
res.Write(data)
与上一版本的性能测试对比
这里主要用到了apache的ab工具,以及开源软件webbench。
两者都在同一环境下,均没有做优化。
- ab测试,并发请求200,总请求数4000:
1
ab -c 200 -n 4000 http://localhost:8080/fact10000.png
测试结果如下:
go 版本: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
33
34
35
36
37
Document Path: /img/fact10000.png
Document Length: 27488 bytes
Concurrency Level: 200
Time taken for tests: 2.493 seconds
Complete requests: 4000
Failed requests: 0
Total transferred: 110276000 bytes
HTML transferred: 109952000 bytes
Requests per second: 1604.80 [#/sec] (mean)
Time per request: 124.626 [ms] (mean)
Time per request: 0.623 [ms] (mean, across all concurrent requests)
Transfer rate: 43205.78 [Kbytes/sec] received //网络传输速度,排除网路问题
/*网络上消耗的时间的分解*/
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 4 39.6 0 1003
Processing: 3 117 143.3 56 1184
Waiting: 2 65 41.9 50 173
Total: 4 121 147.3 57 1184
*下面的内容为整个场景中所有请求的响应情况。在场景中每个请求都有一个响应时间,
其中 50% 的用户响应时间小于 57 毫秒,66 % 的用户响应时间小于 100 毫秒,
最大的响应时间小于 1184 毫秒*/
Percentage of the requests served within a certain time (ms)
50% 57
66% 109
75% 150
80% 157
90% 290
95% 406
98% 583
99% 739
100% 1184 (longest request)
nodejs版本: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
28Concurrency Level: 200
Time taken for tests: 3.019 seconds
Complete requests: 4000
Failed requests: 0
Total transferred: 110352000 bytes
HTML transferred: 109952000 bytes
Requests per second: 1325.07 [#/sec] (mean)
Time per request: 150.935 [ms] (mean)
Time per request: 0.755 [ms] (mean, across all concurrent requests)
Transfer rate: 35699.34 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 24 150.8 0 1003
Processing: 67 119 50.4 103 420
Waiting: 67 118 50.3 103 420
Total: 71 143 156.3 105 1116
Percentage of the requests served within a certain time (ms)
50% 105
66% 125
75% 141
80% 145
90% 160
95% 303
98% 1093
99% 1099
100% 1116 (longest request)
二者情况是差不多的,go略微好于nodejs。
- 连续3次请求测试平均值,选取用户最关心的三项参数
go:1
2
3
4
5
6
7
8
9
10
11Requests per second: 1604.80 [#/sec] (mean) //吞吐率
Time per request: 124.626 [ms] (mean) //用户平均请求等待时间
Time per request: 0.623 [ms] (mean, across all concurrent requests) //服务器平均请求等待时间
Requests per second: 1627.66 [#/sec] (mean)
Time per request: 122.876 [ms] (mean)
Time per request: 0.614 [ms] (mean, across all concurrent requests)
Requests per second: 1747.35 [#/sec] (mean)
Time per request: 114.459 [ms] (mean)
Time per request: 0.572 [ms] (mean, across all concurrent requests)
nodejs:1
2
3
4
5
6
7
8
9
10
11Requests per second: 1325.07 [#/sec] (mean)
Time per request: 150.935 [ms] (mean)
Time per request: 0.755 [ms] (mean, across all concurrent requests)
Requests per second: 1156.69 [#/sec] (mean)
Time per request: 172.908 [ms] (mean)
Time per request: 0.865 [ms] (mean, across all concurrent requests)
Requests per second: 1231.65 [#/sec] (mean)
Time per request: 162.383 [ms] (mean)
Time per request: 0.812 [ms] (mean, across all concurrent requests)
可以看出二者在吞吐率上,Go好于nodejs,平均等待时间上二者相差无几。
- webbench 压力测试
压力测试30秒内,并发请求数200,连续测试3次1
webbench -t 30 -c 200 http://127.0.0.1:8080/img/fact10000.png
go的表现:1
2
3
4
5
6
7
8
9Speed=111574 pages/min, 51317124 bytes/sec. //速度,111574页/每分钟,51317124字节/秒
Speed=100538 pages/min, 46228920 bytes/sec.
Speed=103186 pages/min, 47440784 bytes/sec.
资源占用:
VIRT RES SHR CPU MEM TIME
364756 26252 4336 S 61.9 0.0 0:15.26 serv
364884 25960 4044 S 58.7 0.0
364884 25960 4044 R 57.9 0.0
nodejs的表现:1
2
3
4
5
6
7
8
9Speed=78864 pages/min, 36259112 bytes/sec.
Speed=42868 pages/min, 19710708 bytes/sec.
Speed=36340 pages/min, 16708262 bytes/sec.
资源占用:
VIRT RES SHR CPU MEM TIME
955320 74196 14252 R 73.6 0.1 0:10.49 node
960768 79408 14252 R 33.2 0.1
961604 79256 14252 R 34.7 0.1
二者都是0错误,可以看出nodejs在速度和资源占用率都比Go表现的差,
速度大概只有go的一半不到,内存占用率却是go的3倍左右,CPU使用率
稍好于go。
总结
从测试里可以看到,Go的http性能是好于nodejs的,不过对于这么
简单的应用,可能无法完全反应Go和nodejs的http性能高低吧,但是go的
http的模块我觉得很好用,写代码也是同步方式,不像nodejs我写到逻辑越来
越复杂的时候就有点越发要好好学一学异步编程的思想,这种思维转换一时还
无法适应,所以我往往选择javascript先完成一部分功能,打开思路后使用Go
去实现。
另外今天的收获就是学会了做网络测试了,这对我来说又增长了能力。