Python: python bottle使用多个端口(多个进程)提高并发

python scott 275℃ 0评论

我的程序是用python结合bottle框架写的,但bottle自带wsgi原本只是单进程单线程运行模式(Bottle 默认运行在内置的 wsgiref 服务器上面。这个单线程的 HTTP 服务器在开发的时候特别有用,但其性能低下,在服务器负载不断增加的时候也许会是性能瓶颈, 一次只能响应一个请求)。为了提升程序的处理能力,首先要启用多线程,即在程序中使用gevent( 大多数服务器的线程池都限制了线程池中线程的数量,避免创建和切换线程的代价。尽管和进程 (fork)比起来,线程还是挺便宜的。但是也没便宜到可以接受为每一个请求创建一个线程。gevent 模块添加了 greenlet 的支持。 greenlet 和传统的线程类似,但其创建只需消耗很少的资源。基于 gevent 的服务器可以生成成千上万的 greenlet,为每个连接分配一个 greenlet 也毫无压力。阻塞greenlet,也不会影响到服务器接受新的请求。同时处理的连接数理论上是没有限制的。)。只需要在run中加上 server=’gevent’,如下:

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

尽管使用了多线程模式,但这些线程都是跑在一个进程里,所以需要开启多个进程来进一步提升并发处理能力,因此在启用脚本的时候,在run(port=)里,端口号不能写死,应该使用变量来传递,如下代码(在脚本执行时,在需带一个参数,这个参数是大于1024的整数,否则报错停止脚本):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import<span> gevent,sys
from gevent import<span>  monkey.patch_all()
#获取端口号
try<span>:
    portnum = int(sys.argv[1<span>])
except<span> Exception,e:
    print "请带上整数类型的端口号启动此程序>"<span>
    logging.error("请带上整数类型的端口号启动此程序>"<span>)
    sys.exit(1<span>)
if portnum <= 1024<span>:
    print "端口号请大于1024!>"<span>
    logging.error("端口号请大于1024!>"<span>)
    sys.exit(1<span>)
      代码段……
run(host='0.0.0.0', port=portnum , server='gevent'<span>)

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

python osyw.py 1124

如果纯靠手动操作这些,在生产上,很不方便,所以我写了个shell来管理,这个shell定义了多个端口号,然后去循环启用或停止python进程,脚本大概如下(是用httpd改写的):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#!/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/<span>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=<span>0
start() {
    for i in ${port_list[*<span>]}
    do
        p=`/usr/sbin/lsof -i :${i} |wc -<span>l`
        if [ ${p} -ge 2<span> ]
        then
            action "osyw ${i} already exists !>" /bin/<span>false
        else
            /usr/bin/python ${pro_path} ${i} &>><span> ${log_path}
        RETVAL=<span>$?
            if [ ${RETVAL} ==<span> 0 ]
            then
                action "osyw ${i} start ...>" /bin/<span>true
            else<span>
                action "osyw ${i} start ...>" /bin/<span>false
            fi
        fi
    done
    return<span> $RETVAL
}
stop() {
    for i in ${port_list[*<span>]}
    do
        pidfile="/var/run/osyw_${i}.pid>"
        if [ -<span>f ${pidfile} ]
        then
            pid=<span>`cat ${pidfile}`
            kill -9<span> ${pid}
            RETVAL=<span>$?
            if [ ${RETVAL} ==<span> 0 ]
            then
            action  "osyw ${i} stop ...>" /bin/<span>true
            else<span>
            action  "osyw ${i} stop ...>" /bin/<span>false
            fi
            rm -<span>f ${pidfile}
        else<span>
            action  "osyw ${i} Has stopped !>" /bin/<span>false
        fi
    done
}
# See how we were called.
case "$1>" in<span>
  start)
    start
    ;;
  stop)
    stop
    ;;
  status)
    status -p ${pidfile} 'osyw'<span>
    RETVAL=<span>$?
    ;;
  restart)
    stop
    sleep 2<span>
    start
    ;;
  condrestart|try-<span>restart)
    if status -p ${pidfile} 'osyw' >&/dev/<span>null; then
        stop
        start
    fi
    ;;
  force-reload|<span>reload)
    reload
    ;;
  *<span>)
    echo $"Usage: $prog {start|stop|restart|condrestart|try-restart|force-reload|reload|status|fullstatus|graceful|help|configtest}>"<span>
    RETVAL=2<span>
esac
exit $RETVAL

效果图:

本人的代码是用svn管理的,所以上传代码后,SVN钩子会调用shell脚本来重启这些程序,以下是SVN钩子代码:

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

当然,为了结合shell,python程序里也要做一些处理,如自动把程序转为后台守护进程,然后把进程ID写入文件,以下是关键的python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#定义PID路径  
pid_path = '/var/run/osyw_%s.pid' %<span> portnum  
def<span> daemonize():  
    ">""把本脚本转为守护进程>"">"  
    try<span>:  
        pid=<span>os.fork()  
        if pid><span>0:  
            sys.exit(0)  
    except<span> Exception,e:  
        logging.error(e)  
        sys.exit(1<span>)  
    os.chdir('/'<span>)  
    os.umask(0)  
    os.setsid()  
    try<span>:  
        pid=<span>os.fork()  
        if pid><span>0:  
            sys.exit(0)  
    except<span> Exception,e:  
        logging.error(e)  
        sys.exit(1<span>)  
    PID =<span> str(os.getpid())  
    with open(pid_path,'w'<span>) as f:  
        f.write(PID)  
其它代码段……  
if __name__ == '__main__'<span>:  
    try<span>:  
        from oscore import setting  #导入配置文件
        if setting.status == 'online'#如果配置中是线上的,则程序转入后台运行
<span>          daemonize()  
    except<span> Exception:  
        pass<span>  
    app =<span> 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反向代理的部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<span>upstream myweb {
#ip_hash;
server 192.168.1.240:8811 weight=4 max_fails=2 fail_timeout=<span>30s;
server 192.168.1.240:8812 weight=4 max_fails=2 fail_timeout=<span>30s;
server 192.168.1.240:8813 weight=4 max_fails=2 fail_timeout=<span>30s;
}
server {
    listen     80<span>;
        server_name  192.168.1.240<span>;
    location /<span>
        {
        proxy_pass http://<span>myweb;
        proxy_set_header Host  $host;
        proxy_set_header X-Forwarded-<span>For  $remote_addr;
        proxy_cache_key $host$uri$is_args$args;
        }
        access_log  off;
        }

做完这些后,当访问80端口时,nginx就会平均轮洵分配到每个端口上去,实现了多进程,多线程的运行模式,更有效的提升了并发处理能力

原文:http://www.cnblogs.com/drfdai/p/4518121.html

转载请注明:osetc.com » Python: python bottle使用多个端口(多个进程)提高并发

喜欢 (0)or分享 (0)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址