django,性能测试,以及对fastcgi下进程模型和线程模型的分析

/**
*作者:张荣华
*日期:2008-11-15
**/

之前网上有很多关于django的测试,他们的测试结果都表明django在fastcgi模式下,使用线程模型要比进程模型快,而且更稳定,具体文章见:
http://irobot.blog.hexun.com/20332312_d.html
http://taoyh163.blog.163.com/blog/static/19580356200802433559850/
但是ahuaxuan根据操作系统的原理判断结果不应该是这样,理论上来讲,进程应该更快。为了证明自己的观点,于是做了以下测试。

那么在讲解我的测试方法之前,按照惯例,现来讲以下dango中fastcgi模式的一些知识点。
dango的fastcgi模式有如下几个重要参数:

protocol=PROTOCOL fcgi, scgi, ajp, ... (default fcgi)
host=HOSTNAME hostname to listen on..
port=PORTNUM port to listen on.
socket=FILE UNIX socket to listen on.
method=IMPL prefork or threaded (default prefork)
maxrequests=NUMBER number of requests a child handles before it is
killed and a new child is forked (0 = no limit).
maxspare=NUMBER max number of spare processes / threads
minspare=NUMBER min number of spare processes / threads.
maxchildren=NUMBER hard limit number of processes / threads
daemonize=BOOL whether to detach from terminal.
pidfile=FILE write the spawned process-id to this file.
workdir=DIRECTORY change to this directory when daemonizing.
outlog=FILE write stdout to this file.
errlog=FILE write stderr to this file.
umask=UMASK umask to use when daemonizing (default 022).

相信做java的同学一看就比较明白了,很多参数和tomcat中是一样的,主要有一个host,port,socket需要讲解一下,host和port我们知道应该是成对出现的,那么socket是什么呢,其实他们都是socket,只不过,host+port模式是tcp sock,而socket是unix sock,他们都是套接字,一个是操作系统本地的,一个是网络套接字而已。

我的测试工具是apachbench,简称ab,在apache的bin目录中有这个工具。我的web服务器是lighttpd1.4。
我一共划分了4个场景,第一个场景是操作数据库的请求,第二个场景是请求缓存的场景,而且使用线程模型,第3和第4个场景都是fastcgi的进程模型。

场景一
涉及到查数据库的url,每次请求一条简单的sql语句。
python manage.py runfcgi method=threaded host=127.0.0.1 port=3033 daemonize=false

请求数 并发数 总时间

5000 50 22.86s
5000 25 23.37s
5000 10 23.37s
5000 100 21.58s

场景二
不涉及到数据的url,执行一段判断后返回(可以认为数据都放在缓存中)。
python manage.py runfcgi method=threaded host=127.0.0.1 port=3033
请求数 并发数 总时间
5000 50 7.734s

