Python生產環境部署(fastcgi,uwsgi)

Python部署web開發程序的幾種方法

  • fastcgi ,經過flup模塊來支持,在nginx裏對應的配置指令是 fastcgi_passphp

  • http,nginx使用proxy_pass轉發,這個要求後端appplication必須內置一個能處理高併發的http server,在python的web框架當中,只能選擇tornado.html

  • uwsgi,包括4部分組成:python

    • uwsgi協議
    • web server內置支持協議模塊
    • application服務器協議支持模塊
    • 進程控制程序

    nginx從0.8.4開始內置支持uwsgi協議,uwsgi協議很是簡單,一個4個字節header+一個body,body能夠是不少協議的包,好比說http,cgi等(經過header裏面字段標示)。nginx

    uwsgi的特色在於自帶的進程控制程序.它是用c語言編寫,使用natvie函數,其實和spawn-fcgi/php-fpm相似。因此uwsgi能夠支持多種應用框架,包括(python,lua,ruby,erlang,go)等等程序員

  • mod_python,這是apache內置的模塊,很嚴重的依賴於mod_python編譯使用的python版本,和apache配套使用,不推薦web

  • cgi,這個太old,不推薦,並且nginx不支持cgi方式,只能用lighttpd或者apacheapache

  • spawn-fcgi,這個是fastcgi多進程管理程序,lighttpd安裝包附帶的,和 flup效果同樣,區別是flup是 python代碼級引入,spawn-fcgi是外部程序。spawn-fcgi用途很廣,能夠支持任意語言開發的代碼,php,python,perl,只要你代碼實現了fastcgi接口,它均可以幫你管理你的進程json

  • scgi,全名是Simple Common Gateway Interface,也是cgi的替代版本,scgi協議很簡單,我以爲和fastcgi差很少,只是沒有怎麼推廣開來,nginx對應的配置指令是scgi_pass,你想用就用,flup也支持。flask

  • Gunicorn,和uwsgi相似的工具,從rails的部署工具(Unicorn)移植過來的。可是它使用的協議是 WSGI,全稱是Python Web Server Gateway Interface ,這是python2.5時定義的官方標準(PEP 333 ),根紅苗正,並且部署比較簡單,http://gunicorn.org/ 上有詳細教程後端

  • mod_wsgi,apache的一個module,也是支持WSGI協議,https://code.google.com/p/modwsgi/

 

uwsgi

安裝uwsgi

pip install uwsgi

 

配置uwsgi

uwsgi 有多種配置可用:

1,ini 
2,xml 
3,json
4,yaml

配置示例

$ cat etc/uwsgi.ini 
[uwsgi]
socket = 127.0.0.1:9005
chdir = /Users/suoning/python_project/trunk/
wsgi-file = main.py
processes = 4
stats = 127.0.0.1:9000
daemonize = /tmp/uwsgiServer.log
pidfile = /tmp/uwsgi.pid
vacuum = true
log-maxsize = 50000000
disable-logging = true
callable = app
$

配置參數詳解:

經常使用選項:

socket : 地址和端口號,例如:socket = 127.0.0.1:50000

processes : 開啓的進程數量

