Интерпретация исходного кода Python Web Flask (1)

Python Flask
Интерпретация исходного кода Python Web Flask (1)

обо мне
Небольшой программист в мире программирования, в настоящее время работает руководителем команды в предпринимательской команде.Стек технологий включает Android, Python, Java и Go, который также является основным стеком технологий нашей команды. Контакт: hylinux1024@gmail.com

0x00 Что такое WSGI

Web Server Gateway Interface
это состоит изPythonСтандарт определяет наборWeb ServerиWeb ApplicationизСпецификация взаимодействия интерфейса.

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

это чтоWeb Server(Webсервер) и что такоеWeb Application(Webприменение)?
Примеры легко понять, например, общиеWebКаркас приложения имеетDjango,Flaskподожди, покаWebсервер имеетuWSGI,GunicornЖдать.WSGIОн должен определить спецификацию взаимодействия интерфейса между двумя концами.

0x01 Что такое Werkzeug

Werkzeug is a comprehensive WSGI web application library.

Werkzeugэто набор реализацийWSGIСтандартная библиотека функций. Мы можем использовать его для созданияWeb Application(Webприменение).例如本文介绍的FlaskКаркас приложения основан наWerkzeugразвивать.

Здесь мы используемWerkzeugЗапустите простое серверное приложение

from werkzeug.wrappers import Request, Response

@Request.application
def application(request):
    return Response('Hello, World!')

if __name__ == '__main__':
    from werkzeug.serving import run_simple

    run_simple('localhost', 4000, application)

После запуска в консоли можно увидеть следующую информацию

 * Running on http://localhost:4000/ (Press CTRL+C to quit)

Открыть в браузереhttp://localhost:4000/Инструкции см. в следующей информации.

Hello, World!

0x02 Что такое фляга

Flask is a lightweight WSGI web application framework.

Flaskлегкийwebкаркас приложения, который работает наwebПриложение на сервере.FlaskНижний слой инкапсулированWerkzeug.

использоватьFlaskразрабатыватьwebПриложение очень простое

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return f'Hello, World!'

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

Просто как тот.

Далее посмотримFlaskПроцесс запуска приложения.

0x03 запуск процесса

с адреса проектаGitHub.com/pallets/Фала…Исходный код центраcloneвниз, затем переключитесь на версию 0.1tag. Зачем использовать версию 0.1? Поскольку это версия, написанная автором в начале, объем кода должен быть минимальным, и легко увидеть общие идеи автора по кодированию.

Ниже самый простойDemoначать искатьFlaskкак начать.我们知道程序启动是执行了以下方法

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

и

app = Flask(__name__)

ОткрытымFlaskв исходном коде__init__метод

Flask.init()
        def __init__(self, package_name):
        #: 是否打开debug模式
        self.debug = False

        #: 包名或模块名
        self.package_name = package_name

        #: 获取app所在目录
        self.root_path = _get_package_path(self.package_name)

        #: 存储视图函数的字典,键为函数名称,值为函数对象,使用@route装饰器进行注册
        self.view_functions = {}

        #: 存储错误处理的字典.  键为error code, 值为处理错误的函数,使用errorhandler装饰器进行注册
        self.error_handlers = {}

        #: 处理请求前执行的函数列表,使用before_request装饰器进行注册
        self.before_request_funcs = []

        #: 处理请求前执行的函数列表,使用after_request装饰器进行注册
        self.after_request_funcs = []

        #: 模版上下文
        self.template_context_processors = [_default_template_ctx_processor]
        #: url 映射
        self.url_map = Map()
        
        #: 静态文件
        if self.static_path is not None:
            self.url_map.add(Rule(self.static_path + '/<filename>',
                                  build_only=True, endpoint='static'))
            if pkg_resources is not None:
                target = (self.package_name, 'static')
            else:
                target = os.path.join(self.root_path, 'static')
            self.wsgi_app = SharedDataMiddleware(self.wsgi_app, {
                self.static_path: target
            })

        #: 初始化 Jinja2 模版环境. 
        self.jinja_env = Environment(loader=self.create_jinja_loader(),
                                     **self.jinja_options)
        self.jinja_env.globals.update(
            url_for=url_for,
            get_flashed_messages=get_flashed_messages
        )

существуетFlaskРазличные операции инициализации выполняются в конструкторе .

Тогда естьвоплощать в жизньapp.run()метод

app.run()
def run(self, host='localhost', port=5000, **options):
    """启动本地开发服务器.  如果debug设置为True,那么会自动检查代码是否改动,有改动则会自动执行部署
    :param host: 监听的IP地址. 如果设置为 ``'0.0.0.0'``就可以进行外部访问
    :param port: 端口,默认5000
    :param options: 这个参数主要是对应run_simple中需要的参数
    """
    from werkzeug.serving import run_simple
    if 'debug' in options:
        self.debug = options.pop('debug')
    options.setdefault('use_reloader', self.debug)
    options.setdefault('use_debugger', self.debug)
    return run_simple(host, port, self, **options)

