Анализ всего процесса Django от запуска до запроса и ответа — входная версия

Django

используется в этой статьеDjangoВерсия кода:2.1.3

Анализ, проведенный в данной статье, не ограничивается однимDjangoверсии, но постараюсь обсудить версию2.0+

Обзор процесса

Django 处理请求流程

Обзор:

Djangoи другиеWebобрамленныйHTTPПроцесс обработки примерно такой же: первый проходRequest MiddlewareОпределите объект запроса, а затем передайте значение по умолчаниюURLуказать на метод и, наконец, передатьResponse MiddlewareВыполните пользовательскую обработку объекта ответа.

Подробности:

  1. [Пуск->WSGI] Запустить любым способомDjangoСоздайтеWSGIServerэкземпляр класса
  2. Пользователь запрашивает определенныйDjangoстраница
  3. [WSGI]Django WSGIServerПолучить инициализацию запроса клиента (браузера)WSGIHandlerпример
  4. [WSGI->Загрузить конфигурацию] Импортsettingнастроить иDjangoкласс исключений
  5. [WSGI->Промежуточное ПО] ЗагрузкаsettingПО промежуточного слоя установлено в
  6. [промежуточное ПО] создать_request_middleware,_view_middleware,_response_middleware,_exception_middlewareчетыре списка
  7. [Промежуточное ПО] Выполнение обхода_request_middleware,правильноrequestПроцесс: если вернутьсяNoneПерейти к 8; если вернуться напрямуюHttpResponseОбъект переходит на 12
  8. [Преобразователь URL] разрешитьurlи сделать совпадение (при условии, что совпадение успешно)
  9. [Промежуточное ПО] Выполнение обхода_view_middleware,правильноrequestПроцесс: если вернутьсяNoneВведите до 10; если вернуться напрямуюHttpResponseОбъект переходит на 12.
  10. [промежуточное ПО] реализацияurlсовпалоviewЛогика: если возникает исключение, перейти к 11; если оно возвращается нормальноHttpResponseОбъект переходит на 12
  11. [Промежуточное ПО] Выполнение обхода_exception_middleware
  12. [Промежуточное ПО] Выполнение обхода_response_middleware,правильноHttpResponseпроцесс и, наконец, возвратresponse

запускать

В среде разработки мы обычно выполняем его через командную строку.runserverЗаказ,ruserverкоманда использоватьDjangoавтономныйWeb Server, а в формальной среде обычно используетсяNginx+uWSGIмодель.

В любом случае, когда вы начинаете проект, он делает две вещи:

  1. СоздаватьWSGIServerЭкземпляр класса для приема пользовательских запросов.
  2. когда пользовательHTTPПри поступлении запроса укажитеWSGIHandler, используемый для обработки пользовательских запросов и ответов, этоHandlerзаключается в обработке всегоRequestОсновной.

WSGI

WSGI: полное имяWeb Server Gateway Interface.

WSGIне сервер,Pythonмодули, фреймворки,APIили любое программное обеспечение, просто спецификация, описывающаяWeb ServerКак работать сWeb ApplicationСпецификации для связи.

WSGIСоглашение в основном включаетserverиapplicationДве части:

  1. WSGI ServerОтветственный за прием заявок от клиентов, будетrequestвперед кapplication,будетapplicationвозвращениеresponseвозврат клиенту;
  2. WSGI Applicationполученоserverперенаправленrequest, обработать запрос и вернуть результат обработки вserver.applicationОн может включать в себя несколько промежуточных программ стекового типа (промежуточных программ), которые необходимо внедрять одновременно.serverиapplication, так что может бытьWSGIсервер сWSGIМодерация между приложениями: для сервера промежуточное ПО действует как приложение, а для приложения промежуточное ПО действует как сервер.

Django WSGI Application

WSGI ApplicationДолжен быть реализован как вызываемый объект, такой как: функции, методы, классы (включаяcallметод).

Нужно получить два параметра:

  1. Словарь, содержащий запрошенную клиентом информацию, а также другую информацию. Его можно рассматривать как контекст запроса, обычно называемыйenvironment(В кодировании это часто сокращается какenviron,env);
  2. для отправкиHTTPстатус ответа (HTTP Status), заголовки ответов (HTTP Headers) функция обратного вызова;

Верните статус ответа и заголовки ответа через функцию обратного вызова вWSGI Serverи возвращает тело ответа, которое является итерируемым и содержит несколько строк. НижеDjango WSGI ApplicationКонкретная реализация:

class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 本文作者注:加载中间件
        self.load_middleware()

    def __call__(self, environ, start_response):
        set_script_prefix(get_script_name(environ))
        # 本文作者注:处理请求前发送信号
        signals.request_started.send(sender=self.__class__, environ=environ)
        request = self.request_class(environ)
        response = self.get_response(request)

        response._handler_class = self.__class__

        status = '%d %s' % (response.status_code, response.reason_phrase)
        response_headers = list(response.items())
        for c in response.cookies.values():
            response_headers.append(('Set-Cookie', c.output(header='')))
        # 本文作者注:将响应的 header 和 status 返回给 server
        start_response(status, response_headers)
        if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
            response = environ['wsgi.file_wrapper'](response.file_to_stream)
        return response

Как можно видетьDjango WSGI ApplicationПроцесс включает в себя:

  1. Загрузить все промежуточное ПО, выполнить операции, связанные с фреймворком, установить префикс сценария текущего потока и отправить сигнал запуска запроса;
  2. обработать заявку, позвонитьget_response()Метод обрабатывает текущий запрос, и основная логика этого метода заключается в следующем:urlconfнайти соответствующийviewиcallback, выполнять различныеmiddlewareиcallback;
  3. вызванныйWSGI Serverвходящийstart_response()метод ответитheaderиstatusВернуться кWSGI Server;
  4. Возвращает тело ответа.

Django WSGI Server

ответственный за получениеHTTPзапрос, передать запросDjango WSGI Application,Зависит отDjango WSGI Applicationвозврат после обработки запросаresponse. отDjangoвстроенныйserverВзгляните на конкретную реализацию в качестве примера.

пройти черезrunserverкоманда для запускаDjangoПроект вызовет следующее при запускеrunметод, создайтеWSGIServerэкземпляр , а затем вызовите егоserve_forever()способ запуска службы.

def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
    server_address = (addr, port)
    if threading:
        httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
    else:
        httpd_cls = server_cls
    httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
    if threading:
        httpd.daemon_threads = True
    httpd.set_app(wsgi_handler)
    httpd.serve_forever()

На рисунке ниже показаноWSGI ServerКлючевые классы и методы в потоке обработки сервера (из:ссылка_1)

WSGI Server 服务器处理流程中关键的类和方法

1. WSGIServer

run()метод создастWSGIServerНапример, основной функцией является получение запросов от клиентов, передача запроса вWSGI Application,ПотомWSGI Applicationвозвращениеresponseвозвращен клиенту.

  • При создании экземпляра указываетсяHTTPпросилhandler:WSGIRequestHandlerсвоего рода;
  • пройти черезset_appиget_appУстановить метод и получитьWSGIApplicationпримерwsgi_handler;
  • иметь дело сHTTPПо запросу звонитеhandler_requestметод, создастWSGIRequestHandlerобработка экземпляраHTTPпросить;
  • WSGIServerсерединаget_requestметод переданsocketпринять данные запроса;

2. WSGIRequestHandler

  • Зависит отWSGIServerвызовhandle_requestПри создании экземпляра передатьrequest,cient_address,WSGIServerтри параметра,__init__Когда метод создается, он также вызывает свой собственныйhandleметод;
  • handleметод создастServerHandlerэкземпляр, а затем вызвать егоrunспособ обработки запроса;

3. ServerHandler

  • WSGIRequestHandlerВ своемhandleвызов методаrunметод, пройтиself.server.get_app()параметры, получитьWSGIApplication, а затем вызовите экземпляр (call),Получатьresponse, который пройдет вstart_responseобратный вызов, используемый для обработки возвращенногоheaderиstatus;
  • пройти черезapplicationПолучатьresponseпозже, черезfinish_responseвозвращениеresponse;

4. WSGIHandler (т.е. приложение Django WSGI)

  • WSGIв соглашенииapplication, получает два параметра,environСловарь содержит информацию, запрошенную клиентом, и другую информацию, которую можно рассматривать как контекст запроса,start_responseдля отправки обратноstatusиheaderфункция обратного вызова

Хотя тот, что вышеDjango WSGI ServerОн включает несколько реализаций классов и взаимные ссылки, но на самом деле принцип заключается в вызовеWSGIHandler, параметры входящего запроса и метод обратного вызоваstart_response()и вернуть ответ клиенту.

Python wsgiref simple_server

существуетPython3.7Исходный код даетsimple_serverДело находится вpython3.7/wsgiref/simple_server.py.

Модуль реализует простуюHTTPсервер и дает простойdemo, вы можете запустить его напрямую, и результат выполнения отобразит переменные среды, задействованные в запросе, в браузере. который включает в себя всюHTTPВсе компоненты запроса:ServerHandler,WSGIServer,WSGIRequestHandlerа такжеdemo_appупрощенный вариант представленияWSGIApplication.

