python bottle使用多個端口(多個進程)提高並發


我的程序是用python結合bottle框架寫的,但bottle自帶wsgi原本只是單進程單線程運行模式(Bottle 默認運行在內置的 wsgiref 服務器上面。這個單線程的 HTTP 服務器在開發的時候特別有用,但其性能低下,在服務器負載不斷增加的時候也許會是性能瓶頸, 一次只能響應一個請求)。為了提升程序的處理能力,首先要啟用多線程,即在程序中使用gevent( 大多數服務器的線程池都限制了線程池中線程的數量,避免創建和切換線程的代價。盡管和進程 (fork)比起來,線程還是挺便宜的。但是也沒便宜到可以接受為每一個請求創建一個線程。gevent 模塊添加了 greenlet 的支持。 greenlet 和傳統的線程類似,但其創建只需消耗很少的資源。基於 gevent 的服務器可以生成成千上萬的 greenlet,為每個連接分配一個 greenlet 也毫無壓力。阻塞greenlet,也不會影響到服務器接受新的請求。同時處理的連接數理論上是沒有限制的。)。只需要在run中加上 server='gevent',如下:

1 import gevent
2 from gevent import monkey.patch_all() 3  代碼段…… 4 run(host='0.0.0.0', port=8080, server='gevent')

 

盡管使用了多線程模式,但這些線程都是跑在一個進程里,所以需要開啟多個進程來進一步提升並發處理能力,因此在啟用腳本的時候,在run(port=)里,端口號不能寫死,應該使用變量來傳遞,如下代碼(在腳本執行時,在需帶一個參數,這個參數是大於1024的整數,否則報錯停止腳本):

import gevent,sys
from gevent import monkey.patch_all() #獲取端口號 try: portnum = int(sys.argv[1]) except Exception,e: print "請帶上整數類型的端口號啟動此程序" logging.error("請帶上整數類型的端口號啟動此程序") sys.exit(1) if portnum <= 1024: print "端口號請大於1024!" logging.error("端口號請大於1024!") sys.exit(1) 代碼段…… run(host='0.0.0.0', port=portnum , server='gevent') 

 

執行方式如下(osyw.py是我python程序名):

python osyw.py 1124

 

如果純靠手動操作這些,在生產上,很不方便,所以我寫了個shell來管理,這個shell定義了多個端口號,然后去循環啟用或停止python進程,腳本大概如下(是用httpd改寫的):

 
#!/bin/bash
#
# osyw        Startup script for the osyw HTTP Server
#
# chkconfig: - 88 18
# description: osyw
# processname: osyw
# config: 
# config: /home/bottle/osyw/
# pidfile: /var/run/osyw.pid
#
### BEGIN INIT INFO
# Provides: osyw
# Short-Description: start and stop osyw HTTP Server
# Description: The osyw HTTP Server is an extensible server 
#  implementing the current HTTP standards.
### END INIT INFO
# Source function library.
. /etc/rc.d/init.d/functions
# Path to the apachectl script, server binary, and short-form for messages.
port_list=(8811 8812 8813)     #設置了3個端口
#pidfile='/var/run/osyw.pid'
pro_path='/var/www/osyw/osyw.py'    #程序路徑
log_path='/var/www/osyw/log/access.log'    #訪問日志路徑
RETVAL=0 start() { for i in ${port_list[*]} do p=`/usr/sbin/lsof -i :${i} |wc -l` if [ ${p} -ge 2 ] then action "osyw ${i} already exists !" /bin/false else /usr/bin/python ${pro_path} ${i} &>> ${log_path} RETVAL=$? if [ ${RETVAL} == 0 ] then action "osyw ${i} start ..." /bin/true else action "osyw ${i} start ..." /bin/false fi fi done return $RETVAL } stop() { for i in ${port_list[*]} do pidfile="/var/run/osyw_${i}.pid" if [ -f ${pidfile} ] then pid=`cat ${pidfile}` kill -9 ${pid} RETVAL=$? if [ ${RETVAL} == 0 ] then action "osyw ${i} stop ..." /bin/true else action "osyw ${i} stop ..." /bin/false fi rm -f ${pidfile} else action "osyw ${i} Has stopped !" /bin/false fi done } # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status -p ${pidfile} 'osyw' RETVAL=$? ;; restart) stop sleep 2 start ;; condrestart|try-restart) if status -p ${pidfile} 'osyw' >&/dev/null; then stop start fi ;; force-reload|reload) reload ;; *) echo $"Usage: $prog {start|stop|restart|condrestart|try-restart|force-reload|reload|status|fullstatus|graceful|help|configtest}" RETVAL=2 esac exit $RETVAL

 

效果圖:

shell bottle

 

本人的代碼是用svn管理的,所以上傳代碼后,SVN鈎子會調用shell腳本來重啟這些程序,以下是SVN鈎子代碼:

export LANG=en_US.UTF-8
/usr/bin/svn update --username xxxx --password xxxxxxxx /var/bottle
/bin/bash /etc/init.d/osyw restart

 

當然,為了結合shell,python程序里也要做一些處理,如自動把程序轉為后台守護進程,然后把進程ID寫入文件,以下是關鍵的python代碼:

 

#定義PID路徑  
pid_path = '/var/run/osyw_%s.pid' % portnum    
  
def daemonize(): """把本腳本轉為守護進程""" try: pid=os.fork() if pid>0: sys.exit(0) except Exception,e: logging.error(e) sys.exit(1) os.chdir('/') os.umask(0) os.setsid() try: pid=os.fork() if pid>0: sys.exit(0) except Exception,e: logging.error(e) sys.exit(1) PID = str(os.getpid()) with open(pid_path,'w') as f: f.write(PID) 其它代碼段…… if __name__ == '__main__': try: from oscore import setting #導入配置文件 if setting.status == 'online': #如果配置中是線上的,則程序轉入后台運行  daemonize() except Exception: pass app = default_app() app = SessionMiddleware(app, session_opts) #sessionMiddleware是session插件 run(app=app,host='0.0.0.0', port=portnum,server='gevent')

 

最好,用nginx代理來負載這些端口,我nginx和python程序是安裝在同一台服務器上的:

以上是nginx反向代理的部分代碼:

 

upstream myweb {
#ip_hash;
server 192.168.1.240:8811 weight=4 max_fails=2 fail_timeout=30s; server 192.168.1.240:8812 weight=4 max_fails=2 fail_timeout=30s; server 192.168.1.240:8813 weight=4 max_fails=2 fail_timeout=30s; } server { listen 80; server_name 192.168.1.240; location / { proxy_pass http://myweb; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $remote_addr; proxy_cache_key $host$uri$is_args$args; } access_log off; }

 

做完這些后,當訪問80端口時,nginx就會平均輪洵分配到每個端口上去,實現了多進程,多線程的運行模式,更有效的提升了並發處理能力

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM