之前一直在用nginx + uwsgi + django 架构,之前也在博客里分享过相关故障解决:

nginx+uwsgi完美配置文件,解决“upstream prematurely closed connection”报错

Nginx + gunicorn + Flask 快速部署实现上线水平

nginx + uwsgi + django出现的问题

那么在用了一段时间后发现一个很严重的问题:无论代码层如何优化,都会出现内存泄漏(内存占用无限增长)的问题,

尝试过增加uwsgi.ini配置文件中添加参数:max-requests = 50,来对抗内存泄露(该参数用来控制每个进程处理50个请求后重启该进程)。

但发现并没有什么用,当然网上还有其他折中方案,比如控制达到占用多少内存后自动重启uwsgi,但这些都是治标不治本的,果断弃用nginx + uwsgi + django架构。

Nginx + gunicorn + Flask

在考虑很多架构后,还是选择了一个主流轻量级架构:Nginx + gunicorn + Flask

网上相关的资料也不少,在参考了网上很多demo与教程,加上零碎的实用配置与自己的理解,终于实现了生产环境稳定上线,那么在这里想把自己的最简,最优,最实用配置分享出来,同时也是作为一个备忘。

这里我们应用了flask 的一个扩展:flask_restful

安装:

pip3 install flask && pip3 install flask_restful

以下的demo代码为实现一个接口的最简代码,基本满足接口使用:

接口demo代码

文件名:vuln.py

 

 
from flask import Flask,request,jsonify
from flask_restful import reqparse, abort, Api, Resource

app = Flask(__name__)
api = Api(app)

# 这是第一个接口
class task_1(Resource):
    def get(self):	#这里的get函数,指定处理get请求
        parser = reqparse.RequestParser()
        parser.add_argument("url", type=str, location="args", required=False)
        parser.add_argument("type", type=int, location="args", required=False)
        parser.add_argument("id", type=str, location="args", required=False)

        params = parser.parse_args(strict=False)
        if params['action'] == 'fresh':
			#run something
            return 'vuln.cn demo 1'
        else:
            #run something
            return 'hello vuln.cn'
			
# 这是第二个接口		
class task_2(Resource):
    def get(self):
        parser = reqparse.RequestParser()
        parser.add_argument("id", type=str, location="args", required=False)
        params = parser.parse_args(strict=False)
		#run something
        return 'vuln.cn demo 2'
		
		
		
#路由地址分别对应处理的类
api.add_resource(task_1, '/demo1/')	#实现接口:http://www.vuln.cn:8002/demo1/?url=xxx&type=xxx&id=xxx
api.add_resource(task_2, '/demo2/')	#实现接口:http://www.vuln.cn:8002/demo2/?id=xxx

if __name__ == '__main__':
    app.run(debug=True)

 

flask web启动(仅限测试使用,不可作为生产接口)

 flask run -h 0.0.0.0 -p 8002 

这里0.0.0.0 可以接收外网访问

那么,以上就完成了一个基本的web接口,访问8002端口,加上对应的参数即可看到响应,这里就不截图了。

但是,从代码功能角度是已经没有问题的,接口已经写好,但从系统稳定性与web性能角度是远远不够的,因为你测试的时候会发现,flask 启动的接口同时只能处理一个请求。

gunicorn 配置

这时候就需要引入部署容器gunicorn,安装与配置都非常简单。

安装:

pip3 install gunicorn

启动:

gunicorn -w 50 -b 0.0.0.0:8080 vuln:app --reload -t 500 -D --access-logfile log/gunicorn.log

以上启动命令的含义为:

  • -w 50 开启50个进程
  • 0.0.0.0:8080 定义8080端口
  • vuln:app vuln为项目的文件名,如上面的vuln.py文件名,app为vuln.py代码中 app = Flask(__name__)
  • --realod 监听到项目文件变动自动重启gunicorn 使之生效
  • -t 500 配置每个请求的超时时间为500秒
  • -D 让命令后台执行
  • --access-logfile log/gunicorn.log 将请求日志保存到该文件中

以上我们就可以使用8080端口来访问我们的接口,到这里已经差不多了,但是我们还需要在web最外层用nginx 做下代理提升静态文件的web性能。

nginx最外层代理

nginx相关的配置博客里已经有很多了,在这里再贴一个我的最简配置吧:

 


proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
server {
        listen  80;
        server_name www.vuln.cn;
        location / {
            proxy_pass  http://127.0.0.1:8080;
            access_log /home/web/log/api_access.log;
            proxy_read_timeout 300;
            }


    } 

关于第一行的配置解释可以参考:Nginx反向代理 + Flask + gunicorn 架构解决获取用户真实ip问题

至此,已经实现nginx 80端口代理本地后端gunicorn服务(8080)启动的flask web项目。

以上demo 与部署方案仅为个人经验,仅供参考,本文会持续更新优化