成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

flask如何開(kāi)啟多線(xiàn)程詳解

Yangder / 1325人閱讀

摘要:在我之前寫(xiě)的中源碼的深究和理解一文中解釋了如何支持多線(xiàn)程主要通過(guò)兩個(gè)類(lèi)來(lái)實(shí)現(xiàn)和在中有兩個(gè)屬性和后者用來(lái)獲取線(xiàn)程從而區(qū)分不同線(xiàn)程發(fā)來(lái)的請(qǐng)求這次要說(shuō)的是如何開(kāi)啟多線(xiàn)程先從這個(gè)方法看起會(huì)進(jìn)入這個(gè)函數(shù)經(jīng)過(guò)判斷和設(shè)置后進(jìn)入這個(gè)函數(shù)看下源碼

在我之前寫(xiě)的《flask中current_app、g、request、session源碼的深究和理解》一文中解釋了flask如何支持多線(xiàn)程
主要通過(guò)兩個(gè)類(lèi)來(lái)實(shí)現(xiàn),LocalStack和Local,在Local中有兩個(gè)屬性,__storage__和__ident_func__,后者用來(lái)獲取線(xiàn)程id,從而區(qū)分不同線(xiàn)程發(fā)來(lái)的請(qǐng)求

這次要說(shuō)的是flask如何開(kāi)啟多線(xiàn)程

先從app.run()這個(gè)方法看起

def run(self, host=None, port=None, debug=None, **options):
    from werkzeug.serving import run_simple
    if host is None:
        host = "127.0.0.1"
    if port is None:
        server_name = self.config["SERVER_NAME"]
        if server_name and ":" in server_name:
            port = int(server_name.rsplit(":", 1)[1])
        else:
            port = 5000
    if debug is not None:
        self.debug = bool(debug)
    options.setdefault("use_reloader", self.debug)
    options.setdefault("use_debugger", self.debug)
    try:
        run_simple(host, port, self, **options)  #會(huì)進(jìn)入這個(gè)函數(shù)
    finally:
        # reset the first request information if the development server
        # reset normally.  This makes it possible to restart the server
        # without reloader and that stuff from an interactive shell.
        self._got_first_request = False

經(jīng)過(guò)判斷和設(shè)置后進(jìn)入run_simple()這個(gè)函數(shù),看下源碼

def run_simple(hostname, port, application, use_reloader=False,

           use_debugger=False, use_evalex=True,
           extra_files=None, reloader_interval=1,
           reloader_type="auto", threaded=False,
           processes=1, request_handler=None, static_files=None,
           passthrough_errors=False, ssl_context=None):
"""Start a WSGI application. Optional features include a reloader,
multithreading and fork support.

This function has a command-line interface too::

    python -m werkzeug.serving --help

.. versionadded:: 0.5
   `static_files` was added to simplify serving of static files as well
   as `passthrough_errors`.

.. versionadded:: 0.6
   support for SSL was added.

.. versionadded:: 0.8
   Added support for automatically loading a SSL context from certificate
   file and private key.

.. versionadded:: 0.9
   Added command-line interface.

.. versionadded:: 0.10
   Improved the reloader and added support for changing the backend
   through the `reloader_type` parameter.  See :ref:`reloader`
   for more information.

:param hostname: The host for the application.  eg: ``"localhost"``
:param port: The port for the server.  eg: ``8080``
:param application: the WSGI application to execute
:param use_reloader: should the server automatically restart the python
                     process if modules were changed?
:param use_debugger: should the werkzeug debugging system be used?
:param use_evalex: should the exception evaluation feature be enabled?
:param extra_files: a list of files the reloader should watch
                    additionally to the modules.  For example configuration
                    files.
:param reloader_interval: the interval for the reloader in seconds.
:param reloader_type: the type of reloader to use.  The default is
                      auto detection.  Valid values are ``"stat"`` and
                      ``"watchdog"``. See :ref:`reloader` for more
                      information.
:param threaded: should the process handle each request in a separate
                 thread?
:param processes: if greater than 1 then handle each request in a new process
                  up to this maximum number of concurrent processes.
:param request_handler: optional parameter that can be used to replace
                        the default one.  You can use this to replace it
                        with a different
                        :class:`~BaseHTTPServer.BaseHTTPRequestHandler`
                        subclass.
:param static_files: a list or dict of paths for static files.  This works
                     exactly like :class:`SharedDataMiddleware`, it"s actually
                     just wrapping the application in that middleware before
                     serving.
:param passthrough_errors: set this to `True` to disable the error catching.
                           This means that the server will die on errors but
                           it can be useful to hook debuggers in (pdb etc.)
:param ssl_context: an SSL context for the connection. Either an
                    :class:`ssl.SSLContext`, a tuple in the form
                    ``(cert_file, pkey_file)``, the string ``"adhoc"`` if
                    the server should automatically create one, or ``None``
                    to disable SSL (which is the default).
"""
if not isinstance(port, int):
    raise TypeError("port must be an integer")
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 log_startup(sock):
    display_hostname = hostname not in ("", "*") and hostname or "localhost"
    if ":" in display_hostname:
        display_hostname = "[%s]" % display_hostname
    quit_msg = "(Press CTRL+C to quit)"
    port = sock.getsockname()[1]
    _log("info", " * Running on %s://%s:%d/ %s",
         ssl_context is None and "http" or "https",
         display_hostname, port, quit_msg)

