[原创] flup源码-运行模型剖释 - python的fastcgi

flup作为python的fastcgi实现,可以独立作为一个WSGI的服务,或与django结合,使用django模版、ORM进行开发。django利用其多线程或多进程的运行模型!

flup官网:http://trac.saddi.com/flup
         最终版本:1.0.3,官网最下面的publisher 与Middleware 都被去掉了,只剩下server,clent
         文档:    没有,官方说源码就是最好的文档。

 

先看看django启动为fastcgi模式参数

 

manage.py runfcgi minspare=50 maxspare=200 maxchildren=1000 maxrequests=99999 host=127.0.0.1 port=8080 pidfile=./django.pid method=threaded # 解释下参数意义 # maxspare为初始化的进程/线程数,也是最少个数 # minspare为最少空闲进程/线程数, 注意空闲二字 # maxchildren 进程/线程上限,以每个py进程20~30M算,自己算下服务器内存能创建最大进程数吧! # maxrequests 处理多少个请求后,销毁进/线程,防内存泄漏,默认不销毁 # method = prefork|threaded 进程或线程跑你的程序,默认进程。 # 其它几个不解释

 

 

这主要解释下flup运行模型: 线程(thread) 或 进程(prefork只支持lunix)

 

  • 线程模型 method=threaded

1)线程池:flup.threadpool.py文件:

class ThreadPool(object): """ Thread pool that maintains the number of idle threads between minSpare and maxSpare inclusive. By default, there is no limit on the number of threads that can be started, but this can be controlled by maxThreads. """ def __init__(self, minSpare=1, maxSpare=5, maxThreads=sys.maxint): self._minSpare = minSpare self._maxSpare = maxSpare self._maxThreads = max(minSpare, maxThreads) self._lock = threading.Condition() self._workQueue = [] self._idleCount = self._workerCount = maxSpare self._threads = [] self._stop = False # Start the minimum number of worker threads. for i in range(maxSpare): self._start_new_thread()

这里节选两个函数, 初始化时线程池把空闲线程维持在 minSpare与maxSpare之间,最大线程数 上限为maxThreads,这里看下django启动参数一致.

 

这线程池好东西阿,装flup库,写其它python多线程或异步的程序也可以引入使用,只要实现自己的MyJob类,直接调用addJob即可:

from flup.server.threadpool import ThreadPool gThreadPool = ThreadPool() class MyJob(object): def __init__(self, func,args): self._func = func self._args = copy.deepcopy(args) def run(self): try: return self._func(**self._args) except Exception, e: print e if __debug__:raise e def fun(e): print e gThreadPool.addJob(MyJob(fun, {'e':'test'}))

 

因为python本身并非真正的多线程,这个线程模并不是我程序最终选择的,尽管测试中发现它非常高效,省去了线程的创建、销毁。另外,另一个组在使用线程模型时,出现不稳定,运行久了时不时500。

 

线程模型可以直接使用kill -HUP 主进程PID进行安全退出,其使用 线程的join等待进程退出。 -TERM也行,-9你就悲剧了!

 

  • 进程模型 method=prefork

进程要来得复杂,但最终运行时,稳定一些!内存与CPU都能用尽。


主要看flup.prefrokserver.py文件 PreforkServer 类。其使用linux的标准fork函数进行多进程的派生,每个派生出来的子进程都变成了单线程。只能在运行时进程线程创建!fork特性详看: http://www.opengroup.org/onlinepubs/000095399/functions/fork.html

想创建为多线程 + 多进程的 fastcgi,看我的另一文章。

 

贴一个退出函数,当我们向主进程PID发出kill -HUP PID时,PreforkServer类调用以下函数,向每一个子进程发送一个中断信号SIGINT=2, 再等待10秒,注册一个alrm。子进程必需注册SIGINT信号,不然python虚拟机 将调用默认的键盘中断,子进程如正在处理HTTP请求,会异常中断!你就等着事务数据出乱吧!

def _cleanupChildren(self): """ Closes all child sockets (letting those that are available know that it's time to exit). Sends SIGINT to those that are currently processing (and hopes that it finishses ASAP). Any children remaining after 10 seconds is SIGKILLed. """ # Let all children know it's time to go. for pid,d in self._children.items(): if d['file'] is not None: d['file'].close() d['file'] = None if not d['avail']: # Child is unavailable. SIGINT it. try: os.kill(pid, signal.SIGINT) except OSError, e: if e[0] != errno.ESRCH: raise def alrmHandler(signum, frame): pass # Set up alarm to wake us up after 10 seconds. oldSIGALRM = signal.getsignal(signal.SIGALRM) signal.signal(signal.SIGALRM, alrmHandler) signal.alarm(10)

 

多进程下注册自己的中断处理函数:

import signal def _intHandler(signum, frame): # 你可以不做任何事,只是不让python虚拟机调用默认键盘中断 pass #这个_int变量属于子进程级的,不同的子进程不一样 _int = None # 你也可以不要这个变量,多次去调用signal.signal(signal.SIGINT, _intHandler) def index(request): global _int if _int is None: signal.signal(signal.SIGINT, _intHandler) # 以下处理你的HTTP请求

 

父进程与子进程之间使用非阻塞的sock对进程通信

def _spawnChild(self, sock): """ Spawn a single child. Returns True if successful, False otherwise. """ # This socket pair is used for very simple communication between # the parent and its children. parent, child = socket.socketpair() parent.setblocking(0) setCloseOnExec(parent) child.setblocking(0) setCloseOnExec(child) # 接着fork,然后调用 self._chlid进入子进程。 以下去除了容错代码 pid = os.fork() if not pid: # Child pid = os.getpid() os.setpgid(pid, pid) try: # Enter main loop. self._child(sock, parent) except KeyboardInterrupt: pass sys.exit(0) # mail loop结束后,子进程退出 else: # Parent pass def _child(self, sock, parent): """Main loop for children.""" while True: # Wait for any activity on the main socket or parent socket. r, w, e = select.select([sock, parent], [], []) # 子进程一个while True的循环处理请求,并使用select模型等待主进程或主端口发来的请求。

 

flup只处理了 kill -HUP, -TERM 退出信号; -QUIT, -9 都将直接杀死进程,不能安全退出。



 

你可能感兴趣的:(python,django,多线程,thread,sockets,processing)