runОчень лаконично, в основном по вызовуwerkzeug.servingсерединаrun_simpleметод.

открыть сноваrun_simpleисходный код

rum_simple()

def run_simple(hostname, port, application, use_reloader=False,
               use_debugger=False, use_evalex=True,
               extra_files=None, reloader_interval=1, threaded=False,
               processes=1, request_handler=None, static_files=None,
               passthrough_errors=False, ssl_context=None):
    # 这方法还是比较短的,但是注释写得很详细,由于篇幅问题,就把源码中的注释省略了
    if use_debugger:
        from werkzeug.debug import DebuggedApplication
        application = DebuggedApplication(application, use_evalex)
    if static_files:
        from werkzeug.wsgi import SharedDataMiddleware
        application = SharedDataMiddleware(application, static_files)

    def inner():
        make_server(hostname, port, application, threaded,
                    processes, request_handler,
                    passthrough_errors, ssl_context).serve_forever()

    if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
        display_hostname = hostname != '*' and hostname or 'localhost'
        if ':' in display_hostname:
            display_hostname = '[%s]' % display_hostname
        _log('info', ' * Running on %s://%s:%d/', ssl_context is None
             and 'http' or 'https', display_hostname, port)
    if use_reloader:
        # Create and destroy a socket so that any exceptions are raised before
        # we spawn a separate Python interpreter and lose this ability.
        test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        test_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        test_socket.bind((hostname, port))
        test_socket.close()
        run_with_reloader(inner, extra_files, reloader_interval)
    else:
        inner()

существуетrum_simpleМетод также определяет вложенный методinner(), это основная часть метода.

inner()

def inner():
    make_server(hostname, port, application, threaded, processes, request_handler, passthrough_errors, ssl_context).serve_forever()

существуетinner()метод, вызовmake_server(...).serve_forever()Служба запущена.

make_server()

def make_server(host, port, app=None, threaded=False, processes=1,
                request_handler=None, passthrough_errors=False,
                ssl_context=None):
    """Create a new server instance that is either threaded, or forks
    or just processes one request after another.
    """
    if threaded and processes > 1:
        raise ValueError("cannot have a multithreaded and "
                         "multi process server.")
    elif threaded:
        return ThreadedWSGIServer(host, port, app, request_handler,
                                  passthrough_errors, ssl_context)
    elif processes > 1:
        return ForkingWSGIServer(host, port, app, processes, request_handler,
                                 passthrough_errors, ssl_context)
    else:
        return BaseWSGIServer(host, port, app, request_handler,
                              passthrough_errors, ssl_context)

существуетmake_server()соответствует количеству потоков или процессов, которые будут созданы вWSGIсервер.Flaskсоздается по умолчаниюBaseWSGIServerсервер.

BaseWSGIServer, ThreadedWSGIServer, ForkingWSGIServer
class BaseWSGIServer(HTTPServer, object):
    ...

class ThreadedWSGIServer(ThreadingMixIn, BaseWSGIServer):
    """A WSGI server that does threading."""
    ...

class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer):
    """A WSGI server that does forking."""
    ...

Видно, что их предыдущее отношение наследования выглядит следующим образом

ОткрытымBaseWSGIServerизstart_server()метод

start_server()
def serve_forever(self):
    try:
        HTTPServer.serve_forever(self)
    except KeyboardInterrupt:
        pass

Можно увидеть окончательное использованиеHTTPServer中的启动服务的方法。 иHTTPServerдаPythonИнтерфейсы в стандартной библиотеке классов.

HTTPServerдаsocketserver.TCPServerподкласс

socketserver.TCPServer

Если вы хотите использоватьPythonБиблиотека среднего класса запускаетhttp serverПохоже на код должен выглядеть так

import http.server
import socketserver

PORT = 8000

Handler = http.server.SimpleHTTPRequestHandler

with socketserver.TCPServer(("", PORT), Handler) as httpd:
    print("serving at port", PORT)
    httpd.serve_forever()

В этот момент здесь начинается запуск всего сервиса.

Вызывающий процесс этого процесса

graph TD

A[Flask]-->B[app.run]
B[app.run]-->C[werkzeug.run_simple]
C[werkzeug.run_simple]-->D[BaseWSGIServer]
D[BaseWSGIServer]-->E[HTTPServer.serve_forever]
E[HTTPServer.serve_forever]-->F[TCPServer.serve_forever]

0x04 Суммировать

WSGIдаWEBсервер сWEBСпецификация интерфейса для взаимодействия между приложениями.werkzeugЭто для достижения стандартной библиотеки функции,Flaskкаркас основан наwerkzeugбыть реализованным. мы начинаем сFlask.run()Метод запускает запуск службы, который отслеживает весь процесс запуска службы.

0x05 Учебные материалы