def inner():
    try:
        fd = int(os.environ["WERKZEUG_SERVER_FD"])
    except (LookupError, ValueError):
        fd = None
    srv = make_server(hostname, port, application, threaded,
                      processes, request_handler,
                      passthrough_errors, ssl_context,
                      fd=fd)
    if fd is None:
        log_startup(srv.socket)
    srv.serve_forever()

if use_reloader:
    # If we"re not running already in the subprocess that is the
    # reloader we want to open up a socket early to make sure the
    # port is actually available.
    if os.environ.get("WERKZEUG_RUN_MAIN") != "true":
        if port == 0 and not can_open_by_fd:
            raise ValueError("Cannot bind to a random port with enabled "
                             "reloader if the Python interpreter does "
                             "not support socket opening by fd.")

        # Create and destroy a socket so that any exceptions are
        # raised before we spawn a separate Python interpreter and
        # lose this ability.
        address_family = select_ip_version(hostname, port)
        s = socket.socket(address_family, socket.SOCK_STREAM)
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind(get_sockaddr(hostname, port, address_family))
        if hasattr(s, "set_inheritable"):
            s.set_inheritable(True)

        # If we can open the socket by file descriptor, then we can just
        # reuse this one and our socket will survive the restarts.
        if can_open_by_fd:
            os.environ["WERKZEUG_SERVER_FD"] = str(s.fileno())
            s.listen(LISTEN_QUEUE)
            log_startup(s)
        else:
            s.close()

    # Do not use relative imports, otherwise "python -m werkzeug.serving"
    # breaks.
    from werkzeug._reloader import run_with_reloader
    run_with_reloader(inner, extra_files, reloader_interval,
                      reloader_type)
else:
    inner()  #默認(rèn)會(huì)執(zhí)行

還是經(jīng)過(guò)一系列判斷后默認(rèn)會(huì)進(jìn)入inner()函數(shù),這個(gè)函數(shù)定義在run_simple()內(nèi),屬于閉包,inner()中會(huì)執(zhí)行make_server()這個(gè)函數(shù),看下源碼:

def make_server(host=None, port=None, app=None, threaded=False, processes=1,

            request_handler=None, passthrough_errors=False,
            ssl_context=None, fd=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, fd=fd)
elif processes > 1:
    return ForkingWSGIServer(host, port, app, processes, request_handler,
                             passthrough_errors, ssl_context, fd=fd)
else:
    return BaseWSGIServer(host, port, app, request_handler,
                          passthrough_errors, ssl_context, fd=fd)

看到這也很明白了,想要配置多線(xiàn)程或者多進(jìn)程,則需要設(shè)置threaded或processes這兩個(gè)參數(shù),而這兩個(gè)參數(shù)是從app.run()中傳遞過(guò)來(lái)的:
app.run(**options) ---> run_simple(threaded,processes) ---> make_server(threaded,processes)
默認(rèn)情況下flask是單線(xiàn)程,單進(jìn)程的,想要開(kāi)啟只需要在run中傳入對(duì)應(yīng)的參數(shù):app.run(threaded=True)即可.
從make_server中可知,flask提供了三種server:ThreadedWSGIServer,ForkingWSGIServer,BaseWSGIServer,默認(rèn)情況下是BaseWSGIServer
以線(xiàn)程為例,看下ThreadedWSGIServer這個(gè)類(lèi):

