flashkがマルチスレッドを開く具体的な方法


前にflashkがどのようにマルチスレッドをサポートするかを説明しましたが、Local StockとLocalは、Localの中に二つの属性があります。storage.同前ident_.func_後者はスレッドIDを取得するために用いられ、異なるスレッドからの要求を区別する。
今回はflashkがどうやってマルチスレッドを開くかという話です。
まずap.run()この方法から見ます。

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) #       
 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
判断と設定を経てrun_に入る。simple()この関数はソースコードを見てください。
def run_シンプル(hostname,port,appration,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() #     
判断と設定を経てrun_に入る。simple()この関数はソースコードを見てください。
def run_シンプル(hostname,port,appration,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() #     
それとも一連の判定を経てデフォルトはinner()関数に入ります。この関数はrun_で定義されています。simple()内は、クローズドに属し、inner()でmake_を実行します。server()この関数はソースコードを見てください:
def make_server(host=None,port=None,ap=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)
これを見てもよく分かりました。マルチスレッドまたはマルチプロセスを構成するには、threadedまたはprocessesの2つのパラメータを設定する必要があります。この2つのパラメータは、ap.run()から渡されるものです。
app.run(**options)->run_シンプル-->make_server(threaded,processes)
デフォルトではflashkはシングルスレッドで、シングルプロセスの場合、オープンするにはrunに対応するパラメータが必要です。ap.run(threaded=True)で結構です。
メークからserverでは、flashkは三つのserverを提供しています。ThreadeWSGIServer、Forking WSGIServer、BaseWSGIServer、デフォルトではBaseWSGIServerです。
スレッドを例にとって、ThreadeWSGIServerというクラスを見てみます。
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とは、各要求に対して新しいスレッドを生成して処理することです。
最後に、非常に簡単なアプリケーションを書いて、上記の説明を検証します。
from flashk import Flashk
from flashk import_request_ctx_ロック
ap=Flashk(__u u u_uname_
@ap.route('/')
def index():

print(_request_ctx_stack._local.__ident_func__())
while True:
 pass
return '<h1>hello</h1>'
app.run()〓〓〓マルチスレッドを開く必要があればap.run(threaded=True)
_。request_ctx_stack._local.__ident_.func_()これに対応してget_ident()という関数は、現在のスレッドIDに戻りますが、なぜ後ろにwhile Trueという言葉を付けますか?この関数の説明:
Return a non-zerinteger that uniquely identifries the current thread amongst other threads that ext simulltaneousy.This may be used to ideantify per-thread reource.Even though on some plemtforms ateders apticationand the number shound be seen purey a s a magic cookie.A thread's identity may be reused for another thread after it exit.
キーワードはもう太くなりました。スレッドIDはスレッド終了後に再利用されますので、ルート関数にこのデッドサイクルを追加して、要求をブロックして、異なるidが観測されやすくなります。
1.マルチスレッドがオープンしていない場合は、一度に要請してサーバが直接ブロックされ、その後の他の要求もブロックされます。
2.マルチスレッドを開くと毎回異なるスレッドIDがプリントされます。
結果:
第一の場合
Running onhttp://127.0.0.1:5000/ (PresCTRL+C to quit)
139623180527360
第二の場合
Running onhttp://127.0.0.1:5000/ (PresCTRL+C to quit)
140315469436672
140315477829376
140315486222080
140315316901632
1403151051163008
140315096770304
140315088377600
結果が歴然としている
上記の通り、flashkはマルチスレッドをサポートしていますが、デフォルトでは開いていません。次に、ap.run()は開発環境にのみ適用され、生産環境下ではuWSGI、Gunicornなどのウェブサーバが使えます。
コンテンツ拡張:
flashkはマルチスレッドを開いていますか?それともマルチプロセスですか?
Flaaskはデフォルトではシングルプロセス、シングルスレッドブロックのタスクモードであり、プロジェクトがオンラインになった時にはnginx+gnicornでflashkタスクを展開することができます。
しかし、開発の過程で遅延方式で高合併をテストしたいなら、どうやって実現しますか?
app.run()では、スレッドサポートとプロセスサポートを開くために、2つのパラメータが受け入れられます。
1.threaded:マルチスレッドサポート、デフォルトはFalse、つまりマルチスレッドを開けません。
2.processes:プロセス数、デフォルトは1.
オープンモード:

if __name__ == '__main__':
   app.run(threaded=True)
 
   # app.run(processes=4)
注意:マルチプロセスまたはマルチスレッドは一つしか選択できません。同時に開くことはできません。
以上はflashkがマルチスレッドを開く具体的な方法の詳細です。flashkがどうやってマルチスレッドを開くかについて詳しく説明している資料は他の関連記事に注目してください。