项目部署Nginx+Tornado+Supervisor

在项目中应用到了 Tornado 框架,开始时只是单进程裸跑 Tornado 实例,但是在高并发的情况下,单进程无法满足上千的连接请求。Nginx 是我们熟知的反向代理服务器,它可以支持上万的平行连接,在与 PHP、Python 集成后可以应用在大规模的集群上,Nginx 同时也可以用作负载平衡器,以减小单点服务器的压力。

Supervisor 是一个进程管理工具,我们可以使用它管理多个 Tornado 实例,非常方便。

总的思路是:Nginx 通过唯一监听端口接收大量请求,然后将请求分发到不同的 Tornado 实例上,Supervisor 将多个 Tornado 实例进程统一管理。

Tornado

Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其非阻塞的方式和对 epoll 的运用,Tornado 每秒可以处理数以千计的连接,这意味着对于实时 Web 服务来说,Tornado 是一个理想的 Web 框架。Tornado 由于内置了支持 epoll/kqueue 等高效网络库,而具备了处理高并发的能力。

程序入口:

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
# -*- coding: utf-8 -*-
import os.path

import tornado.escape
import tornado.ioloop
import tornado.web
from tornado.httpserver import HTTPServer
from tornado.options import define, options

from handlers import stream_handler

define("port", default=8888, help="run on the given port", type=int)
define("debug", default=False, help="run in debug mode")


class Application(tornado.web.Application):
def __init__(self):
handlers = [
# (r"/", main_handler.MainHandler),
(r"/stream.*?", stream_handler.StreamHandler),
]
settings = dict(
template_path=os.path.join(os.path.dirname(__file__), "templates"),
static_path=os.path.join(os.path.dirname(__file__), "static"),
debug=options.debug,
)
super(Application, self).__init__(handlers, **settings)

pass


def main():
tornado.options.parse_command_line()
http_server = HTTPServer(Application())
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()


if __name__ == "__main__":
main()

执行以下命令运行:

1
python27 app.py --port=8081

注意: 因为我的机器上安装了不同版本的 Python 解释器,python27 是我为 Python 2.7.13 创建的软连接。

以下一行代码必须有:

1
tornado.options.parse_command_line()

因为我们运行程序脚本时,需要指定不同的端口,这是在执行 Tornado 的解析命令行。

Supervisor 管理 Tornado 进程

Supervisor 的安装可以在官方文章中找到,可使用 yum pip easy_insatll 等方法。最简单的就是:

1
pip27 install supervisor

注意: 因为我的机器上安装了不同版本的 pip 包,pip27 是我为 Python 2.7.13 使用的。

创建文件夹:

1
mkdir /etc/supervisor

创建配置文件:

1
echo_supervisord_conf > /etc/supervisor/supervisord.conf

修改 /etc/supervisor/supervisord.conf 文件内容,在文件结尾 [include] 节点处