class ThreadedWSGIServer(ThreadingMixIn, BaseWSGIServer): #繼承自ThreadingMixIn, BaseWSGIServer

"""A WSGI server that does threading."""
multithread = True
daemon_threads = True

ThreadingMixIn = socketserver.ThreadingMixIn

class ThreadingMixIn:

"""Mix-in class to handle each request in a new thread."""

# Decides how threads will act upon termination of the
# main process
daemon_threads = False

def process_request_thread(self, request, client_address):
    """Same as in BaseServer but as a thread.

    In addition, exception handling is done here.

    """
    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):
    """Start a new thread to process the request."""
    t = threading.Thread(target = self.process_request_thread,
                         args = (request, client_address))
    t.daemon = self.daemon_threads
    t.start()

process_request就是對(duì)每個(gè)請(qǐng)求產(chǎn)生一個(gè)新的線(xiàn)程來(lái)處理
最后寫(xiě)一個(gè)非常簡(jiǎn)單的應(yīng)用來(lái)驗(yàn)證以上說(shuō)法:

from flask import Flask
from flask import _request_ctx_stack

app = Flask(__name__)

@app.route("/")
def index():

print(_request_ctx_stack._local.__ident_func__())
while True:
    pass
return "

hello

"

app.run() #如果需要開(kāi)啟多線(xiàn)程則app.run(threaded=True)

_request_ctx_stack._local.__ident_func__()對(duì)應(yīng)這get_ident()這個(gè)函數(shù),返回當(dāng)前線(xiàn)程id,為什么要在后面加上while True這句呢,我們看下get_ident()這個(gè)函數(shù)的說(shuō)明:
Return a non-zero integer that uniquely identifies the current thread amongst other threads that exist simultaneously. This may be used to identify per-thread resources. Even though on some platforms threads identities may appear to be allocated consecutive numbers starting at 1, this behavior should not be relied upon, and the number should be seen purely as a magic cookie. A thread"s identity may be reused for another thread after it exits.

關(guān)鍵字我已經(jīng)加粗了,線(xiàn)程id會(huì)在線(xiàn)程結(jié)束后重復(fù)利用,所以我在路由函數(shù)中加了這個(gè)死循環(huán)來(lái)阻塞請(qǐng)求以便于觀察到不同的id,這就會(huì)產(chǎn)生兩種情況:
1.沒(méi)開(kāi)啟多線(xiàn)程的情況下,一次請(qǐng)求過(guò)來(lái),服務(wù)器直接阻塞,并且之后的其他請(qǐng)求也都阻塞
2.開(kāi)啟多線(xiàn)程情況下,每次都會(huì)打印出不同的線(xiàn)程id

結(jié)果:

第一種情況

Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

139623180527360


第二種情況

Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

140315469436672
140315477829376
140315486222080
140315316901632
140315105163008
140315096770304
140315088377600

結(jié)果顯而易見(jiàn)
綜上所述:flask支持多線(xiàn)程,但默認(rèn)沒(méi)開(kāi)啟,其次app.run()只適用于開(kāi)發(fā)環(huán)境,生產(chǎn)環(huán)境下可以使用uWSGI,Gunicorn等web服務(wù)器

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/44732.html

