[Nginx上传]对Nginx上传进行代码跟踪时对glibc的 Asynchronous I/O 写性能测试这nginx上传模块研究使用。
背景:近来对Nginx的上传模块这一块,Flash上传时进度条都过了,但是那个文件还没有出现,一会儿后才出现那个文件,并慢慢增大,我用的是Nginx的上传插件,这种问题怎么去查?我刚用了strace,我在想它是把上传的东东放在哪儿去了呢?
http://www.grid.net.ru/nginx/download/nginx_upload_module-2.2.0.tar.gz
Rango 上午 11:56:57
用apache来做吧,apache是交给php来处理的,
回忆未来-向东-Jàck 下午 12:01:57
嗯,我再看看,兄弟觉得Apache下的PHP比起Nginx的PHP在上传一块有更高的效率是么?
Rango 下午 12:02:23
都差不多吧
apache比较成熟
=================================================================================
Rango 下午 01:45:42
你看下 man pwrite
回忆未来-向东-Jàck 下午 01:55:19
好看到了,这个PHP上传大文件和Nginx上传大文件,是先接进来放到内存后,在写到文件里吧?
Rango 下午 01:55:30
嗯,是的,都是这样做了
pwrite就是相当于 fseek + fwrite
回忆未来-向东-Jàck 下午 01:56:55
至于内存里放在什么时候开始写,那应该是由nginx程序里的配置来实现的吧?
如果我猜测没错的话
=================================================================================
upload_buffer_size 上传缓冲区大小
upload_max_file_size 指定上传文件最大大小,软限制。client_max_body_size硬限制。
upload_limit_rate 上传限速,如果设置为0则表示不限制。
upload_pass_args 是否转发参数。
=============================================================================
=============================================================================
ack 1460:
第一个报文的TCP头里通过MSS这个可选项告知对方本端能够接收的最大报文(当然,这个大小是TCP净荷的大小),以太网上这个值一般设置成1460,因为1460Byte净荷+20Byte TCP头+20Byte IP头 = 1500字节,正好符合链路层最大报文的要求。
http://wenku.baidu.com/view/d77645d533d4b14e84246800.html
strace -p 7269 -o s.log
Nginx太多了进程:
[root@test tmp]# cat strace.sh
都是读4096个字符一次写,pwrite64和这个相当应该:
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
原型 char * fgets(char * s, int n,FILE *stream);
参数:
s: 字符型指针,指向存储读入数据的缓冲区的地址。
n: 从流中读入n-1个字符
stream : 指向读取的流。
函数原型
ssize_t pwrite(intfd, const void *buf, size_tcount, off_toffset);
编辑本段
用法
返回值:成功,返回写入到文件中的字节数;失败,返回-1;
参数:
(1) fd:要写入数据的文件描述符
(2) buf:数据缓存区指针,存放要写入文件中的数据
(3) count:写入文件中的数据的字节数
(4) offset:写入起始地址的偏移量,写入地址=文件开始+offset。注意,执行后,文件偏移指针不变
什么是字节,字节数相关学习备案:
字节(Byte 发音:/‘bait/):字节是通过网络传输信息(或在硬盘或内存中存储信息)的单位。在ASCII码中,一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间。符号:英文标点占一个字节,中文标点占两个字节。举例:英文句号“.”占1个字节的大小,中文句号“。”占2个字节的大小 。一个二进制数字序列,在计算机中作为一个数字单元,一般为8位二进制数,换算为十进制。最小值:0 最大值:255 。如一个ASCII码就是一个字节,此类单位的换算为: 1KB(Kilobyte 千字节)=1024B,1MB(Megabyte 兆字节 简称“兆”)=1024KB,1GB(Gigabyte 吉字节 又称“千兆”)=1024MB,
数据包是信息在通行通道上传递的形式~~
字节是信息的单位~~一字节等于8个比特位!
wc - c 统计字节数:
[root@test ~]# cat a.txt |wc -c
2 [里面写:a的字节数]
[root@test ~]# vi a.txt
[root@test ~]# cat a.txt |wc -c
8 [里面写:a向东 的字节数]
比特(bit)即一个二进制位 例如100011就是6比特
字节(byte)这是计算机中数据类型最基本的单位了,8bit 组成1byte
字(word)两个byte称为一个word,所以字大小应该是16位bit,共两字节。(Shell里的)
双字(double word 简写为DWORD)见名知意,两个字,四个字节,32bit。
C语言:char 字符型 占1byte 即8位,一个char型数据(例如:a、#、!之类的)用了1个字节来存储:
root@192.168.137.128:~# ./a.out
size of char a=1
size of char =1
中间断开测试,浏览器突然断开:
accept4(5, {sa_family=AF_INET, sin_port=htons(58438), sin_addr=inet_addr("192.168.137.1")}, [16], SOCK_NONBLOCK) = 3
epoll_ctl(13, EPOLL_CTL_ADD, 3, {EPOLLIN|EPOLLET, {u32=3039736117, u64=722997274181808437}}) = 0
epoll_wait(13, {{EPOLLIN, {u32=3039736117, u64=722997274181808437}}}, 512, 60000) = 1
gettimeofday({1368072732, 443326}, NULL) = 0
recv(3, "POST /upload HTTP/1.1\r\nHost: tes"..., 32768, 0) = 32768
recv(3, "\0\1\0\0*0\0\0\0\2\0\0\0\0\0\0\0\1\0\0*0\0\0\0\2\0\0\0\0\0\0"..., 8192, 0) = 8192
epoll_wait(13, {{EPOLLIN, {u32=3039736117, u64=722997274181808437}}}, 512, 7) = 1
gettimeofday({1368072732, 446037}, NULL) = 0
epoll_wait(13, {}, 512, 4) = 0
gettimeofday({1368072732, 451445}, NULL) = 0
open("/data/app/test.local/upload_tmp/0000000005", O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0600) = 4
pwrite64(4, "\0\0\0\24ftypisom\0\0\0\1isom\0\rx\373moov\0\0\0l"..., 4096, 0) = 4096
pwrite64(4, "\0\2\0\0\0\0\0\0\0\3\0\0\16\20\0\0\0\1\0\0*0\0\0\0\2\0\0\0\0\0\0"..., 4096, 4096) = 4096
pwrite64(4, "\0\1\0\0*0\0\0\0\2\0\0\0\0\0\0\0\1\0\0*0\0\0\0\2\0\0\0\0\0\0"..., 4096, 8192) = 4096
.........................................
pwrite64(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096, 7622656) = 4096
pwrite64(4, "e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\240\5\343\t\240\335d\t@\327d\t\240.\341\t"..., 4096, 7626752) = 4096
recv(3, 0xa0a9048, 8192, 0) = -1 EAGAIN (Resource temporarily unavailable)
epoll_wait(13, {{EPOLLIN|EPOLLERR|EPOLLHUP, {u32=3039736117, u64=722997274181808437}}}, 512, 60000) = 1
gettimeofday({1368072745, 256485}, NULL) = 0
recv(3, 0xa0a9048, 8192, 0) = -1 ECONNRESET (Connection reset by peer)
pwrite64(4, "s\0i\0t\0o\0r\0y\0\0\0\0\0\36\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0"..., 2800, 7630848) = 2800
close(4) = 0
unlink("/data/app/test.local/upload_tmp/0000000005") = 0
write(9, "2013/05/09 12:12:25 [alert] 7269"..., 240) = 240
close(3) = 0
epoll_wait(13, <unfinished ...>
测试主机是AIX6.1
测试数据是向文件中顺序写入 8K * 50000 大小的数据,生成的文件大约为390M
测试对比了两种写操作,一种是aio_write, 另一种是 pwrite64; dwFlags 是open文件时,传入的参数
第一种测试
dwFlags = O_RDWR | O_LARGEFILE | O_DIRECT | O_CREAT | O_EXCL;
pwrite64 时间 : 237 秒
aio_write 时间: 236 秒
第二种测试
dwFlags = O_RDWR | O_LARGEFILE | O_SYNC | O_CREAT | O_EXCL;
pwrite64 时间 : 560秒
aio_write 时间: 567秒
第三种测试
dwFlags = O_RDWR | O_LARGEFILE | O_DIRECT | O_SYNC | O_CREAT | O_EXCL;
pwrite64 时间 : 568秒
aio_write 时间: 569秒
由于测试只是测试写操作,而且是顺序写,没有随机写;另一方面,没有在写操作中,加入写读作;还有就是只是对单一文件的写操作,不是多线程同时对多个文件的操作,所以测试还有很多局限性,更详细的测试需要进一步完成,还需测试不同页面大小时对性能的影响
从上面这三种测试来看,
第一,当 dwFlags 相同时, 在单文件上面,同步写(pwrite64 )和异步写(aio_write )差距不大,主要原因估计是glibc的底层实现也是通过pwrite64 实现的,只不过加了一个多线程,也许对多个文件时,会有一些差别。
第二,O_DIRECT 比 O_SYNC 比较大的提高写性能,快了一半,
第三,O_SYNC 与 O_DIRECT | O_SYNC 几乎相同,
下面是测试用的程序
来源:http://5itest.net/bbs/TopicOther.asp?t=5&BoardID=16&id=1014
http://www.grid.net.ru/nginx/download/nginx_upload_module-2.2.0.tar.gz
Rango 上午 11:56:57
用apache来做吧,apache是交给php来处理的,
回忆未来-向东-Jàck 下午 12:01:57
嗯,我再看看,兄弟觉得Apache下的PHP比起Nginx的PHP在上传一块有更高的效率是么?
Rango 下午 12:02:23
都差不多吧
apache比较成熟
=================================================================================
Rango 下午 01:45:42
你看下 man pwrite
回忆未来-向东-Jàck 下午 01:55:19
好看到了,这个PHP上传大文件和Nginx上传大文件,是先接进来放到内存后,在写到文件里吧?
Rango 下午 01:55:30
嗯,是的,都是这样做了
pwrite就是相当于 fseek + fwrite
回忆未来-向东-Jàck 下午 01:56:55
至于内存里放在什么时候开始写,那应该是由nginx程序里的配置来实现的吧?
如果我猜测没错的话
=================================================================================
upload_buffer_size 上传缓冲区大小
upload_max_file_size 指定上传文件最大大小,软限制。client_max_body_size硬限制。
upload_limit_rate 上传限速,如果设置为0则表示不限制。
upload_pass_args 是否转发参数。
=============================================================================
=============================================================================
ack 1460:
第一个报文的TCP头里通过MSS这个可选项告知对方本端能够接收的最大报文(当然,这个大小是TCP净荷的大小),以太网上这个值一般设置成1460,因为1460Byte净荷+20Byte TCP头+20Byte IP头 = 1500字节,正好符合链路层最大报文的要求。
http://wenku.baidu.com/view/d77645d533d4b14e84246800.html
strace -p 7269 -o s.log
Nginx太多了进程:
[root@test tmp]# cat strace.sh
都是读4096个字符一次写,pwrite64和这个相当应该:
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
原型 char * fgets(char * s, int n,FILE *stream);
参数:
s: 字符型指针,指向存储读入数据的缓冲区的地址。
n: 从流中读入n-1个字符
stream : 指向读取的流。
函数原型
ssize_t pwrite(intfd, const void *buf, size_tcount, off_toffset);
编辑本段
用法
返回值:成功,返回写入到文件中的字节数;失败,返回-1;
参数:
(1) fd:要写入数据的文件描述符
(2) buf:数据缓存区指针,存放要写入文件中的数据
(3) count:写入文件中的数据的字节数
(4) offset:写入起始地址的偏移量,写入地址=文件开始+offset。注意,执行后,文件偏移指针不变
什么是字节,字节数相关学习备案:
字节(Byte 发音:/‘bait/):字节是通过网络传输信息(或在硬盘或内存中存储信息)的单位。在ASCII码中,一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间。符号:英文标点占一个字节,中文标点占两个字节。举例:英文句号“.”占1个字节的大小,中文句号“。”占2个字节的大小 。一个二进制数字序列,在计算机中作为一个数字单元,一般为8位二进制数,换算为十进制。最小值:0 最大值:255 。如一个ASCII码就是一个字节,此类单位的换算为: 1KB(Kilobyte 千字节)=1024B,1MB(Megabyte 兆字节 简称“兆”)=1024KB,1GB(Gigabyte 吉字节 又称“千兆”)=1024MB,
数据包是信息在通行通道上传递的形式~~
字节是信息的单位~~一字节等于8个比特位!
wc - c 统计字节数:
[root@test ~]# cat a.txt |wc -c
2 [里面写:a的字节数]
[root@test ~]# vi a.txt
[root@test ~]# cat a.txt |wc -c
8 [里面写:a向东 的字节数]
比特(bit)即一个二进制位 例如100011就是6比特
字节(byte)这是计算机中数据类型最基本的单位了,8bit 组成1byte
字(word)两个byte称为一个word,所以字大小应该是16位bit,共两字节。(Shell里的)
双字(double word 简写为DWORD)见名知意,两个字,四个字节,32bit。
C语言:char 字符型 占1byte 即8位,一个char型数据(例如:a、#、!之类的)用了1个字节来存储:
root@192.168.137.128:~# ./a.out
size of char a=1
size of char =1
中间断开测试,浏览器突然断开:
accept4(5, {sa_family=AF_INET, sin_port=htons(58438), sin_addr=inet_addr("192.168.137.1")}, [16], SOCK_NONBLOCK) = 3
epoll_ctl(13, EPOLL_CTL_ADD, 3, {EPOLLIN|EPOLLET, {u32=3039736117, u64=722997274181808437}}) = 0
epoll_wait(13, {{EPOLLIN, {u32=3039736117, u64=722997274181808437}}}, 512, 60000) = 1
gettimeofday({1368072732, 443326}, NULL) = 0
recv(3, "POST /upload HTTP/1.1\r\nHost: tes"..., 32768, 0) = 32768
recv(3, "\0\1\0\0*0\0\0\0\2\0\0\0\0\0\0\0\1\0\0*0\0\0\0\2\0\0\0\0\0\0"..., 8192, 0) = 8192
epoll_wait(13, {{EPOLLIN, {u32=3039736117, u64=722997274181808437}}}, 512, 7) = 1
gettimeofday({1368072732, 446037}, NULL) = 0
epoll_wait(13, {}, 512, 4) = 0
gettimeofday({1368072732, 451445}, NULL) = 0
open("/data/app/test.local/upload_tmp/0000000005", O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0600) = 4
pwrite64(4, "\0\0\0\24ftypisom\0\0\0\1isom\0\rx\373moov\0\0\0l"..., 4096, 0) = 4096
pwrite64(4, "\0\2\0\0\0\0\0\0\0\3\0\0\16\20\0\0\0\1\0\0*0\0\0\0\2\0\0\0\0\0\0"..., 4096, 4096) = 4096
pwrite64(4, "\0\1\0\0*0\0\0\0\2\0\0\0\0\0\0\0\1\0\0*0\0\0\0\2\0\0\0\0\0\0"..., 4096, 8192) = 4096
.........................................
pwrite64(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096, 7622656) = 4096
pwrite64(4, "e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\240\5\343\t\240\335d\t@\327d\t\240.\341\t"..., 4096, 7626752) = 4096
recv(3, 0xa0a9048, 8192, 0) = -1 EAGAIN (Resource temporarily unavailable)
epoll_wait(13, {{EPOLLIN|EPOLLERR|EPOLLHUP, {u32=3039736117, u64=722997274181808437}}}, 512, 60000) = 1
gettimeofday({1368072745, 256485}, NULL) = 0
recv(3, 0xa0a9048, 8192, 0) = -1 ECONNRESET (Connection reset by peer)
pwrite64(4, "s\0i\0t\0o\0r\0y\0\0\0\0\0\36\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0"..., 2800, 7630848) = 2800
close(4) = 0
unlink("/data/app/test.local/upload_tmp/0000000005") = 0
write(9, "2013/05/09 12:12:25 [alert] 7269"..., 240) = 240
close(3) = 0
epoll_wait(13, <unfinished ...>
测试主机是AIX6.1
测试数据是向文件中顺序写入 8K * 50000 大小的数据,生成的文件大约为390M
测试对比了两种写操作,一种是aio_write, 另一种是 pwrite64; dwFlags 是open文件时,传入的参数
第一种测试
dwFlags = O_RDWR | O_LARGEFILE | O_DIRECT | O_CREAT | O_EXCL;
pwrite64 时间 : 237 秒
aio_write 时间: 236 秒
第二种测试
dwFlags = O_RDWR | O_LARGEFILE | O_SYNC | O_CREAT | O_EXCL;
pwrite64 时间 : 560秒
aio_write 时间: 567秒
第三种测试
dwFlags = O_RDWR | O_LARGEFILE | O_DIRECT | O_SYNC | O_CREAT | O_EXCL;
pwrite64 时间 : 568秒
aio_write 时间: 569秒
由于测试只是测试写操作,而且是顺序写,没有随机写;另一方面,没有在写操作中,加入写读作;还有就是只是对单一文件的写操作,不是多线程同时对多个文件的操作,所以测试还有很多局限性,更详细的测试需要进一步完成,还需测试不同页面大小时对性能的影响
从上面这三种测试来看,
第一,当 dwFlags 相同时, 在单文件上面,同步写(pwrite64 )和异步写(aio_write )差距不大,主要原因估计是glibc的底层实现也是通过pwrite64 实现的,只不过加了一个多线程,也许对多个文件时,会有一些差别。
第二,O_DIRECT 比 O_SYNC 比较大的提高写性能,快了一半,
第三,O_SYNC 与 O_DIRECT | O_SYNC 几乎相同,
下面是测试用的程序
来源:http://5itest.net/bbs/TopicOther.asp?t=5&BoardID=16&id=1014
作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:https://jackxiang.com/post/6336/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!
最后编辑: jackxiang 编辑于2014-11-15 10:42
评论列表