1
2
3
4
5
6
7
; [include]
; files = relative/directory/*.ini

=>

[include]
files = conf.d/*.conf

保存并退出

/etc/supervisor/ 下创建 conf.d 文件夹,及 ProjectName.conf (以项目名称命名的,例如:tornados.conf)

执行:

1
vi tornados.conf

添加以下内容:

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
[group:tornados]
programs=tornado-0,tornado-1,tornado-2,tornado-3,tornado-4,tornado-5,tornado-6,tornado-7

[program:tornado-0]
# 执行的命令
command=python27 /home/ly/analysisweb/app.py --port=8020
# 项目目录
directory=/home/ly/analysisweb/
# 用户
user=root
# 自动重启
autorestart=true
redirect_stderr=true
# log文件路径
stdout_logfile=/home/ly/analysisweb/log/supervisor/tornado0.log
# log级别
loglevel=info

[program:tornado-1]
command=python27 /home/ly/analysisweb/app.py --port=8021
directory=/home/ly/analysisweb/
user=root
autorestart=true
redirect_stderr=true
stdout_logfile=/home/ly/analysisweb/log/supervisor/tornado1.log
loglevel=info

[program:tornado-2]
command=python27 /home/ly/analysisweb/app.py --port=8022
directory=/home/ly/analysisweb/
user=root
autorestart=true
redirect_stderr=true
stdout_logfile=/home/ly/analysisweb/log/supervisor/tornado2.log
loglevel=info

[program:tornado-3]
command=python27 /home/ly/analysisweb/app.py --port=8023
directory=/home/ly/analysisweb/
user=root
autorestart=true
redirect_stderr=true
stdout_logfile=/home/ly/analysisweb/log/supervisor/tornado3.log
loglevel=info

[program:tornado-4]
command=python27 /home/ly/analysisweb/app.py --port=8024
directory=/home/ly/analysisweb/
user=root
autorestart=true
redirect_stderr=true
stdout_logfile=/home/ly/analysisweb/log/supervisor/tornado4.log
loglevel=info

[program:tornado-5]
command=python27 /home/ly/analysisweb/app.py --port=8025
directory=/home/ly/analysisweb/
user=root
autorestart=true
redirect_stderr=true
stdout_logfile=/home/ly/analysisweb/log/supervisor/tornado5.log
loglevel=info

[program:tornado-6]
command=python27 /home/ly/analysisweb/app.py --port=8026
directory=/home/ly/analysisweb/
user=root
autorestart=true
redirect_stderr=true
stdout_logfile=/home/ly/analysisweb/log/supervisor/tornado6.log
loglevel=info

[program:tornado-7]
command=python27 /home/ly/analysisweb/app.py --port=8027
directory=/home/ly/analysisweb/
user=root
autorestart=true
redirect_stderr=true
stdout_logfile=/home/ly/analysisweb/log/supervisor/tornado7.log
loglevel=info

保存并退出

执行以下命令启动 Supervisor :

1
supervisord -c /etc/supervisor/supervisord.conf

坑1:出现错误 Error: Another program is already listening on a port that one of our HTTP servers is configured to use. Shut this program down first before starting supervisord.For help, use /usr/bin/supervisord –h

解决办法:是因为有一个使用 supervisor 配置的应用程序正在运行。执行 ps -ef | grep supervisor 查看进程,杀掉 supervisor 的所有进程,重新运行 参考这里

坑2:出现错误 Unlinking stale socket /tmp/supervisor.sock

解决办法:执行 unlink /tmp/supervisor.sock 参考这里

如果执行成功,ps -ef | grep python 结果如下:

1
2
3
4
5
6
7
8
9
root     21314     1  0 15:22 ?        00:00:00 /usr/bin/python27 /usr/local/python2.7/bin/supervisord -c /etc/supervisor/supervisord.conf
root 21315 21314 0 15:22 ? 00:00:00 python27 /home/ly/analysisweb/app.py --port=8021
root 21316 21314 0 15:22 ? 00:00:00 python27 /home/ly/analysisweb/app.py --port=8020
root 21317 21314 0 15:22 ? 00:00:00 python27 /home/ly/analysisweb/app.py --port=8023
root 21318 21314 0 15:22 ? 00:00:00 python27 /home/ly/analysisweb/app.py --port=8022
root 21319 21314 0 15:22 ? 00:00:00 python27 /home/ly/analysisweb/app.py --port=8025
root 21320 21314 0 15:22 ? 00:00:00 python27 /home/ly/analysisweb/app.py --port=8024
root 21321 21314 0 15:22 ? 00:00:00 python27 /home/ly/analysisweb/app.py --port=8027
root 21322 21314 0 15:22 ? 00:00:00 python27 /home/ly/analysisweb/app.py --port=8026

Nginx 作反向代理

2017-9-1 161933

安装好 Nginx 后,编辑 nginx.conf 文件:

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
http {
# 我启动了8个Tornado实例,配置不同端口
upstream tornados {
server 127.0.0.1:8020;
server 127.0.0.1:8021;
server 127.0.0.1:8022;
server 127.0.0.1:8023;
server 127.0.0.1:8024;
server 127.0.0.1:8025;
server 127.0.0.1:8026;
server 127.0.0.1:8027;
}

proxy_next_upstream error;

server {
# nginx 监听8081端口
listen 8081;

# 静态文件的路径
location /static/{
alias /home/ly/analysisweb/static/;
expires 24h;
}

location / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://tornados;
}

}

server {
......
}
}

可参考 运行和部署

以上,就完成了Nginx+Tornado+Supervisor 的项目部署,分别启动 nginx、supervisord ,就可以达到不错的负载均衡效果(我的项目中没有访问静态资源的情景,所以主要还是作负载均衡用)