workers : 開啓的進程數量,等同於processes(官網的說法是spawn the specified number of  workers / processes

chdir : 指定運行目錄(chdir to specified directory before apps loading)

wsgi-file : 載入wsgi-file(load .wsgi file)

stats : 在指定的地址上,開啓狀態服務(enable the stats server on the specified address)

threads : 運行線程。因爲GIL的存在,我以爲這個真心沒啥用。(run each worker in prethreaded mode with the specified number of threads)

master : 容許主進程存在(enable master process)

daemonize : 使進程在後臺運行,並將日誌打到指定的日誌文件或者udp服務器(daemonize uWSGI)。實際上最經常使用的,仍是把運行記錄輸出到一個本地文件上。

log-maxsize :以固定的文件大小(單位KB),切割日誌文件。 例如:log-maxsize = 50000000  就是50M一個日誌文件。 

pidfile : 指定pid文件的位置,記錄主進程的pid號。

vacuum : 當服務器退出的時候自動清理環境,刪除unix socket文件和pid文件(try to remove all of the generated file/sockets)

disable-logging : 不記錄請求信息的日誌。只記錄錯誤以及uWSGI內部消息到日誌中。若是不開啓這項,那麼你的日誌中會大量出現這種記錄:

[pid: 347|app: 0|req: 106/367] 117.116.122.172 () {52 vars in 961 bytes} [Thu Jul  7 19:20:56 2016] POST /post => generated 65 bytes in 6 msecs (HTTP/1.1 200) 2 headers in 88 bytes (1 switches on core 0)

 

配置nginx

$ cat etc/nginx/servers/pan.conf 

server {
    listen       80;
    server_name  localhost;

    location / {
        include uwsgi_params;
        uwsgi_pass 127.0.0.1:9005;
    }

    location /webstatic/ {
        expires 7d;
        add_header Cache-Control public;
        alias /Users/suoning/probject/python_project/webstatic/trunk/;
    }

}

$ 
$ nginx -t
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful
$ 
$ nginx -s reload
$ 

 

配置application

flask 示例

...
app = Flask('pan')
...

if __name__ == '__main__':
    # app.run(host='0.0.0.0', port=5000)
    app.run()

# 注意:變量app對應uwsgi配置文件uwsgi.ini中 callable = app 

 

啓動uwsgi

$ 
$ uwsgi --ini /usr/local/etc/uwsgi.ini
[uWSGI] getting INI configuration from /usr/local/etc/uwsgi.ini
$ 
$ ps -ef|grep uwsgi
  501 11428     1   0 11:40下午 ??         0:01.23 uwsgi --ini /usr/local/etc/uwsgi.ini
  501 11432 11428   0 11:40下午 ??         0:00.00 uwsgi --ini /usr/local/etc/uwsgi.ini
  501 11433 11428   0 11:40下午 ??         0:00.00 uwsgi --ini /usr/local/etc/uwsgi.ini
  501 11434 11428   0 11:40下午 ??         0:00.00 uwsgi --ini /usr/local/etc/uwsgi.ini
  501 11435 11428   0 11:40下午 ??         0:00.00 uwsgi --ini /usr/local/etc/uwsgi.ini
  501 11440 69240   0 11:40下午 ttys000    0:00.00 grep uwsgi
$ 
$ lsof -i tcp:9000
COMMAND   PID    USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
uwsgi   11428 suoning   28u  IPv4 0x5583e11534d24e73      0t0  TCP localhost:cslistener (LISTEN)
$
$ lsof -i tcp:9005
COMMAND   PID    USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
uwsgi   11428 suoning    6u  IPv4 0x5583e11535699e73      0t0  TCP localhost:9005 (LISTEN)
uwsgi   11432 suoning    6u  IPv4 0x5583e11535699e73      0t0  TCP localhost:9005 (LISTEN)
uwsgi   11433 suoning    6u  IPv4 0x5583e11535699e73      0t0  TCP localhost:9005 (LISTEN)
uwsgi   11434 suoning    6u  IPv4 0x5583e11535699e73      0t0  TCP localhost:9005 (LISTEN)
uwsgi   11435 suoning    6u  IPv4 0x5583e11535699e73      0t0  TCP localhost:9005 (LISTEN)
$

 

FCGI

參考:http://webpy.org/cookbook/fastcgi-nginx

配置Nginx

$ cat etc/nginx/servers/pan.conf 

server {
    listen       80;
    server_name  localhost;

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }

    location / {
        fastcgi_param REQUEST_METHOD $request_method;
        fastcgi_param QUERY_STRING $query_string;
        fastcgi_param CONTENT_TYPE $content_type;
        fastcgi_param CONTENT_LENGTH $content_length;
        fastcgi_param GATEWAY_INTERFACE CGI/1.1;
        fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
        fastcgi_param REMOTE_ADDR $remote_addr;
        fastcgi_param REMOTE_PORT $remote_port;
        fastcgi_param SERVER_ADDR $server_addr;
        fastcgi_param SERVER_PORT $server_port;
        fastcgi_param SERVER_NAME $server_name;
        fastcgi_param SERVER_PROTOCOL $server_protocol;
        fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_script_name;
        fastcgi_pass 127.0.0.1:9005;
    }

    location /webstatic/ {
        expires 7d;
        add_header Cache-Control public;
        alias /Users/suoning/probject/python_project/webstatic/trunk/;
    }

}

$

 

配置application

簡單示例

from flup.server.fcgi import WSGIServer
from pan import app

WSGIServer(
    app,
    bindAddress=(host, port),
maxThreads
=threads ).run()

生產環境示例

#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'suoning'


import sys
import argparse
from flup.server.fcgi import WSGIServer
from lib.daemon import Daemon
from pan import app

APP_NAME = 'pan_platform'
APP_INST_NAME = '20170501'

parser = argparse.ArgumentParser(description=u'Run an pan FastCGI server')
parser.add_argument('command', type=str,
                    help=u'command [start|stop|restart]',
                    choices=['start', 'stop', 'restart'])
parser.add_argument('-p', '--port', type=int,
                    help=u'port of this server', required=True)
parser.add_argument('-t', '--threads', type=int, default=50,
                    help=u'max number of threads')
parser.add_argument('-host', '--host', default='0.0.0.0',
                    help=u'Listen to the main clause')


class panPlatformDaemon(Daemon):
    def run(self):
        # 運行服務
        try:
            WSGIServer(
                app,
                bindAddress=(args.host, args.port),
                maxThreads=args.threads,
                umask=0111
            ).run()
        except:
            sys.stderr.write('oops')


def gen_pidfile(port):
    return '/var/run/%s_%s_%d.pid' % (APP_NAME, APP_INST_NAME, port)


if __name__ == '__main__':
    args = parser.parse_args()
    daemon = panPlatformDaemon(gen_pidfile(args.port))
    if 'start' == args.command:
        daemon.start()
    elif 'stop' == args.command:
        daemon.stop()
    elif 'restart' == args.command:
        daemon.restart()
    else:
        print "Unknown command"
        sys.exit(2)
    sys.exit(0)

 

fastcgi協議和http協議在代碼部署中的的優劣對比

  • fastcgi雖然是二進制協議,相對於http協議,並不節省資源。二進制協議,只能節省數字的表達,好比 1234567,用字符串表示須要7個Byte,用數字就是4個Byte,而字符串到哪裏都同樣

  • fastcgi在傳輸數據的時候,爲了兼容cgi協議,還要帶上一堆cgi的環境變量,因此和http協議相比,用fastcgi傳輸數據並不省,反而多一些

  • fastcgi 惟一的優勢是,它是長鏈接的,用戶併發1000個request,fastcgi可能就用10個 連接轉發給後端的appplication,若是用http協議,那來多少給多少,會向後端appplication 發起1000個請求

  • http代理轉發方式,在面對超高併發的狀況下會出問題,由於,tcp協議棧當中,port是int16整型 你本地新建一個connect,須要消耗一個端口,最多能到65536。外部併發幾十萬個請求,port池耗幹,你的服務器只能拒絕響應了

 

CGI, FCGI, SCGI, WSGI 區別

WIKI Links:

CGI - http://en.wikipedia.org/wiki/Common_Gateway_Interface
FCGI - http://en.wikipedia.org/wiki/Fcgi
SCGI - http://en.wikipedia.org/wiki/SCGI
WSGI - http://en.wikipedia.org/wiki/Wsgi
 
Other reference:
http://helpful.knobs-dials.com/index.php/CGI%2C_FastCGI%2C_SCGI%2C_WSGI%2C_servlets_and_such#FastCGI_and_SCGI
 
CGI = Common Gateway Interface
顧名思義,它是一種接口規範。該規範詳細定義了Web服務器中運行的服務器代理程序,怎樣獲取及返回網頁生成過程當中,服務器環境上下文和HTTP協議中的參數名稱,如你們所熟知的:REQUEST_METHOD,QUERY_STRING,CONTENT_TYPE等等。絕大部分的Web服務器程序,是以腳本的形式代理接受並處理HTTP請求,返回HTTP頁面或響應。這些腳本程序,就是你們所熟知的PHP、ASP、JSP等等。
 
FCGI = Fast CGI
它實際上是CGI在具體實現中的的一個變種。其設計思路是,經過減小CGI代理程序和Web宿主服務程序的通訊開銷,從而達到提升Web服務性能的最終目的。因而可知,FCGI在規範上跟CGI並無不一樣,只是具體實現方式上有所改進:CGI的作法是,對於每一個HTTP請求,Web宿主服務程序都創建新的進程以調用服務器腳本,相應該請求;FCGI的作法是,創建一個獨立的FCGI服務程序進程,和Web宿主服務程序進程通訊,FCGI服務進程被一旦啓動後,本身分配資源、建立線程響應HTTP請求、並決定自身生命週期,從而大大下降了系統爲了建立進程而作出的資源開銷。現代流行的Web服務器程序,如PHP、ASP.Net,基本都是FCGI的實現。
 
SCGI = Simple CGI
它是FCGI在精簡數據協議和響應過程後的產物。其設計目的是爲了適應愈來愈多基於AJAX或REST的HTTP請求,而作出更快更簡潔的應答。而且SCGI約定,當服務器返回對一個HTTP協議請求響應後,馬上關閉該HTTP鏈接。因此不難看出,SCGI更加適合於廣泛意義上SOA所提倡的「請求-忘記」這種通訊模式。
 
WSGI = Web Server Gateway Interface
此協議是Python語言的專利,它定義了一組在Web服務宿主程序和HTTP響應代理程序之間通訊的廣泛適用的接口。它的產生是由於Python程序員注意到,對於Web框架和Web宿主服務器程序間,有嚴重的耦合性,好比說,某些框架是針對Apache的mod_python設計的。因而,WSGI就定義了一套很是低級別的接口。常見的Python Web框架都實現了這個協議:如 CherryPy, Django, web.py, web2py, TurboGears, Tornado, Pylons, BlueBream, Google App Engine[dubious – discuss], Trac, Flask, Pyramid,等等.
相關文章
相關標籤/搜索