Если вам интересно, вы можете сами взглянуть на исходный код.

загрузить конфигурацию

Djangoконфигурация находится в{project_name}/settings.pyопределяется в , может бытьDjangoКонфигурация также может быть пользовательской конфигурацией, и обеdjango.conf.settingsдоступ.

промежуточное ПО - промежуточное ПО

Обзор:

DjangoсерединаMiddlewareПодобно облегченной системе плагинов в нижнем слое, она может вмешиватьсяDjangoПроцесс запроса и ответа изменен глобальноDjangoвходное и выходное содержимое. Из общей схемы процесса видно, чтоDjangoСуть процесса обработки запроса заключается вMiddleware,DjangoВсе запросы и ответы вMiddlewareУчастие.

Подробности:

中间件流程图

ОдинHTTPзапрос, который сначала преобразуется вHttpRequestобъект, который затем передаетсяRequest Middlewareобрабатывать, если он возвращаетсяHttpResponseобъект, он передается непосредственно вResponse MiddlewareСделайте последние штрихи. если неRequest MiddlewareпосетитURLнастраивать, ориентироватьсяviewиметь дело сHttpRequestобъект после определенияview, но если он не был выполнен, системаHttpRequestобъект переданView Middlewareобрабатывать, если он возвращаетсяHttpResponseобъект, тоHttpResponseобъект будет переданResponse Middlewareдля последующей обработки, иначе детерминированныйviewфункция процесса и возвратаHttpResponseобъект, если исключение выбрасывается и выбрасывается в течение всего процесса, оно будетException Middlewareдля обработки.

Порядок выполнения промежуточного ПО

中间件流程图

На этапе запроса перед вызовом представленияDjangoв соответствии сsetting.pyПорядок настроек, выполнение обхода приложения сверху внизRequest Middleware. Вы можете думать об этом как о луковице: каждый класс промежуточного программного обеспечения представляет собой «слой», покрывающий ядро ​​луковицы. Если запрос проходит через все слои луковицы (каждый вызовget_response), чтобы передать запрос на следующий уровень, вплоть до представления ядра, тогда ответ будет проходить через каждый уровень (в обратном порядке) на обратном пути.

Углубленный взгляд на то, как написать собственное промежуточное программное обеспечение, т.е. промежуточное программное обеспечение.

Легко написать собственное ПО промежуточного слоя, каждый компонент ПО промежуточного слоя является автономным.Python Class, вы можете настроитьClassНапишите один или несколько следующих методов:

process_request

Стиль функции:process_request(request);

Разбор параметров:requestЯвляетсяHTTPRequestобъект;

Время звонка: вкл.Djangoрешить, что выполнитьviewДо,process_request()будет вызван по запросу;

произвести ответ: он должен вернутьNoneилиHttpResponseобъект, если возвращаетсяNone,Djangoпродолжит обработку запроса; если он вернетHTTPResponseобъект,Djangoперейдет прямо кResponse Middleware;

process_view

Стиль функции:process_view(request, view_func, view_args, view_kwargs);

Разбор параметров:requestЯвляетсяHTTPRequestобъект,view_funcдаDjangoфункция, которая будет вызываться (точнее, объект функции, а не строка, представляющая имя функции),view_argsэто значение, которое будет передано в представление*args,view_kwargsэто значение, которое будет передано в представление**kwargs,view_argsиview_kwargsни включатьrequest;

Время звонка:process_view()Будет вDjangoперечислитьviewбыл вызван раньше;

произвести ответ: он должен вернутьNoneилиHttpResponseобъект, если возвращаетсяNone,Djangoпродолжит обработку запроса; если он вернетHTTPResponseобъект,Djangoперейдет прямо кResponse Middleware;

ПС: кромеCsrfViewMiddlewareВнешнее промежуточное ПО запускается перед запуском представления или вprocess_view()доступ вrequest.POSTсделает все последующие виды неизменяемымиrequest, поэтому его следует избегать, насколько это возможно.

process_template_response

Стиль функции:process_template_response(request, response);

Разбор параметров:requestЯвляетсяHttpRequestобъект,responseЯвляетсяTemplateResponseобъект (или аналогичный),DjangoView или промежуточное ПО возвращается;

Время звонка: еслиresponseПример этогоrender()метод,process_template_response()Вызывается сразу после выполнения представления, указывая, что этоTemplateResponseобъект (или аналогичный объект);

Генерировать ответ: этот метод должен возвращать реализацию, реализующуюrender()методTemplateResponseобъект (или аналогичный), который изменяет данныйresponseобъект или создать совершенно новыйTemplateResponseобъект (или аналогичный объект);