Concurrency Level: 50
Time taken for tests: 7.883 seconds
Complete requests: 5000
Failed requests: 0
Write errors: 0
Total transferred: 5505084 bytes
HTML transferred: 4685937 bytes
Requests per second: 634.28 [#/sec] (mean)
Time per request: 78.830 [ms] (mean)
Time per request: 1.577 [ms] (mean, across all concurrent requests)
Transfer rate: 681.98 [Kbytes/sec] received

5000 25 7.545s

Concurrency Level: 25
Time taken for tests: 7.859 seconds
Complete requests: 5000
Failed requests: 0
Write errors: 0
Total transferred: 5504770 bytes
HTML transferred: 4685000 bytes
Requests per second: 636.20 [#/sec] (mean)
Time per request: 39.296 [ms] (mean)
Time per request: 1.572 [ms] (mean, across all concurrent requests)
Transfer rate: 684.01 [Kbytes/sec] received

5000 10 7.481s

Concurrency Level: 10
Time taken for tests: 7.920 seconds
Complete requests: 5000
Failed requests: 0
Write errors: 0
Total transferred: 5503153 bytes
HTML transferred: 4685000 bytes
Requests per second: 631.28 [#/sec] (mean)
Time per request: 15.841 [ms] (mean)
Time per request: 1.584 [ms] (mean, across all concurrent requests)
Transfer rate: 678.52 [Kbytes/sec] received

5000 100 7.776s

Concurrency Level: 100
Time taken for tests: 7.776 seconds
Complete requests: 5000
Failed requests: 0
Write errors: 0
Total transferred: 5504370 bytes
HTML transferred: 4685937 bytes
Requests per second: 643.04 [#/sec] (mean)
Time per request: 155.511 [ms] (mean)
Time per request: 1.555 [ms] (mean, across all concurrent requests)
Transfer rate: 691.32 [Kbytes/sec] received

场景一和场景 二对比可以发现,带有数据操作的请求明显需要更多的时间,之间从缓存中拿数据,每秒中fastcgi可以处理1000个请求。

场景三
不涉及到数据的url,执行一段判断后返回(可以认为数据都放在缓存中)。使用进程模型。
python manage.py runfcgi method=prefork host=127.0.0.1 port=3033
请求数 并发数 总时间
5000 50 22 s

Concurrency Level: 50
Time taken for tests: 22.676 seconds
Complete requests: 5000
Failed requests: 15
(Connect: 0, Receive: 0, Length: 15, Exceptions: 0)
Write errors: 0
Non-2xx responses: 15
Total transferred: 5519788 bytes
HTML transferred: 4676480 bytes
Requests per second: 220.50 [#/sec] (mean)
Time per request: 226.762 [ms] (mean)
Time per request: 4.535 [ms] (mean, across all concurrent requests)
Transfer rate: 237.71 [Kbytes/sec] received

5000 25 25 s

Concurrency Level: 25
Time taken for tests: 25.330 seconds
Complete requests: 5000
Failed requests: 15
(Connect: 0, Receive: 0, Length: 15, Exceptions: 0)
Write errors: 0
Non-2xx responses: 15
Total transferred: 5481652 bytes
HTML transferred: 4676480 bytes
Requests per second: 197.40 [#/sec] (mean)
Time per request: 126.649 [ms] (mean)
Time per request: 5.066 [ms] (mean, across all concurrent requests)
Transfer rate: 211.34 [Kbytes/sec] received

5000 10 15 s

Concurrency Level: 10
Time taken for tests: 15.463 seconds
Complete requests: 5000
Failed requests: 9
(Connect: 0, Receive: 0, Length: 9, Exceptions: 0)
Write errors: 0
Non-2xx responses: 9
Total transferred: 5536528 bytes
HTML transferred: 4679888 bytes
Requests per second: 323.35 [#/sec] (mean)
Time per request: 30.926 [ms] (mean)
Time per request: 3.093 [ms] (mean, across all concurrent requests)
Transfer rate: 349.66 [Kbytes/sec] received

5000 100 21 s

Concurrency Level: 100
Time taken for tests: 21.225 seconds
Complete requests: 5000
Failed requests: 15
(Connect: 0, Receive: 0, Length: 15, Exceptions: 0)
Write errors: 0
Non-2xx responses: 15
Total transferred: 5541355 bytes
HTML transferred: 4676480 bytes
Requests per second: 235.57 [#/sec] (mean)
Time per request: 424.498 [ms] (mean)
Time per request: 4.245 [ms] (mean, across all concurrent requests)
Transfer rate: 254.96 [Kbytes/sec] received

通过场景二和三的对比,我们可以看出线程模型在默认情况下比进程模型更加快。不过根据操作系统的特性,ahuaxuan认为事有蹊跷。理论上来讲,在速度方面,进程模型不应该比线程模型慢,虽然网上有的文章确实有提到线程模型比进程模型快,不过ahuaxuan觉得他们的测试是有问题的。在研究了django的fastcgi参数之后,再根据做java的经验我发现问题可能出现在进程的创建上。于是调整参数,继续测试。

场景四
不涉及到数据的url,执行一段判断后返回(可以认为数据都放在缓存中)。将最大进程数和最小进程数调整到50。
python manage.py runfcgi method=prefork host=127.0.0.1 port=3033 daemonize=false minspare=50 maxspare=50
请求数 并发数 总时间
5000 100 8.16s
第一次:

Concurrency Level: 100
Time taken for tests: 9.682 seconds
Complete requests: 5000
Failed requests: 0
Write errors: 0
Total transferred: 5557585 bytes
HTML transferred: 4685000 bytes
Requests per second: 516.42 [#/sec] (mean)
Time per request: 193.642 [ms] (mean)
Time per request: 1.936 [ms] (mean, across all concurrent requests)
Transfer rate: 560.55 [Kbytes/sec] received

第二次

Concurrency Level: 100
Time taken for tests: 5.134 seconds
Complete requests: 5000
Failed requests: 0
Write errors: 0
Total transferred: 5560000 bytes
HTML transferred: 4685000 bytes
Requests per second: 973.84 [#/sec] (mean)
Time per request: 102.686 [ms] (mean)
Time per request: 1.027 [ms] (mean, across all concurrent requests)
Transfer rate: 1057.53 [Kbytes/sec] received

分析,一模一样的两次请求,为什么差两倍的速度呢,根据ahuaxuan的分析,问题应该出在进程的创建上,第二次测试,由于进程已经存在,所以速度非常的快,比线程模型快了2倍不到一点。

5000 25 8.90s

Concurrency Level: 25
Time taken for tests: 5.347 seconds
Complete requests: 5000
Failed requests: 0
Write errors: 0
Total transferred: 5559748 bytes
HTML transferred: 4685000 bytes
Requests per second: 935.07 [#/sec] (mean)
Time per request: 26.736 [ms] (mean)
Time per request: 1.069 [ms] (mean, across all concurrent requests)
Transfer rate: 1015.38 [Kbytes/sec] received

5000 10 8.78s

Concurrency Level:      10

Time taken for tests:   5.723 seconds

Complete requests:      5000

Failed requests:        0

Write errors:           0

Total transferred:      5562916 bytes

HTML transferred:       4687811 bytes

Requests per second:    873.64 [#/sec] (mean)

Time per request:       11.446 [ms] (mean)

Time per request:       1.145 [ms] (mean, across all concurrent requests)

Transfer rate:          949.22 [Kbytes/sec] received

Concurrency Level: 10
Time taken for tests: 5.723 seconds
Complete requests: 5000
Failed requests: 0
Write errors: 0
Total transferred: 5562916 bytes
HTML transferred: 4687811 bytes
Requests per second: 873.64 [#/sec] (mean)
Time per request: 11.446 [ms] (mean)
Time per request: 1.145 [ms] (mean, across all concurrent requests)
Transfer rate: 949.22 [Kbytes/sec] received

5000 50 7.90s

Concurrency Level: 50
Time taken for tests: 5.239 seconds
Complete requests: 5000
Failed requests: 0
Write errors: 0
Total transferred: 5560923 bytes
HTML transferred: 4685937 bytes
Requests per second: 954.43 [#/sec] (mean)
Time per request: 52.387 [ms] (mean)
Time per request: 1.048 [ms] (mean, across all concurrent requests)
Transfer rate: 1036.63 [Kbytes/sec] received

对比场景三和场景四发现,在进程模式下在没有指定maxspare和minspare值的情况下,由于每次并发大的时候都动态的去创建进程,效率明显下降,5000个请求居然需要20s之多。而一旦设置了maxspare和minspare之后,只有第一次请求的时候,需要创建进程,之后经常已经存在,不需要创建,也不需要动态的消亡(maxspare和minspare值太小会导致fastcgi父进程频繁的创建和销毁子进程,非常的消耗cpu),整个应用程序的处理能力大大提高。

再对比场景二和场景四,可以发现不管是进程模式还是线程模式,每秒都能处理超过1000次的请求。而且在并发较大的情况下,进程模式效率更高。由此可见在网站访问量巨大的情况下,使用进程模型才是比较好的选择,而不是网上所说的使用线程模型。

后来为了作对比,ahuaxuan在线程模型上也加了maxspare=50,minspare=50,不过性能和没有加几乎一样,可见,这两个参数对进程模型的影响比较大。而且也可以进一步说明操作系统创建进程消耗确实大。

从这个对比结果,我们还可以得知:
1线程创建在ubuntu中的代价比进程小的多。(根据观察,在创建进程的时候,cpu上升到100%,而线程模型的cpu只有80%的样子)
2在进程已经存在的情况下,处理请求的能力,进程要比线程能力强。而且要强出1/3左右的样子

最后,贴出我的机器配置
cpu:t8100
内存:2g
硬盘:5400转的希捷

希望本文能够给对django性能有怀疑,以及对fastcgi下认为线程模型更快的同学有所帮助。

时间: 2024-09-27 02:19:11

django,性能测试,以及对fastcgi下进程模型和线程模型的分析的相关文章

linux下进程的最大线程数、进程最大数、进程打开的文件数【转】

转自:http://www.cnblogs.com/niocai/archive/2012/04/01/2428154.html ===========最大线程数============== linux 系统中单个进程的最大线程数有其最大的限制 PTHREAD_THREADS_MAX 这个限制可以在 /usr/include/bits/local_lim.h 中查看 对 linuxthreads 这个值一般是 1024,对于 nptl 则没有硬性的限制,仅仅受限于系统的资源 这个系统的资源主要就

Linux 下的五种 IO 模型详细介绍_Linux

概念说明 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限.为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操作系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间.针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空

Linux环境下进程中断的原因

问题描述 Linux环境下进程中断的原因 linux环境下,哪些原因可能造成某进程中断?或者说,代码中哪些不合理的地方可能会引起进程的中断? 解决方案 内存溢出等,程序无法处理的异常都会造成程序进程中断 解决方案二: 中断是一种是一种机制: 看下面的一段汇编代码: .section .data string:.ascii "hello" .section .text .globl _start _start: movl $4,%eax#sys_call_num movl $2,%ebx

linux-Linux系统下进程接收到信号后。。

问题描述 Linux系统下进程接收到信号后.. linux系统下,比如说一个进程给自身发信号,那么当进程接到信号时,是先执行发送语句的下一句,还是先执行信号处理函数? 解决方案 这个问题问的不是很具体. 一个进程给自身发信号(很罕见的情形),那么势必存在一个信号的监听函数listen,也就需要一个信号的处理函数handle. 而一般来说进程发出信号以后很可能有其他的任务要做,不能一直等待接收信号,所以要么采用多线程机制,要么采用异步选择机制. 如果是这样的话,先执行哪个任务就不好说了. 不知道我

ubuntu-关于PYTHON下进程控制器supervisor的设置,为啥文件修改被拒绝?

问题描述 关于PYTHON下进程控制器supervisor的设置,为啥文件修改被拒绝? XXXX@ubuntu:/usr/local/bin$ echo_supervisord_conf > /etc/supervisord.conf bash: /etc/supervisord.conf: Permission denied XXXX@ubuntu:/usr/local/bin$ sudo echo_supervisord_conf > /etc/supervisord.conf bash:

Windows下进程占用CPU过大的解决方案

Windows下进程占用CPU过大 1.WMI Provider Host(wmiprvse.exe)占用CPU高         & &         >      

Linux下进程描述(1)—进程控制块【转】

转自:http://www.cnblogs.com/33debug/p/6705391.html 进程概念介绍    进程是操作系统对运行程序的一种抽象. • 一个正在执行的程序: • 一个正在计算机上执行的程序实例: • 能分配给处理器并由处理器执行的实体: • 一个具有普以下特征的活动单元:一组指令序列的执行.一个当前状态和相关的系统资源集. 内核观点:担当分配系统资源(CPU时间,内存)的实体.进程的两个基本元素:程序代码(可能被执行的其他进程共享).数据集.进程是一种动态描述,但是并不代

云计算环境下动态虚拟企业伙伴选择模型

云计算环境下动态虚拟企业伙伴选择模型 张以文 倪志伟 宋捷 王力 互联网内海量的企业信息检索以及虚拟企业UDDI的搭建和管理已成为中小型企业组建虚拟企业的严重障碍.云计算通过互联网络提供虚拟化的资源计算模式,使企业能够快速部署资源和获取信息服务,从而使中小企业以快速和较低的成本创建企业联盟,争取主导地位成为可能.提出了一种云计算环境下动态虚拟企业伙伴选择模型,采用并行筛选机制降低问题求解空间,实现了中小企业联盟海量伙伴选择和UDDI搭建等问题,并通过实验验证了模型的合理性和可行性. 云计算环境下

Java线程模型缺陷研究

Java 编程语言的线程模型可能是此语言中最薄弱的部分.它完全不适合实际复杂程序的要求,而且也完全不是面向对象的.本文建议对 Java 语言进行重大修改和补充,以解决这些问题. Java 语言的线程模型是此语言的一个最难另人满意的部分.尽管 Java 语言本身就支持线程编程是件好事,但是它对线程的语法和类包的支持太少,只能适用于极小型的应用环境. 关于 Java 线程编程的大多数书籍都长篇累牍地指出了 Java 线程模型的缺陷,并提供了解决这些问题的急救包(Band-Aid/邦迪创可贴)类库.我