论坛首页 编程语言技术论坛

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

浏览 31131 次
该帖已经被评为精华帖
作者 正文
   发表时间:2008-11-15   最后修改: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了(不过第一不可避免,总得有第一次)

0 请登录后投票
   发表时间:2008-11-15   最后修改: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进程,也一样可以。

 
0 请登录后投票
   发表时间: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在性能上有一定的升缩范围).

0 请登录后投票
   发表时间: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的样子。

0 请登录后投票
   发表时间:2008-11-25   最后修改: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多
0 请登录后投票
   发表时间:2008-11-25   最后修改: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工具。
0 请登录后投票
   发表时间:2008-11-25   最后修改: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同时设置不正确导致的.
0 请登录后投票
   发表时间: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超出请求数就自动销毁重起我觉得没什么意义

尽管这种自动重起机制可以一定程度上减轻内存泄漏带来的痛苦,但是如果我进程没有内存泄漏,跑的好好的,你干吗非要给我定期重起呢? 所以这种按照计数机制进行定期重起,还远不如我根据资源监控的状况来决定释放重起,如果你进程内存泄漏到一定程度,我就重起你,如果你不泄漏,我自然就不会重起你,这样的机制更合理。
14 请登录后投票
   发表时间:2008-11-28   最后修改:2008-12-02

manage.py runfcgi protocol=scgi method=prefork host=127.0.0.1 port=3033 minspare=15 maxspare=150

 

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

 
manage.py runfcgi protocol=scgi method=threaded host=127.0.0.1 port=3033 minspare=15 maxspare=150

 

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

 

 

0 请登录后投票
   发表时间: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后有原生的线程了
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics