日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

WSGI协议的原理及实现

發布時間:2023/12/15 综合教程 29 生活家
生活随笔 收集整理的這篇文章主要介紹了 WSGI协议的原理及实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文將介紹WSGI協議的原理,并親自實現一個符合協議規范的WSGI server

WSGI,是Python Web Server Gateway Interface的簡稱,是web底層跟application解耦的協議,我們的web服務器使用WSGI協議來調用application稱為WSGI server。為了各類web server和web application之間能夠相互兼容,常見的web server(如Nginx,Apache)無法與web application(如Flask,django、tornado)直接通信,需要WSGI server作為橋梁。如今WSGI已經成為Python的一種標準協議PEP333。

WSGI的工作原理

WSGI的工作原理分為服務器層和應用程序層:

服務器層:將來自socket的數據包解析為http,調用application,給application提供環境信息environ,這個environ包含wsgi自身的信息(host,post,進程模式等),還有client的header和body信息。同時還給application提供一個start_response的回調函數,這個回調函數主要在應用程序層進行響應信息處理。
應用程序層:在WSGI提供的start_response,生成header,body和status后將這些信息socket send返回給客戶端。

上面的工作原理通過流程圖可以表示如下:

可以看到,wsgi server已經完成了底層http的解析和數據轉發等一系列網絡底層的實現,開發者可以更加專注于開發web application。

使用wsgiref模塊理解WSGI工作流程

Python自帶模塊wsgiref可以實現上述WSGI工作流程,這里使用wsgiref實現一個wsgi server的例子,以對wsgi的流程有感性的認識。

from wsgiref.simple_server import make_server
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [b'<h1>Hello, web!</h1>']
if __name__ == '__main__':
    httpd = make_server('', 9999, application)
    print("Serving HTTP on port 9999...")
    httpd.serve_forever()

該例子中application就是web application,這里是定義為一個函數,在實際的Flask和bottle中,就是一個類,這些類實現__call__方法,且該方法中帶參數environstart_response即可,運行server時只需調用self即可,例如:

class Flask:
  def __init__(self):
    # init params
    pass
  
  def __call__(self, environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [b'<h1>Hello, web!</h1>']
  
  def listen(self, port)
      from wsgiref.simple_server import make_server
        server = make_server('127.0.0.1', 9999, self)
        print('serve on 127.0.0.1:9999')
        server.serve_forever()

因此,在這里例子里,可以具體看到wsgi server調用application后,在start_response中application返回header、content-typebody,最后在瀏覽器打開127.0.0.1:9999即可看到由wigs server返回的內容。

自己實現一個wsgi server

了解了wsgi的工作原理和具體的使用例子,我們自己來實現一個wsgi服務器,可以進行請求解析,并可以調用Flask暴露出來的wsgi_app參數來將Flask運行起來。

首先組織代碼結構:

import socket
import StringIO
import sys

class WSGIserver(object):
  def __init__(self):
    pass
  
  def serve_forever(self):
    pass
  
  def handle_one_request(self):
    request_data = '' # 暫不處理
    self.parse_request(request_data)
    env = self.get_environ()
    result = self.application(env, self.start_response)
    self.finish_response(result)
    
  def parse_request(self, data):
      pass
  
  def get_environ(self):
    pass
  
  def start_response(self, status, response_headers, exc_info=None):
    pass
  
  def finish_response(self, result):
    pass
  
def make_server(server_address, application):
    server = WSGIServer(server_address)
    server.set_app(application)
    return server
  
if __name__ == '__main__':
  httpd = make_server('', 8888, application)
  httpd.serve_forever()

分析如下:

__init__:wsgi server的初始化操作
serve_forever:可以讓wsgi server一直監聽客戶端請求
handle_one_request:對每一次請求進行參數解析,包括parse_requestget_environ
start_response:傳遞給application的回調函數,根據PEP333,start_response需要包含status,response_headers,exc_info三個參數。status是http的狀態碼,如“200 OK”,”404 Not Found”。response_headers是一個(header_name, header_value)元組的列表,在進行application開發時需要注意這點。exc_info通常不需要,直接設置為None即可。具體的一些參數的解釋可以參考協議的詳細解釋。
finish_response:解析一次請求后,需要關閉socket端口,同時將application返回的data返回至客戶端。

有了代碼結構,根據PEP333標準協議,那么具體可以完善上述代碼,如下(wsgi.py):

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import socket
import StringIO
import sys

class WSGIServer(object):

    address_family = socket.AF_INET
    socket_type = socket.SOCK_STREAM
    request_queue_size = 1

    def __init__(self, server_address):

        # 創建socket,利用socket獲取客戶端的請求
        self.listen_socket = listen_socket = socket.socket(self.address_family, self.socket_type)
        # 設置socket的工作模式
        listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 綁定socket地址
        listen_socket.bind(server_address)
        # socket active, 監聽文件描述符
        listen_socket.listen(self.request_queue_size)

        # 獲得serve的host name和port
        host, port = self.listen_socket.getsockname()[:2]
        self.server_name = socket.getfqdn(host)
        self.server_port = port

        self.headers_set = []

    def set_app(self, application):
        self.application = application 

    #啟動WSGI server服務,不停的監聽并獲取socket數據。
    def serve_forever(self):
        listen_socket = self.listen_socket
        while True:
            self.client_connection, client_address = listen_socket.accept() #接受客戶端請求
            #處理請求
            self.handle_one_request()

    def handle_one_request(self):
        self.request_data = request_data = self.client_connection.recv(1024)
        self.parse_request(request_data)
        # print(''.join(
  # '< {line}
'.format(line=line)
  # for line in request_data.splitlines()
  # ))
          # Construct environment dictionary using request data
        env = self.get_environ()
      
        #給flask	ornado傳遞兩個參數,environ,start_response
        result = self.application(env, self.start_response)
        self.finish_response(result)

    #處理socket的http協議
    def parse_request(self, data):
        format_data = data.splitlines()
        if len(format_data):
            request_line = data.splitlines()[0]
            request_line = request_line.rstrip('
')
            (self.request_method, self.path, self.request_version) = request_line.split() ## ['GET', '/', 'HTTP/1.1']

    # 獲取environ數據并設置當前server的工作模式
    def get_environ(self):
        env = {}
        env['wsgi.version']      = (1, 0)
        env['wsgi.url_scheme']   = 'http'
        env['wsgi.input']        = StringIO.StringIO(self.request_data)
        env['wsgi.errors']       = sys.stderr
        env['wsgi.multithread']  = False
        env['wsgi.multiprocess'] = False
        env['wsgi.run_once']     = False
        # Required CGI variables
        env['REQUEST_METHOD']    = self.request_method    # GET
        env['PATH_INFO']         = self.path              # /hello
        env['SERVER_NAME']       = self.server_name       # localhost
        env['SERVER_PORT']       = str(self.server_port)  # 8888
        return env

    def start_response(self, status, response_headers, exc_info=None):
        server_headers = [('Date', 'Tue, 31 Mar 2015 12:54:48 GMT'), ('Server', 'WSGIServer 0.2')]
        self.headers_set = [status, response_headers + server_headers]

    #把application返回給WSGI的數據返回給客戶端。
    def finish_response(self, result):
        try:
            status, response_headers = self.headers_set
            response = 'HTTP/1.1 {status}
'.format(status=status)
            for header in response_headers:
                response += '{0}: {1}
'.format(*header)
            response += '
'
            for data in result:
                response += data
            self.client_connection.sendall(response)
            print(''.join(
                '> {line}
'.format(line=line)
                for line in response.splitlines()
            ))
        finally:
            self.client_connection.close()

SERVER_ADDRESS = (HOST, PORT) = '', 8888

def make_server(server_address, application):
    server = WSGIServer(server_address)
    server.set_app(application)
    return server


if __name__ == '__main__':
    if len(sys.argv) < 2:
        sys.exit('Provide a WSGI application object as module:callable')
    app_path = sys.argv[1]
    module, application = app_path.split(':') # 第一個參數是文件名,第二個參數時長文件內app的命名
    module = __import__(module)
    application = getattr(module, application) # getattr(object, name[, default]) -> value
    httpd = make_server(SERVER_ADDRESS, application)
    print('WSGIServer: Serving HTTP on port {port} ...
'.format(port=PORT))
    httpd.serve_forever()

至此,wsgi server的代碼已完成,下面寫一個簡單的Flask實例(flaskapp.py)來驗證這個server是否可用:

from flask import Flask
from flask import Response

flask_app = Flask(__name__)

@flask_app.route('/hello')
def hello_world():
    return Response(
        'hello',
        mimetype='text/plain'
    )

app = flask_app.wsgi_app # 調用Flask暴露的wsgi_app,提供給wsgi server作為application,而不是直接run(port)

在終端輸入:

$python wsgi.py flaskapp:app
WSGIServer: Serving HTTP on port 8888 ...

打開瀏覽器,輸入http://localhost:8888/hello可看到hello的信息,同時,終端輸出一些request和response的信息:

> HTTP/1.1 200 OK
> Content-Type: text/plain; charset=utf-8
> Content-Length: 5
> Date: Tue, 31 Mar 2015 12:54:48 GMT
> Server: WSGIServer 0.2
> > hello

總結

本文主要從WSGI的原理入手,同時通過原生wsgiref模塊了解了WSGI的工作流程,最后利用socket模塊自己實現了一個WSGI server,我們可以通過Flask這類web框架暴露出來的WSGI接口啟動應用。

由于WSGI實現了web server和web application的高度解耦,在具體的使用中可以使用一些比較成熟的WSGI server如uwsgi和gunicorn對任何一個符合WSGI規范的app進行部署,這給Python Web應用的部署帶來了極大的靈活性。

http://geocld.github.io/2017/08/14/wsgi/

總結

以上是生活随笔為你收集整理的WSGI协议的原理及实现的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。