本篇內(nèi)容主要講解“Flask請(qǐng)求處理流程是什么”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“Flask請(qǐng)求處理流程是什么”吧!
在做網(wǎng)站、網(wǎng)站制作過(guò)程中,需要針對(duì)客戶的行業(yè)特點(diǎn)、產(chǎn)品特性、目標(biāo)受眾和市場(chǎng)情況進(jìn)行定位分析,以確定網(wǎng)站的風(fēng)格、色彩、版式、交互等方面的設(shè)計(jì)方向。創(chuàng)新互聯(lián)建站還需要根據(jù)客戶的需求進(jìn)行功能模塊的開發(fā)和設(shè)計(jì),包括內(nèi)容管理、前臺(tái)展示、用戶權(quán)限管理、數(shù)據(jù)統(tǒng)計(jì)和安全保護(hù)等功能。
handler的處理流程.(路由)
ThreadedWSGIServer.(多線程非阻塞服務(wù)器)
多線程如何保證請(qǐng)求安全.(ctx)
# 1.wsgi階段 WSGI -> Flask().__call__ # 2.Flask階段 wsgi_app() -> full_dispatch_request() -> dispatch_request() -> view_handler() full_dispatch_request: 框架實(shí)現(xiàn). 1.請(qǐng)求封裝, 請(qǐng)求ctx入棧 2.觸發(fā)鉤子函數(shù).(try_trigger_before_first_request_functions) 3.發(fā)送請(qǐng)求開始信號(hào).(request_started.send(self)) 4.觸發(fā)dispatch_request. 調(diào)用到用戶的handler view_handler: 用戶編寫, 實(shí)現(xiàn)業(yè)務(wù)
call(environ, start_response))
flask.app.py 實(shí)現(xiàn)了wsgi接口.(上線可以托管在uwsgi/gunicorn后面)
class Flask(_PackageBoundObject): def dispatch_request(self): # 1.從_request_ctx_stack棧頂獲取請(qǐng)求. req = _request_ctx_stack.top.request if req.routing_exception is not None: self.raise_routing_exception(req) # 2.Rule對(duì)象包含了url到viewhandler的映射 rule = req.url_rule # ... # 3.取出handler并執(zhí)行 return self.view_functions[rule.endpoint](**req.view_args) def full_dispatch_request(self): # 1.觸發(fā)first_request_functions self.try_trigger_before_first_request_functions() try: # 2.發(fā)送request_started信號(hào), 觸發(fā)所有注冊(cè)了信號(hào)的函數(shù) request_started.send(self) # 3.預(yù)處理請(qǐng)求 rv = self.preprocess_request() if rv is None: # 4.處理請(qǐng)求,調(diào)用到用戶注冊(cè)的對(duì)應(yīng)方法 rv = self.dispatch_request() except Exception as e: rv = self.handle_user_exception(e) # 5.結(jié)束請(qǐng)求 return self.finalize_request(rv) def wsgi_app(self, environ, start_response): # 1.請(qǐng)求封裝ctx ctx = self.request_context(environ) error = None try: try: # 2.ctx入棧 ctx.push() response = self.full_dispatch_request() except Exception as e: error = e response = self.handle_exception(e) except: # noqa: B001 error = sys.exc_info()[1] raise return response(environ, start_response) finally: if self.should_ignore_error(error): error = None # 3.ctx自動(dòng)出棧 ctx.auto_pop(error) def __call__(self, environ, start_response): return self.wsgi_app(environ, start_response)
View: 基視圖類.
實(shí)現(xiàn)as_view
dispatch_request: 子類實(shí)現(xiàn)
MethodViewType:方法視圖元類.
cls.methods = methods 綁定方法集到類屬性
MethodView(with_metaclass(MethodViewType, View)): CBV
flask.views.py
http_method_funcs = frozenset( ["get", "post", "head", "options", "delete", "put", "trace", "patch"] ) class View(object): """ class MyView(View): methods = ['GET'] def dispatch_request(self, name): return 'Hello %s!' % name app.add_url_rule('/hello/', view_func=MyView.as_view('myview')) """ methods = None provide_automatic_options = None decorators = () def dispatch_request(self): raise NotImplementedError() @classmethod def as_view(cls, name, *class_args, **class_kwargs): def view(*args, **kwargs): # 1.實(shí)例化視圖類 self = view.view_class(*class_args, **class_kwargs) # 2.觸發(fā)dispatch_request return self.dispatch_request(*args, **kwargs) # 注冊(cè)裝飾器 if cls.decorators: view.__name__ = name view.__module__ = cls.__module__ for decorator in cls.decorators: view = decorator(view) view.view_class = cls view.__name__ = name view.__doc__ = cls.__doc__ view.__module__ = cls.__module__ view.methods = cls.methods view.provide_automatic_options = cls.provide_automatic_options # 路由注冊(cè)的函數(shù). 與url形成映射. 記錄在Flask().views_functions return view class MethodViewType(type): def __init__(cls, name, bases, d): super(MethodViewType, cls).__init__(name, bases, d) # 1.cls的屬性中沒(méi)有methods時(shí) if "methods" not in d: methods = set() for base in bases: if getattr(base, "methods", None): methods.update(base.methods) for key in http_method_funcs: if hasattr(cls, key): methods.add(key.upper()) if methods: # 2.綁定methods屬性到當(dāng)前cls cls.methods = methods class MethodView(with_metaclass(MethodViewType, View)): """A class-based view that dispatches request methods to the corresponding class methods. For example, if you implement a ``get`` method, it will be used to handle ``GET`` requests. :: class CounterAPI(MethodView): def get(self): return session.get('counter', 0) def post(self): session['counter'] = session.get('counter', 0) + 1 return 'OK' app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter')) """ def dispatch_request(self, *args, **kwargs): # 1.獲取視圖實(shí)例的meth meth = getattr(self, request.method.lower(), None) if meth is None and request.method == "HEAD": meth = getattr(self, "get", None) assert meth is not None, "Unimplemented method %r" % request.method # 2.執(zhí)行methd return meth(*args, **kwargs)
實(shí)現(xiàn)路由.
auth_view.py
auth = Blueprint('auth', __name__) # 綁定 /api/auth/login 到login函數(shù)上, 支持GET,POST @auth.route('/login', methods=['GET', 'POST']) def login(): pass # CBV class Files(views.MethodView): methods = ['POST', 'GET', 'PUT'] decorators = [auth_decorator] def get(self, *args, **kwargs): pass def post(self, *args, **kwargs): pass def put(self, *args, **kwargs): pass auth.add_url_rule('/files', view_func=Files.as_view(name='files'))
main.py
from auth_view import auth app = Flask(__name__) app.register_blueprint(auth, url_prefix='/api/auth')
開發(fā)環(huán)境的服務(wù)器.(默認(rèn): ThreadedWSGIServer)
多線程服務(wù)器啟動(dòng).(每個(gè)建立一個(gè)連接,創(chuàng)建一個(gè)線程處理請(qǐng)求).
WSGIRequestHandler: 處理請(qǐng)求,封裝(environ, start_response)
Flask().call(environ, start_response): 被執(zhí)行
app.run -> run_simple -> inner() -> ThreadedWSGIServer().serve_forever() -> BaseWSGIServer().serve_forever() -> HTTPServer().serve_forever() -> TCPServer().serve_forever() -> BaseServer().serve_forever()
flask.server.py
# app.run() class Flask(_PackageBoundObject): def run(self, host=None, port=None, debug=None, load_dotenv=True, **options): options.setdefault("use_reloader", self.debug) options.setdefault("use_debugger", self.debug) options.setdefault("threaded", True) try: run_simple(host, port, self, **options) finally: self._got_first_request = False
SocketServer.py: 標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)
serve_forever: 服務(wù)器啟動(dòng),等待請(qǐng)求
process_request: 處理請(qǐng)求
class BaseServer: def _handle_request_noblock(self): """Handle one request, without blocking. I assume that select.select has returned that the socket is readable before this function was called, so there should be no risk of blocking in get_request(). """ try: request, client_address = self.get_request() except socket.error: return if self.verify_request(request, client_address): try: # 調(diào)用ThreadingMixIn().process_request處理請(qǐng)求. self.process_request(request, client_address) except: self.handle_error(request, client_address) self.shutdown_request(request) def serve_forever(self, poll_interval=0.5): """Handle one request at a time until shutdown. Polls for shutdown every poll_interval seconds. Ignores self.timeout. If you need to do periodic tasks, do them in another thread. """ self.__is_shut_down.clear() try: while not self.__shutdown_request: r, w, e = _eintr_retry(select.select, [self], [], [], poll_interval) if self in r: self._handle_request_noblock() finally: self.__shutdown_request = False self.__is_shut_down.set() class TCPServer(BaseServer): pass class HTTPServer(SocketServer.TCPServer): pass
werkzeug.serving.py: 實(shí)現(xiàn)處理wsgi協(xié)議的請(qǐng)求
{ "wsgi.multiprocess": False, "HTTP_COOKIE": "csrftoken=; gfsessionid=", "SERVER_SOFTWARE": "Werkzeug/0.16.0", "SCRIPT_NAME": "", "REQUEST_METHOD": "GET", "PATH_INFO": "/api/auth/login", "SERVER_PROTOCOL": "HTTP/1.1", "werkzeug.server.shutdown":, "HTTP_CONNECTION": "keep-alive", "werkzeug.request": , "wsgi.input": ", mode "rb" at 0x10d04e030>, "wsgi.multithread": True, "REQUEST_URI": "/api/auth/login?username=test&password=test", "wsgi.version": "(1, 0)", "REMOTE_ADDR": "127.0.0.1", "HTTP_ACCEPT_ENCODING": "gzip, deflate" ... }
class WSGIRequestHandler(BaseHTTPRequestHandler, object): def run_wsgi(self): # 1.讀取socket的數(shù)據(jù),封裝成wsgi的env self.environ = environ = self.make_environ() def execute(app): # 2.調(diào)用Flask().__call__() 觸發(fā)請(qǐng)求流程 application_iter = app(environ, start_response) try: for data in application_iter: write(data) if not headers_sent: write(b"") finally: if hasattr(application_iter, "close"): application_iter.close() application_iter = None try: execute(self.server.app) except (_ConnectionError, socket.timeout) as e: self.connection_dropped(e, environ) except Exception: if self.server.passthrough_errors: raise pass class BaseWSGIServer(HTTPServer, object): """ 單線程,單進(jìn)程 wsgi server """ def init(): handler = WSGIRequestHandler #給http服務(wù)器綁定了wsgi請(qǐng)求的handler HTTPServer.__init__(self, server_address, handler) class ThreadedWSGIServer(ThreadingMixIn, BaseWSGIServer): """A WSGI server that does threading.""" multithread = True daemon_threads = True def make_server(): return ThreadedWSGIServer( host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd ) def inner(...): srv = make_server() # 啟動(dòng)服務(wù)器 srv.serve_forever()
app.run流程:
-> srv.serve_forever() -> ThreadedWSGIServer().serve_forever() -> BaseServer().serve_forever()
ThreadingMixIn: 實(shí)現(xiàn)了process_request接口. 啟動(dòng)子線程處理當(dāng)前請(qǐng)求
class ThreadingMixIn: """Mix-in class to handle each request in a new thread.""" daemon_threads = False def process_request_thread(self, request, client_address): try: self.finish_request(request, client_address) self.shutdown_request(request) except: self.handle_error(request, client_address) self.shutdown_request(request) def process_request(self, request, client_address): t = threading.Thread(target = self.process_request_thread, args = (request, client_address)) t.daemon = self.daemon_threads t.start()
serve_forever 中使用非阻塞處理請(qǐng)求,一旦請(qǐng)求可處理,執(zhí)行self.process_request, 調(diào)用ThreadingMixIn().process_request()
啟動(dòng)新的線程處理請(qǐng)求.
主線程繼續(xù)執(zhí)行serve_forever()
新線程的啟動(dòng)執(zhí)行流程.(觸發(fā)Flask().call())
1.process_request_thread() 2.self.finish_request(request, client_address) -> BaseServer().finish_request(self, request, client_address): -> self.RequestHandlerClass(request, client_address, self) -> WSGIRequestHandler().handle() -> WSGIRequestHandler().run_wsgi() -> app(environ, start_response) # Flask().__call__() 3.self.shutdown_request(request)
參考
werkzeug.local.py
Local
LocalProxy
LocalStack
LocalManager 當(dāng)使用線程模型時(shí),get_ident獲取線程標(biāo)識(shí) 當(dāng)使用greenlet協(xié)程時(shí), get_ident獲取到協(xié)程標(biāo)識(shí)
try: from greenlet import getcurrent as get_ident except ImportError: try: from thread import get_ident except ImportError: from _thread import get_ident class Local(object): __slots__ = ("__storage__", "__ident_func__") def __init__(self): object.__setattr__(self, "__storage__", {}) object.__setattr__(self, "__ident_func__", get_ident) def __iter__(self): return iter(self.__storage__.items()) def __call__(self, proxy): """Create a proxy for a name.""" return LocalProxy(self, proxy) def __release_local__(self): self.__storage__.pop(self.__ident_func__(), None) def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) def __setattr__(self, name, value): ident = self.__ident_func__() storage = self.__storage__ try: storage[ident][name] = value except KeyError: storage[ident] = {name: value} def __delattr__(self, name): try: del self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name)
flask.globals.py
_request_ctx_stack: 請(qǐng)求上下文棧
_app_ctx_stack: app上下文棧
current_app: 當(dāng)前請(qǐng)求,同時(shí)啟動(dòng)多個(gè)Flask()
request
session
g
# context locals _request_ctx_stack = LocalStack() _app_ctx_stack = LocalStack() current_app = LocalProxy(_find_app) request = LocalProxy(partial(_lookup_req_object, "request")) session = LocalProxy(partial(_lookup_req_object, "session")) g = LocalProxy(partial(_lookup_app_object, "g"))
py3 context是否可以替換: context
到此,相信大家對(duì)“Flask請(qǐng)求處理流程是什么”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!