`
ahuaxuan
  • 浏览: 643229 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

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

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下认为线程模型更快的同学有所帮助。
分享到:
评论
24 楼 ahuaxuan 2008-12-08  
robbin 写道
现在低端的PC服务器动辄都双四核了,你想,这8个cpu内核的服务器,你不用多进程,不是傻子嘛。

嗯,明白.

而且发现大多数公司现在都有的一个问题,就是一个tomcat下部署多个app,其实是所有的app都部署在一个tomcat下,完全没有安装机器的特点和进程模型与线程模型的特点来考量是否需要发挥机器的最大性能.


如果说需要发挥机器的性能,尤其是cpu密集型应用,应该是部署多个tomcat,每个tomcat一个进程,这样才能更好的利用机器,才能充分发挥机器的性能.
23 楼 robbin 2008-12-04  
引用
通过环境的对比,发现两点:
1单核的情况下,进程模型和线程模型测试的结果类似,也就是说在单核的情况下,而且合理的设置maxspare值的情况下,两者性能类似。

2多核的情况下,进程模型明显比线程模型快,在ahuaxuan 的2核t8100的cpu下,进程模型不管怎么测,都比线程模型快1/3。

由此可见,对于多核的cpu下,进程模型明显更占优势,因为在多核的情况下,两个核心可以”同时“控制不同的进程。而线程模型下是单进程的,有一个核就被浪费了。所以多核情况下进程模型始终比线程模型快。

而且随着现代计算机的发展,进程模型应该会越来越被广泛使用,因为这种模型更能够发挥机器的实力。


python多线程是native thread的,多线程可以同时使用不同的cpu,但是python是有GIL锁的,一旦涉及某些阻塞的操作系统调用,还是会上全局锁,所以多核的服务器上面,只有多进程才可能真正实现“并行”(parellel),而且cpu越多,多进程优势越明显。 现在低端的PC服务器动辄都双四核了,你想,这8个cpu内核的服务器,你不用多进程,不是傻子嘛。
22 楼 剑事 2008-12-04  
希望有 p4 和 core 服务器的同鞋测试下
21 楼 ahuaxuan 2008-12-03  
再次测试hello world。
分别是线程模型和进程模型
测试方法:./ab -c 100 -n 5000 http://localhost/userdetail/userdetail/initRegisterUser

prefork(进程模型)
启动:
python manage.py runfcgi method=prefork host=127.0.0.1 port=3033 daemonize=false maxspare=150 minspare=50
结果:
1128/s
threaded(线程模型)
启动:
python manage.py runfcgi method=threaded host=127.0.0.1 port=3033 daemonize=false maxspare=150 minspare=50
结果:800/s

---------------------------------------------------------------------------------
再此总结:

之前有的同志测出threaded和prefork差不多,而我的测试表明prefork更快,经过多次的再分析和思考还有测试,渐渐的结果貌似显现出来

通过环境的对比,发现两点:
1单核的情况下,进程模型和线程模型测试的结果类似,也就是说在单核的情况下,而且合理的设置maxspare值的情况下,两者性能类似。

2多核的情况下,进程模型明显比线程模型快,在ahuaxuan 的2核t8100的cpu下,进程模型不管怎么测,都比线程模型快1/3。

由此可见,对于多核的cpu下,进程模型明显更占优势,因为在多核的情况下,两个核心可以”同时“控制不同的进程。而线程模型下是单进程的,有一个核就被浪费了。所以多核情况下进程模型始终比线程模型快
-----------------------
修改:以上结论的原理分析有误(结论在linux kernel2.6的情况下是正确的),有误在什么地方,请继续往下看

而且随着现代计算机的发展,进程模型应该会越来越被广泛使用,因为这种模型更能够发挥机器的实力。
20 楼 ahuaxuan 2008-11-29  
感谢大家的回帖,也感谢剑事的测试结果,由于大家努力使越来越多的人了解django,了解python。

从剑事的测试里得到的信息大概是这样的,在maxspare=50的情况下,prefork和threaded的处理能力是一样的,但是如果更高的情况下,prefork的处理能力开始下降,threaded确有对应的上升。

可以说剑事的测试结果和我的结果还是有较大的差距,我思前想后,觉得有可能是环境的差异造成的。

剑事使用的as4,as4应该是2.4的内核,而我的ubuntu8.04是2.6的内核,我现在强烈怀疑2.4内核在进程调度上的能力要远低于2.6内核,很有可能真是因为两个内核在进程调度上的差异导致我们测试的结果截然不同,希望有2.6环境的同志们帮忙测试一下,让我们找到真正的幕后黑手,我们需要知道真相。
19 楼 七猫 2008-11-29  
diogin 写道
Linux下的线程创建API跟进程创建API是同一个,只是参数不同。实际上Linux内核并没有区别进程和线程,而是统一称之为task。

动态spawn进程也可以设定一个上限的,不会出现worker进程撑死的情况。flup使用maxsparechildren和minsparechildren只是出于一个“进程池”的作用,缓冲一下负载,每个进程还是可以设定最大请求数的,超出请求数后被master进程销毁重建,这一点跟静态spawn出来的worker进程是一样的,而且两者都可以设定不销毁,即maxrequest无限,但这可能会带来内嵌解释器的worker进程的内存泄漏问题。

maxspare和minspare设成相同的后果是瞬间的大量连接会导致不停地创建worker进程,等于完全失去了这两个参数的意义,因为master进程必须保证这两个参数满足条件,当当前worker进程跑满,这时再来一个请求时,master使用一个spare的进程,导致spare减1,违反了minspare的值,这时master就必须再创建一个worker进程,原理很明了。

比较好的参数是两者有一定的差距,比如maxspare为64,minspare为16,这样可以得到较好的缓冲效果。

另外,PHP方面有一个patch,可以为PHP的FastCGI SAPI提供命令行下启动php-cgi进程的能力,无需类似spawn-fcgi之类的第三方spawn工具。


2.6后有原生的线程了
18 楼 剑事 2008-11-28  
<p>manage.py runfcgi protocol=scgi method=prefork host=127.0.0.1 port=3033 minspare=15 maxspare=150 </p>
<p> </p>
<pre name='code' class='java'>Server Software:        lighttpd/1.5.0
Server Port:            80

Document Path:          /company/left/
Document Length:        0 bytes

Concurrency Level:      1000
Time taken for tests:   3.667889 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Non-2xx responses:      1013
Total transferred:      248185 bytes
HTML transferred:       0 bytes
Requests per second:    272.64 [#/sec] (mean)
Time per request:       3667.889 [ms] (mean)
Time per request:       3.668 [ms] (mean, across all concurrent requests)
Transfer rate:          65.98 [Kbytes/sec] received</pre>
<p> <br/>manage.py runfcgi protocol=scgi method=threaded host=127.0.0.1 port=3033 minspare=15 maxspare=150</p>
<p> </p>
<pre name='code' class='java'>Server Software:        lighttpd/1.5.0
Server Port:            80

Document Path:          /company/left/
Document Length:        0 bytes

Concurrency Level:      1000
Time taken for tests:   2.632491 seconds
Complete requests:      1000
Failed requests:        0
Write errors:           0
Non-2xx responses:      1000
Total transferred:      245000 bytes
HTML transferred:       0 bytes
Requests per second:    379.87 [#/sec] (mean)
Time per request:       2632.491 [ms] (mean)
Time per request:       2.632 [ms] (mean, across all concurrent requests)
Transfer rate:          90.79 [Kbytes/sec] received</pre>
<p> </p>
<p> </p>
17 楼 robbin 2008-11-26  
引用
动态spawn进程也可以设定一个上限的,不会出现worker进程撑死的情况。flup使用maxsparechildren和 minsparechildren只是出于一个“进程池”的作用,缓冲一下负载,每个进程还是可以设定最大请求数的,超出请求数后被master进程销毁重建,这一点跟静态spawn出来的worker进程是一样的,而且两者都可以设定不销毁,即maxrequest无限,但这可能会带来内嵌解释器的 worker进程的内存泄漏问题。


强调两点:

1、动态spawn进程的缺陷不在于worker进程撑死不撑死,而在于黑客可以很容易的通过瞬间发起大量请求来消耗干净CPU资源,让服务器失去响应,严重的甚至无法远程ssh上去。尽管你可能有一定的spare进程,但是这个在黑客的大量请求面前没什么效果。而静态spawn的好处就在于碰到这种情况最多网站动态请求无法响应,其他一切照常,你有充足的时间查看lighttpd的并发连接情况,找出肇事IP,然后从容的远程ssh上去封掉它。

事实上litespeed也可以通过lsapi来支持Ruby进程的动态spawn,但是经过我测试,是存在上述问题的。当然Apache的mod_rails也存在上述问题,而且更严重一些,特别是像一个比较大型的Rails应用,初始化的过程非常长,要初始化加载的数据很多,启动的时候CPU消耗是非常大的,这就是我为什么抛弃动态spawn的根本原因。

2、动态spawn超出请求数就自动销毁重起我觉得没什么意义

尽管这种自动重起机制可以一定程度上减轻内存泄漏带来的痛苦,但是如果我进程没有内存泄漏,跑的好好的,你干吗非要给我定期重起呢? 所以这种按照计数机制进行定期重起,还远不如我根据资源监控的状况来决定释放重起,如果你进程内存泄漏到一定程度,我就重起你,如果你不泄漏,我自然就不会重起你,这样的机制更合理。
16 楼 ahuaxuan 2008-11-25  
diogin 写道
Linux下的线程创建API跟进程创建API是同一个,只是参数不同。实际上Linux内核并没有区别进程和线程,而是统一称之为task。

你说的是由其他进程来创建一个新进程,这个是spawn,但是父进程创建子进程应该不是这么回事了,上面的讨论其实不止是spawn的问题,还有fork的问题.在父进程中创建线程和创建子进程对操作系统的开销是完全不一样的,这个我当时做实验的时候已经观察过了.所以虽然都叫task,虽然api是一样,但是对机器产生的影响是不一样的.我不觉得创建进程和线程的代价是一样的.


diogin 写道

动态spawn进程也可以设定一个上限的,不会出现worker进程撑死的情况。flup使用maxsparechildren和minsparechildren只是出于一个“进程池”的作用,缓冲一下负载,每个进程还是可以设定最大请求数的,超出请求数后被master进程销毁重建,这一点跟静态spawn出来的worker进程是一样的,而且两者都可以设定不销毁,即maxrequest无限,但这可能会带来内嵌解释器的worker进程的内存泄漏问题。


动态的fork进程就是用maxchildren来控制的,flup的maxspare和minspare目的就是要避免动态的不停的fork和销毁子进程(访问量波动频繁的情况).这一点想必做过java的人都知道,不论哪个servlet容器里都有这种设置,只不过很多人都会用默认值.


diogin 写道

maxspare和minspare设成相同的后果是瞬间的大量连接会导致不停地创建worker进程,等于完全失去了这两个参数的意义,因为master进程必须保证这两个参数满足条件,当当前worker进程跑满,这时再来一个请求时,master使用一个spare的进程,导致spare减1,违反了minspare的值,这时master就必须再创建一个worker进程,原理很明了。

比较好的参数是两者有一定的差距,比如maxspare为64,minspare为16,这样可以得到较好的缓冲效果。

这个我想了一下,确实如你所说,之前我确实没有仔细考虑maxspare和minspare之间的关系.即使在tomcat中,我也从来没有把两个值设置成一样.都是有一定差距的,不过由于java中创建线程的代价较小,所以即使在tomcat中恰当的设置这两个值性能也不会得到多大的提高.所以我也没有多想下去,谢谢你的提醒


不过,如果设置成一样,也未必完全失去了这两个参数的意义,因为处理请求和创建新的子进程应该是同时进行的,也就是说即使一样(违反了minspare值之后,新的请求进来,flup可以立刻分配给一个子进程,然后同时再创建一个新的,而不会是先建一个新的,再把这个请求交给这个新的处理),不过这样做最大的缺点父进程还在fork和销毁子进程,但是请求确无需等待,可以立刻得到处理,这样做机器性能会下降,但是请求的速度应该不会减低.我的实验也证明了这一点,因为我把maxspare和minspare都设置了50的情况下,5000个请求只要5秒,还是非常非常的快,而且这个时候,父进程虽然还在创建子进程,但是这些子进程的django都是没有初始化过的,因为我没有看到middleware中有不停的被init和del的日志(我自己写了一个middleware,在init和del方法里都打印了日志)

不过这样做始终是不好,比较好的设置还是两个值有一定的差距较好.

那么更正一下结论:在场景3中为什么默认情况下进程模型慢是因为maxspare值设置不正确导致的,而不是maxspare和minspare同时设置不正确导致的.
15 楼 diogin 2008-11-25  
Linux下的线程创建API跟进程创建API是同一个,只是参数不同。实际上Linux内核并没有区别进程和线程,而是统一称之为task。

动态spawn进程也可以设定一个上限的,不会出现worker进程撑死的情况。flup使用maxsparechildren和minsparechildren只是出于一个“进程池”的作用,缓冲一下负载,每个进程还是可以设定最大请求数的,超出请求数后被master进程销毁重建,这一点跟静态spawn出来的worker进程是一样的,而且两者都可以设定不销毁,即maxrequest无限,但这可能会带来内嵌解释器的worker进程的内存泄漏问题。

maxspare和minspare设成相同的后果是瞬间的大量连接会导致不停地创建worker进程,等于完全失去了这两个参数的意义,因为master进程必须保证这两个参数满足条件,当当前worker进程跑满,这时再来一个请求时,master使用一个spare的进程,导致spare减1,违反了minspare的值,这时master就必须再创建一个worker进程,原理很明了。

比较好的参数是两者有一定的差距,比如maxspare为64,minspare为16,这样可以得到较好的缓冲效果。

另外,PHP方面有一个patch,可以为PHP的FastCGI SAPI提供命令行下启动php-cgi进程的能力,无需类似spawn-fcgi之类的第三方spawn工具。
14 楼 ahuaxuan 2008-11-25  
嗯,理解
其实就是一个静态spwan和动态fork的优劣的问题,我现在页觉得静态的spawn应该是比动态fork是更先进的.

之前也翻了坛子上一些帖子,看到说很多空间供应商都开始用mod_rails了,可能他们的做法是设定apache的maxspare和minspare和maxchildren这几种类似的参数来防止flood或者ddos等的(只是猜想,原理上应该和django的maxspare,minspare,maxchildren类似),不过显然lighttpd+静态的spawn+动态监控的方案更优.


更正,之前说django的fastcgi动态fork出来的是轻量级进程,不过思考了一下,应该不是,应该是cow进程.
轻量级进程允许父子经常共享在内核中的数据结构,这些数据结构占的空间有限,应该不会有10-20m,所以一个是cow进程,父子进程读相同的物理页,这些fastcgi进程的物理页差不多应该是10m多
13 楼 robbin 2008-11-23  
ahuaxuan 写道
robbin 写道

lighttpd 的spawn-fcgi是用来启动fcgi进程的通用工具,不但可以用来启动ruby的fcgi进程,还可以用来启动PHP的fcgi进程(甚至在nginx上面用fcgi跑PHP,也需要这个spawn-fcgi),所以你用spawn-fcgi来启动python的fcgi进程,也一样可以。

今天又想到一个问题,就是关于lighttpd spawn-fcgi的问题:
在部署ror时使用lighttpd 的spawn-fcgi功能是为了解决fcgi进程无法fork轻量级子进程,所以必须要由其他外部进程来spawnfcgi进程.这个时候,每次spawn出来的fcgi进程都是独立的,也就是说是无法共享进程在内核中的很多数据结构.

而django的fcgi进程可以自己fork子进程,而且fork出来的子进程我怀疑是轻量级子进程,他们和父进程可以共享内核中的数据结果,如果是这样,那么是不是可以说django的fastcgi模型要更加先进一点呢(而且动态的fork子进程+合适maxspare,minspare和maxchildren可以让app在性能上有一定的升缩范围).



1、进程没有轻量不轻量的区别,你fork的时候开销都一样大。

2、所谓父子进程共享数据无非就是copy-on-write而已,这样仅仅可以节省一些物理内存,其他的好处并不大。

3、mod_rails就是采取这种方式,但这种方式的缺点我前面讲过了。

4、就我的观察来看,FastCGI进程内存开销最的部分还是内存堆,动辄100-200MB,甚至更多,你copy-on-write节省的共享代码段内存非常有限,也就10-20MB的样子。

12 楼 ahuaxuan 2008-11-21  
robbin 写道

lighttpd 的spawn-fcgi是用来启动fcgi进程的通用工具,不但可以用来启动ruby的fcgi进程,还可以用来启动PHP的fcgi进程(甚至在nginx上面用fcgi跑PHP,也需要这个spawn-fcgi),所以你用spawn-fcgi来启动python的fcgi进程,也一样可以。

今天又想到一个问题,就是关于lighttpd spawn-fcgi的问题:
在部署ror时使用lighttpd 的spawn-fcgi功能是为了解决fcgi进程无法fork轻量级子进程,所以必须要由其他外部进程来spawnfcgi进程.这个时候,每次spawn出来的fcgi进程都是独立的,也就是说是无法共享进程在内核中的很多数据结构.

而django的fcgi进程可以自己fork子进程,而且fork出来的子进程我怀疑是轻量级子进程,他们和父进程可以共享内核中的数据结果,如果是这样,那么是不是可以说django的fastcgi模型要更加先进一点呢(而且动态的fork子进程+合适maxspare,minspare和maxchildren可以让app在性能上有一定的升缩范围).

11 楼 robbin 2008-11-15  
ahuaxuan 写道
刚才在坛子里找了一些资料看,发现了一个问题,不知道我理解的是不是正确:
好像在rails里用fcgi+lighttpd的时候,fcgi进程是由lighttpd来控制而产生的,所以可以用通过spawn-fcgi来控制进程的产生。

而在django中,lighttpd是不能去控制fcgi进程的产生或者消亡,这些工作都是flup,也就是python manager.py funfcgi 这个命令来产生一个父进程,然后,根据请求由这个父进程来动态的spawn子进程,不过,如果maxspare和minspare如果够大,比如说和maxchilden一样大,都是50 ,那么最大子进程数就是50,最大空闲进程数也是50,最小空闲进程数也是50,这样,父进程就不会不停的销毁和创建子进程了。这样其实也算是避免了动态的spawn了(不过第一不可避免,总得有第一次)




lighttpd 的spawn-fcgi是用来启动fcgi进程的通用工具,不但可以用来启动ruby的fcgi进程,还可以用来启动PHP的fcgi进程(甚至在nginx上面用fcgi跑PHP,也需要这个spawn-fcgi),所以你用spawn-fcgi来启动python的fcgi进程,也一样可以。

 
10 楼 ahuaxuan 2008-11-15  
刚才在坛子里找了一些资料看,发现了一个问题,不知道我理解的是不是正确:
好像在rails里用fcgi+lighttpd的时候,fcgi进程是由lighttpd来控制而产生的,所以可以用通过spawn-fcgi来控制进程的产生。

而在django中,lighttpd是不能去控制fcgi进程的产生或者消亡,这些工作都是flup,也就是python manager.py funfcgi 这个命令来产生一个父进程,然后,根据请求由这个父进程来动态的spawn子进程,不过,如果maxspare和minspare如果够大,比如说和maxchilden一样大,都是50 ,那么最大子进程数就是50,最大空闲进程数也是50,最小空闲进程数也是50,这样,父进程就不会不停的销毁和创建子进程了。这样其实也算是避免了动态的spawn了(不过第一不可避免,总得有第一次)

9 楼 robbin 2008-11-15  
ahuaxuan 写道
robbin 写道

我部署JavaEye服务器是自己写shell脚本来控制FCGI进程的spawn,respawn,很稳定,很健壮,而且很容易维护。

想了几分钟,还是没有想通具体的做法或者思路是什么样子的,shell脚本是如何去控制系统中进程的行为的呢,难道那个进程有提供这样的接口或者什么滴?

知识不够用了,robbin大哥能否为我们讲解一下具体的思路。


我们是静态spawn的,用lighttpd的spawn-fcgi命令来spawn进程就可以了,不进行动态spawn,当然shell启动脚本还有很多其他工作要做。另外的一个shell脚本用来监控FCGI的内存使用状况,发现超过限额以后就respawn它,另外还有一个进程健康检查的shell,如果发现进程crash掉,就spawn一个新的进程。
8 楼 ahuaxuan 2008-11-15  
robbin 写道

我部署JavaEye服务器是自己写shell脚本来控制FCGI进程的spawn,respawn,很稳定,很健壮,而且很容易维护。

想了几分钟,还是没有想通具体的做法或者思路是什么样子的,shell脚本是如何去控制系统中进程的行为的呢,难道那个进程有提供这样的接口或者什么滴?

知识不够用了,robbin大哥能否为我们讲解一下具体的思路。
7 楼 robbin 2008-11-15  
ahuaxuan 写道
robbin 写道

我没有研究过django的部署,不过从上文描述来看,FCGI进程是可以动态spawn的。但我以为动态spawn并不适合真正高负载的网络环境,黑客们只需要在瞬间发起上万个并发链接,服务器因为需要短时间内创建大量进程的开销而导致CPU消耗光,服务器失去响应。



从场景三来看确实是这样的,动态的spawn导致我的本本的cpu在整个测试期间(也就是多次5000个请求里)都是100%,然后我通过自己写的中间件发现,进程在不停的被创建和销毁,这个对操作系统来说简直是恶梦啊

在django里也可以解决这个问题,通过设置合理的maxchildren,minspare和maxspare应该就可以解决这个问题了


我部署JavaEye服务器是自己写shell脚本来控制FCGI进程的spawn,respawn,很稳定,很健壮,而且很容易维护。
6 楼 ahuaxuan 2008-11-15  
robbin 写道

我没有研究过django的部署,不过从上文描述来看,FCGI进程是可以动态spawn的。但我以为动态spawn并不适合真正高负载的网络环境,黑客们只需要在瞬间发起上万个并发链接,服务器因为需要短时间内创建大量进程的开销而导致CPU消耗光,服务器失去响应。



从场景三来看确实是这样的,动态的spawn导致我的本本的cpu在整个测试期间(也就是多次5000个请求里)都是100%,然后我通过自己写的中间件发现,进程在不停的被创建和销毁,这个对操作系统来说简直是恶梦啊

在django里也可以解决这个问题,通过设置合理的maxchildren,minspare和maxspare应该就可以解决这个问题了
5 楼 robbin 2008-11-15  
SCGI和FCGI仅仅是通讯协议略有差别而已,性能差别会在测试误差范围之内的。

我没有研究过django的部署,不过从上文描述来看,FCGI进程是可以动态spawn的。但我以为动态spawn并不适合真正高负载的网络环境,黑客们只需要在瞬间发起上万个并发链接,服务器因为需要短时间内创建大量进程的开销而导致CPU消耗光,服务器失去响应。

Ruby的mod_rails就是这种动态spawn模式,我很反对。JavaEye是静态spawn模式,应用服务器启动特定数量的FCGI进程数量,不论并发请求如何波动,不创建更多进程,也不销毁进程,这样系统的负载比较平滑。

相关推荐

    Django项目测试aa

    Django项目测试是一个系统性的工作,它要求测试人员具备多方面的知识和技能,包括对Django框架的熟悉、对测试方法的掌握、以及对项目的深入理解。通过有效的测试,可以确保Django项目的质量,减少生产环境中出现的...

    Python-使用PythonDjango的Web自动化测试平台

    2. **模型测试**:在Django中,模型是数据结构的定义,测试模型通常包括验证数据的有效性、查询行为以及与数据库的交互。我们可以创建测试类来覆盖这些测试点,确保模型的正确性。 3. **视图测试**:视图是处理HTTP...

    2023年Django大二下学期期末复习

    在2023年的大二下学期,Django框架的学习成为了重要的课程内容,这门期末复习资料涵盖了Django的核心概念、关键技术和实际应用。Django是Python编程语言中的一款高级Web开发框架,它以“快速开发、清晰设计、强调可...

    基于yolov5+django实现交通标志检测识别源码+模型.zip

    1. **数据预处理**:首先,需要对TT100K数据集进行预处理,包括图像标注、归一化和划分训练集、验证集和测试集。这一步通常涉及使用labelme等工具进行标注,并将标注信息转换为YOLOv5可读的格式。 2. **模型训练**...

    Python-Django模型字段加密解密您的数据并加密保存至数据库中

    本文将深入探讨如何在Django中实现模型字段的加密和解密,以及如何利用luojilab-django-mirage-field-5d96836这样的库来简化这个过程。 首先,理解Django模型字段。在Django中,模型是数据库表的面向对象表示。每个...

    浅析Django处理流程和结构分析django.doc

    Django 框架处理流程和结构分析 Django 是一个高级 Python Web 框架,鼓励快速开发和干净的、MVC 设计。它包括一个模板系统、对象相关的映射和用于动态创建管理界面的框架。支持 ORM、Cache、i18n、Admin 等众多...

    8. Django 表单与模型

    本文将深入探讨Django中的表单(Forms)和模型(Models),以及它们如何协同工作来构建高效、安全的Web应用。 ### Django模型(Models) **1. 模型的概念** Django模型是数据库交互的主要接口,它定义了数据库表...

    Python-动态创建一个模型的多个tablename并通过DjangoORM操作

    默认情况下,Django会根据模型类的名字来生成数据库表名,但可以通过`Meta`类中的`db_table`属性自定义表名。 在Django中动态创建模型并设置不同表名,我们需要创建一个基类,然后通过继承该基类来创建不同的模型。...

    python的Django框架模型

    Python的Django框架模型是Django的核心组成部分之一,它提供了数据模型的设计和数据库交互的能力。在Django中,模型(Model)是用来描述数据库结构的Python类,它定义了数据库表的字段、类型以及各种关系。下面我们...

    基于YOLOv5+django交通标志物检测源码+训练好的模型+web系统.zip

    标题中的"基于YOLOv5+django交通标志物检测源码+训练好的模型+web系统.zip"揭示了这个项目的核心内容:它是一个整合了YOLOv5深度学习模型和Django Web框架的交通标志检测系统。这个系统不仅包含了用于识别交通标志的...

    django中如何使用admin进行多个模型的后台管理

    默认情况下,Django会为每个模型生成一个基本的管理界面。然而,我们可以通过创建自定义的`AdminModel`类来定制显示和操作。例如,我们可以为`Author`模型创建一个自定义的Admin类: ```python # admin.py class ...

    Django(三)模型与数据库

    除了基本操作,Django的模型还支持更高级的功能,如表关联(如一对一、一对多、多对多关系)、字段的默认值、自定义校验规则、序列化等。此外,还可以通过模型定义复杂的查询,如过滤、排序、分组和聚合。 在实际...

    基于Django构建在线文本分类预测系统代码、模型、数据集:SVM模型在线预测与部署 基于 Django 3.2 框架

    基于Django构建在线文本分类预测系统代码、模型、数据集:SVM模型在线预测与部署 基于 Django 3.2 框架,参考博客:https://blog.csdn.net/wangyaninglm/article/details/116334297

    django部署配置以及nginx配置

    这会启动一个主进程和4个工作进程,每个进程有2个线程,监听本地8000端口,并使用指定的Django项目目录和wsgi入口。 文件"配置步骤.txt"应该包含详细的部署步骤,大致可能包括以下内容: 1. 安装Nginx和uWSGI。 2. ...

    django-performance-testing:通过自动化测试对Django进行性能测试!

    3. **监控和报告(Monitoring & Reporting)**:收集性能数据,生成详细的报告,以便分析和决策。 **使用方法** 要开始使用"django-performance-testing",首先需要安装该库,这可以通过`pip install django-...

    Django数据传输测试

    需要注意的是,在实际开发过程中,还需要考虑安全性、性能优化、前端交互以及后端逻辑的复杂性等多个方面的问题。本例仅提供了一个基础的入门示例,用于引导初学者搭建一个最简单的Django数据传输应用。

    基于django垃圾邮件分类多种模型在线部署页面展示源码+说明文档.zip

    基于django垃圾邮件分类多种模型在线部署页面展示源码+说明文档.zip基于django垃圾邮件分类多种模型在线部署页面展示源码+说明文档.zip基于django垃圾邮件分类多种模型在线部署页面展示源码+说明文档.zip基于django...

    Django实现并发处理的例子

    在这个例子中,我们关注的是如何在Django项目中处理并发,特别是在Linux环境下,这涉及到多线程、锁机制以及资源管理等核心概念。 首先,"并发处理"是指系统在同一时间内处理多个请求的能力。在Django中,这可以...

    基于Django开发的智能分类图片平台源代码+数据库+测试集+模型

    测试集用于验证系统性能,确保分类的准确性和稳定性。而预训练模型是已经过训练的神经网络模型,它可能使用了如卷积神经网络(CNN)等深度学习技术来学习和理解图像特征,以进行分类任务。 “Django范文/模板/素材...

Global site tag (gtag.js) - Google Analytics