Flask,webpy,Django都带着 WSGI server,当然性能都不好,自带的web server 更多的是测试用途。线上发布时,则使用高性能的 wsgi server或者是联合nginx做uwsgi 。

greenlet是一个轻量级的协程库。gevent是基于greenlet的网络库。 guincorn是支持wsgi协议的http server,gevent只是它支持的模式之一 ,是为了解决django、flask这些web框架自带wsgi server性能低下的问题。它的特点是与各个web框架结合紧密,部署特别方便。

一、WSGI 协议简介

Web框架致力于如何生成HTML代码,而Web服务器用于处理和响应HTTP请求。Web框架和Web服务器之间的通信,需要一套双方都遵守的接口协议。WSGI协议就是用来统一这两者的接口的。

二、Gunicorn

即 Green Unicorn

常用的WSGI容器有Gunicorn和uWSGI,但Gunicorn直接用命令启动,不需要编写配置文件,相对uWSGI要容易很多,所以这里我也选择用Gunicorn作为容器。

安装

pip install gunicorn

启动

gunicorn [options] module_name:variable_name

module_name对应python文件,variable_name对应web应用实例。

以最简单的flask应用为例:

#main.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return 'hello world'
    
if __name__ == '__main__':
    app.run()

启动代码

gunicorn main:app

其中main就是指python程序代码main.py,app就是那个wsgi func的名字。这样运行的话, gunicorn 默认作为一个监听 127.0.0.1:8000 的web server,可以在本机通过: http://127.0.0.1:8000 访问。

设置监听端口

如果要通过网络访问,则需要绑定不同的地址(也可以同时设置监听端口)。

gunicorn -b 127.0.0.1:8080

使用多进程

在多核服务器上,为了支持更多的并发访问并充分利用资源,可以使用更多的 gunicorn 进程。

gunicorn -w 8 main:app

这样就可以启动8个进程同时处理HTTP请求,提高系统的使用效率及性能。

配合gevent

另外, gunicorn 默认使用同步阻塞的网络模型(-k sync),对于大并发的访问可能表现不够好, 它还支持其它更好的模式,比如:gevent或meinheld。

gunicorn -k gevent main:app

指定配置文件

以上设置还可以通过 -c 参数传入一个配置文件实现。配置文件如下:

import os
import gevent.monkey
gevent.monkey.patch_all()

import multiprocessing

# 需要log目录存在。如果不存在,启动会报错

# debug = True  # 生产环境不需要这个配置项。开启debug项后,在启动gunicorn的时候可以看到所有可配置项的配置
loglevel = 'debug'  # 用于控制errorlog的信息级别,可以设置为debug、info、warning、error、critical
bind = "0.0.0.0:5000"    # 监听IP放宽,以便于Docker之间、Docker和宿主机之间的通信
pidfile = "log/gunicorn.pid"
accesslog = "log/access.log"    # 访问日志,可以通过access_log_format设置访问日志格式
errorlog = "log/debug.log"
daemon = True     # 以守护进程形式来运行Gunicorn进程(其实就是将这个服务放到后台去运行)

#启动的进程数
workers = multiprocessing.cpu_count() * 2 + 1 
worker_class = 'gunicorn.workers.ggevent.GeventWorker'
# workers = 5    # 定义同时开启的处理请求的进程数量,根据网站流量适当调整
# worker_class = "gevent"   # 采用gevent库,支持异步处理请求,提高吞吐量

x_forwarded_for_header = 'X-FORWARDED-FOR'

指定配置文件的启动命令如下:

gunicorn main:app -c gunicorn.conf.py

单纯的flask 自带的web服务器做下测试,会看到压力大的时候出现socket的问题,因为他是单进程单线程的。使用gunicorn来启动,响应速度和能力提升显著。

配置中workers指定启动的进程数。cpu的损耗是平均到各个进程。workers的值一定不要过大,毕竟多进程对于系统的调度消耗比较大。

关于gevent

gevent是一个基于libev的并发库。它为各种并发和网络相关的任务提供了整洁的API。gunicorn对于“协程”也就是Gevent的支持非常好。

gevent程序员指南:gevnet指南

gevent.monkey介绍详见:关于gevent monkey

三、部署注意事项

开发flask应用时,常用flask-script添加一些命令扩展。而部署应用时,就不需要再从flask-script的Manager实例中启动应用了。 在项目中添加wsgi.py文件:

在项目中添加wsgi.py文件:

# -*- coding: utf-8 -*-
import os

from . import create_app
import datetime

# Create an application instance that web servers can use. We store it as
# "application" (the wsgi default) and also the much shorter and convenient
# "app".
application = app = create_app('default')

@app.context_processor
def template_extras():
    """
    上下文处理装饰器,返回的字典的键可以在上下文中使用
    :return:
    """
    return {'enumerate': enumerate, 'len': len, 'datetime': datetime}

在wsgi文件中创建flask实例给gunicorn使用。 创建实例后,注册上下文装饰器。 再通过gunicorn启动flask应用:

gunicorn -b 127.0.0.1:8000 -k gevent -w 1 app.wsgi

四、Nginx

Gunicorn对静态文件的支持不太好,所以生产环境下常用Nginx作为反向代理服务器。

安装

sudo apt-get install nginx 或者 yum install nginx

启动

sudo /etc/init.d/nginx start或者sudo systemctl start nginx

如果想在系统启动时启用Nginx。请输入以下命令:

sudo systemctl enable nginx

修改配置文件

先将配置文件备份: cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak

然后修改配置文件:

server {
        listen 80;
        server_name _; # 外部地址

        location / {
                proxy_pass http://127.0.0.1:8000;
                proxy_redirect     off;
                proxy_set_header   Host                 $http_host;
                proxy_set_header   X-Real-IP            $remote_addr;
                proxy_set_header   X-Forwarded-For      $proxy_add_x_forwarded_for;
                proxy_set_header   X-Forwarded-Proto    $scheme;
        }
}

这里将Nginx设置为代理模式,代理到本地的8000端口,之后就可以通过公网访问flask应用了。

总结

最后,总结下这几个部分的关系:

web platform   <—>   WSGI   <—>   Nginx   <—>   web_client

以一段对话结束本篇博客

(nginx收到客户端发来的请求,根据nginx中配置的路由,将其转发给WSGI)

nginx:”WSGI,找你的来了!”

(WSGI服务器根据WSGI协议解析请求,配置好环境变量,调用start_response方法呼叫flask框架)

WSGI服务器:”flask,快来接客,客户资料我都给你准备好了!”

(flask根据env环境变量,请求参数和路径找到对应处理函数,生成html)

flask:”!@#$%^……WSGI,html文档弄好了,拿去吧。”

(WSGI拿到html,再组装根据env变量组装成一个http响应,发送给nginx)

WSGI服务器:”nginx,刚才谁找我来着?回他个话,!@#$%^…..”

(nginx再将响应发送给客户端)