相關(guān)文章

  • flask文檔學(xué)習(xí)筆記1-快速入門(mén)

    摘要:示例如下靜態(tài)路由使用動(dòng)態(tài)變量的路由未指定變量類(lèi)型使用動(dòng)態(tài)變量的路由指定變量類(lèi)型指定的路由變量,可以作為被裝飾的函數(shù)參數(shù)傳入進(jìn)來(lái)。 開(kāi)始決定認(rèn)真的在網(wǎng)上寫(xiě)一些東西,主要原因還是在于希望能提升學(xué)習(xí)效果。雖說(shuō)python寫(xiě)了有幾年,但是web后端框架的確沒(méi)怎么接觸過(guò),買(mǎi)了本狗書(shū)寥寥草草的過(guò)了一遍,發(fā)現(xiàn)很多東西還是理解不深,真的是好記性不如爛筆頭,知識(shí)也要從基礎(chǔ)開(kāi)始,退回來(lái)好好看看官方文檔,再...

    lingdududu 評(píng)論0 收藏0
  • flask接收請(qǐng)求并推入棧

    摘要:前面兩篇講明了怎么支持多線(xiàn)程以及怎么開(kāi)啟多線(xiàn)程的這篇來(lái)講講當(dāng)后端接收到請(qǐng)求后是怎么一步步封裝的類(lèi)中的當(dāng)應(yīng)用啟動(dòng)后會(huì)通過(guò)接收請(qǐng)求中返回的是方法主要做了兩件事情第一件事是通過(guò)的另一個(gè)方法返回得到了一個(gè)封裝好的對(duì)象然后調(diào)用中的在最后調(diào)用了將請(qǐng)求對(duì) 前面兩篇講明了flask怎么支持多線(xiàn)程以及怎么開(kāi)啟多線(xiàn)程的,這篇來(lái)講講當(dāng)后端接收到請(qǐng)求后是怎么一步步封裝的 Flask類(lèi)中的wsgi_app()當(dāng)...

    崔曉明 評(píng)論0 收藏0
  • Flask Web Development —— Email

    摘要:函數(shù)攜帶目的地址主題郵件體模板和一組關(guān)鍵字參數(shù)。許多擴(kuò)展操作是在假設(shè)有活動(dòng)的應(yīng)用程序和請(qǐng)求上下文的情況下進(jìn)行的。但是當(dāng)函數(shù)在一個(gè)不同的線(xiàn)程上執(zhí)行,應(yīng)用程序上下文需要人為地創(chuàng)建使用。例如,執(zhí)行函數(shù)可以將郵件發(fā)送到的任務(wù)隊(duì)列中。 許多類(lèi)型的應(yīng)用程序都會(huì)在某些事件發(fā)生的時(shí)候通知用戶(hù),常用的溝通方法就是電子郵件。盡管在Flask應(yīng)用程序中,可以使用Python標(biāo)準(zhǔn)庫(kù)中的smtplib包來(lái)發(fā)送電...

    SKYZACK 評(píng)論0 收藏0
  • 精選50道Python面試題,快來(lái)看看你已經(jīng)掌握了少道吧

    摘要:從存儲(chǔ)的字符串表示中檢索原始對(duì)象的過(guò)程稱(chēng)為。這稱(chēng)為命名空間。如果需要八進(jìn)制或十六進(jìn)制表示,請(qǐng)使用內(nèi)置函數(shù)或。和有什么區(qū)別返回對(duì)象,而返回列表,并使用相同的內(nèi)存,無(wú)論范圍大小是多少。它提供了靈活性,并允許開(kāi)發(fā)人員為他們的項(xiàng)目使用正確的工具。 ...

    zzir 評(píng)論0 收藏0
  • Flask Web Development —— 基本應(yīng)用程序結(jié)構(gòu)(下)

    摘要:有兩類(lèi)應(yīng)用級(jí)和請(qǐng)求級(jí)。一個(gè)響應(yīng)中非常重要的部分是狀態(tài)碼,默認(rèn)設(shè)置來(lái)指示請(qǐng)求已經(jīng)成功處理。重定向通常由響應(yīng)狀態(tài)碼注明并且重定向的由頭部的給出。因?yàn)檫@些變化,應(yīng)用程序獲得一組基本的命令行選項(xiàng)。運(yùn)行顯示可用信息在應(yīng)用程序上下文的內(nèi)部運(yùn)行一個(gè)。 5、請(qǐng)求-響應(yīng)循環(huán) 現(xiàn)在你已經(jīng)玩過(guò)一個(gè)基本的Flask應(yīng)用程序,你也許想要知道更多關(guān)于Flask如何施展魔力。下面章節(jié)描述了一些框架設(shè)計(jì)方面的特點(diǎn)。...

    caohaoyu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<