PS: на этапе обработки ответа промежуточное ПО выполняется в обратном порядке, включаяprocess_template_response;

process_response

Стиль функции:process_response(request, response);

Разбор параметров:requestЯвляетсяHttpRequestобъект,responseЯвляетсяHttpResponseобъект, поDjangoView или промежуточное ПО возвращается;

Время звонка:process_requestВызывается до того, как все ответы будут возвращены клиенту;

Создать ответ: этот метод должен возвращатьHttpRequestобъект, который изменяет данныйresponseобъект или создать совершенно новыйHttpRequestобъект;

PS:process_responseвсегда называется, что означает вашprocess_responseнельзя полагаться наprocess_request

process_exception

Стиль функции:process_exception(request, exception);

Разбор параметров:requestЯвляетсяHttpRequestобъект,exceptionбросается взглядомExceptionобъект;

Время вызова: когда представление выдает исключение,Djangoпозвонюprocess_exceptionобрабатывать;

произвести ответ: он должен вернутьNoneилиHttpResponseобъект, если возвращаетсяNone,Djangoпродолжит обработку запроса; если он вернетHTTPResponseобъект, объект шаблона иResponse Middlewareбудут возвращены непосредственно клиенту, в противном случае будет инициирована обработка исключений по умолчанию. ;

URL Resolver

Обзор:

Предположение: промежуточное ПО облегчает выполнение_request_middleware,_view_middlewareвернуться послеNone.

когдаDjangoобход завершен_request_middlewareзатем получит обработанныйrequestобъект, в настоящее времяDjangoбудет в паре по порядкуurlВыполнять обычное сопоставление, если совпадение не удалось, будет выдано исключение; если совпадение успешно,Djangoбудет продолжать цикл_view_middlewareИ продолжайте выполнять только что успешно совпавшие после выполненияview.

существуетsettingсуществует одинROOT_URLCONF, что указывает наurls.pyфайл, согласно которомуurlconf, по сути онurlи таблицу сопоставления между функциями представления, а затем передатьresolverРазобратьurl, найти первое соответствиеview.

Подробности:

URL Resolver流程图

Расположение исходного кода важной функции:

_path: django/urls/conf.py
URLPattern: django/urls/resolvers.py
ResolverMatch: django/urls/resolvers.py
URLResolver: django/urls/resolvers.py

Исходный код довольно длинный, поэтому я не буду его публиковать, если вам интересно, посмотрите сами.

  1. пройти черезurlpatternsвыполнение конфигурации_pathфункция;
  2. _pathфункция судить: если этоlistилиtuple, затем применитьURLResolverпроцесс, перейдите к 4; если обычный вызываемыйviewфункция, использованиеURLPatternОбработать, перейти к; если совпадение не удалось, выдать исключение;
  3. URLPatternВыполнить после инициализации соответствующего значенияresolveМетод: если совпадение прошло успешно, вернутьResolverMatch;если совпадение не удалось, генерировать исключение;
  4. URLResolverсовпадениеpathЕсли совпадение прошло успешно, продолжайте сопоставлять егоurl_patterns, перейти к 5, совпадение не удалось, выдать исключение;
  5. совпадениеurl_patterns: еслиurlpatternЕсли совпадение прошло успешно, вернитесьResolverMatch; еслиURLResolverрекурсивный вызовURLResolverПерейти к 4, если совпадение не удалось, создать исключение;

Можно обнаружить, что ключом ко всему процессу являетсяResolverMatch,URLPatternиURLResolverТри класса, из них:ResolverMatchрезультат сопоставления, который содержит информацию, необходимую после успешного сопоставления;URLPatternЯвляетсяurlОбъект картографической информации, содержащийurlСопоставьте соответствующие вызываемые объекты и другую информацию;URLResolverреализуетсяurlмаршрутизация, парсингurlключевое место этоurl_patternsлибоURLPatternтак же может бытьURLResolver, именно из-за этой конструкцииurlуровневый анализ.

Обзор

Реальный процесс запроса и ответа определенно сложнее, чем я упомянул, но мои способности действительно ограничены, и я могу понять только этот уровень в настоящее время.Если я ошибаюсь, пожалуйста, поправьте меня.

Использованная литература:

  1. Краткая книга: вам нужно понимать веб-разработку на Python: WSGI и uWSGI Автор: rainybowe
  2. Самородки: процесс Django от запроса до ответа
  3. Академия современной магии: Python и Django - Анализ процесса архитектуры Django
  4. Краткая книга: маршрутизация URL (URLResolver) для анализа исходного кода django Автор: 2hanson
  5. Официальная документация Джанго