Что такое WSGI, вы должны понять после прочтения

Python
Что такое WSGI, вы должны понять после прочтения

Пишу на python web уже несколько лет, но до сих пор не знаю, что такое WSGI, и много ли там людей. Это нормально говорить, потому что как разработчику редко нужно понимать, что такое wsgi, да и сайт сделать можно.

Но если вы хотите написать свой собственный веб-фреймворк, вы должны понимать wsgi.

Оглядываясь назад, когда мы используем python для веб-разработки, мы обычно разрабатываем на основе веб-фреймворка, django или других фреймворков, таких как flask. После завершения разработки бизнеса его необходимо развернуть на сервере для обеспечения внешнего доступа.

В это время вы заходите в интернет и вам скажут, что для деплоя нужно использовать gunicorn или uwsgi. Итак, что такое gunicorn и uwsgi.

Посмотрите на эту картинку и вы все поймете, я нашел картинку в интернете

Роль, которую здесь играет uwsgi или gunicorn, — это роль веб-сервера, который здесь представляет собой сервер программного уровня, который обрабатывает HTTP-запросы, отправленные браузером, и возвращает ответ внешнему интерфейсу. Основной задачей веб-фреймворка является обработка сгенерированных бизнес-логикой результатов на веб-сервере, а затем веб-сервер возвращает их в браузер.

Связь между веб-платформой и веб-сервером должна соответствовать набору спецификаций, то есть WSGI.

Зачем делать такой набор норм? Спецификация предназначена для унификации стандарта и облегчения использования всеми

Представьте себе, что зарядные порты наших мобильных телефонов теперь все Type-c. Type-c — это спецификация. Производители мобильных телефонов производят мобильные телефоны в соответствии с этой спецификацией, а производители зарядных устройств производят зарядные устройства в соответствии со спецификациями Type-c. телефоны Можно использовать с зарядными устройствами разных производителей. Apple, с другой стороны, разработала собственный набор спецификаций, что в конечном итоге привело к невозможности зарядных устройств Android заряжать Apple.

![

](Страх 9-Nuggets.byte IMG.com/to S-talent-i-can 3…)

Так как же написать прикладную (каркасную) программу и сервер, соответствующие спецификации WSGI?

Как показано на рисунке выше, веб-сервер находится слева, а веб-фреймворк или приложение — справа.

заявление

WSGI предусматривает, что приложение должно быть вызываемым объектом (вызываемым объектом может быть функция, класс или реализация__call__instance object) и должен принимать два параметра, возвращаемое значение объекта должно быть итерируемым объектом.

Мы можем написать пример простейшего приложения

HELLO_WORLD = b"Hello world!\n"

def application(environ, start_response):
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return [HELLO_WORLD]

приложение является функцией, оно должно быть вызываемым объектом, а затем получает два параметра, два параметра: environ и start_response

  • environ — это словарь, в котором хранится весь контент, связанный с HTTP-запросами, такой как заголовки, параметры запроса и т. д.
  • start_response — это функция, передаваемая сервером WSGI для передачи заголовка ответа и кода состояния серверу.

Вызов функции start_response отвечает за передачу заголовка ответа и кода состояния на сервер, а тело ответа возвращается на сервер функцией приложения.Эти две функции обеспечивают полный HTTP-ответ.

Но любой веб-фреймворк, реализующий wsgi, будет иметь такой вызываемый объект.

сервер

Что делает сервер WSGI, так это каждый раз получает HTTP-запрос, создает объект окружения, затем вызывает объект приложения и, наконец, возвращает HTTP-ответ в браузер.

Ниже приведен код полного сервера wsgi.

import socket
import sys
from io import StringIO


class WSGIServer(object):
    address_family = socket.AF_INET
    socket_type = socket.SOCK_STREAM
    request_queue_size = 1

    def __init__(self, server_address):
        # Create a listening socket
        self.listen_socket = listen_socket = socket.socket(
            self.address_family,
            self.socket_type
        )
        # Allow to reuse the same address
        listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # Bind
        listen_socket.bind(server_address)
        # Activate
        listen_socket.listen(self.request_queue_size)
        # Get server host name and port
        host, port = self.listen_socket.getsockname()[:2]
        self.server_name = socket.getfqdn(host)
        self.server_port = port
        # Return headers set by Web framework/Web application
        self.headers_set = []

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

    def serve_forever(self):
        listen_socket = self.listen_socket
        while True:
            # New client connection
            self.client_connection, client_address = listen_socket.accept()
            # Handle one request and close the client connection. Then
            # loop over to wait for another client connection
            self.handle_one_request()

    def handle_one_request(self):
        self.request_data = request_data = self.client_connection.recv(1024)
        # Print formatted request data a la 'curl -v'
        print(''.join(
            '< {line}\n'.format(line=line)
            for line in request_data.splitlines()
        ))
        self.parse_request(request_data)
        # Construct environment dictionary using request data
        env = self.get_environ()
        # It's time to call our application callable and get
        # back a result that will become HTTP response body
        result = self.application(env, self.start_response)
        # Construct a response and send it back to the client
        self.finish_response(result)

    def parse_request(self, text):
        request_line = text.splitlines()[0]
        request_line = request_line.rstrip('\r\n')
        # Break down the request line into components
        (self.request_method,  # GET
         self.path,  # /hello
         self.request_version  # HTTP/1.1
         ) = request_line.split()

    def get_environ(self):
        env = {}
        # The following code snippet does not follow PEP8 conventions
        # but it's formatted the way it is for demonstration purposes
        # to emphasize the required variables and their values
        #
        # Required WSGI variables
        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):
        # Add necessary server headers
        server_headers = [
            ('Date', 'Tue, 31 Mar 2015 12:54:48 GMT'),
            ('Server', 'WSGIServer 0.2'),
        ]
        self.headers_set = [status, response_headers + server_headers]
        # To adhere to WSGI specification the start_response must return
        # a 'write' callable. We simplicity's sake we'll ignore that detail
        # for now.
        # return self.finish_response

    def finish_response(self, result):
        try:
            status, response_headers = self.headers_set
            response = 'HTTP/1.1 {status}\r\n'.format(status=status)
            for header in response_headers:
                response += '{0}: {1}\r\n'.format(*header)
            response += '\r\n'
            for data in result:
                response += data
            # Print formatted response data a la 'curl -v'
            print(''.join(
                '> {line}\n'.format(line=line)
                for line in response.splitlines()
            ))
            self.client_connection.sendall(response)
        finally:
            self.client_connection.close()


SERVER_ADDRESS = (HOST, PORT) = 'localhost', 8080


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


if __name__ == '__main__':
    httpd = make_server(SERVER_ADDRESS, application)
    print('WSGIServer: Serving HTTP on port {port} ...\n'.format(port=PORT))
    httpd.serve_forever()

Конечно, если вы просто пишете сервер для среды разработки, вам не нужно заморачиваться, чтобы сделать свои собственные колеса, потому что встроенный модуль python обеспечивает функцию сервера wsgi.

from wsgiref.simple_server import make_server
srv = make_server('localhost', 8080, application)
srv.serve_forever()

Пока 3 строки кода могут обеспечить сервер wsgi, это очень удобно? Наконец, давайте посетим и проверим эффект от браузера, инициирующего запрос

Вышеприведенное является введением в wsgi.Если у вас есть глубокое понимание wsgi, вы можете ознакомиться с PEP333.

Статья одновременно публикуется в паблике: Zen of python, прошу обратить внимание