flask高階

內容:node

1.進程線程複習python

2.flask多線程的問題nginx

3.線程隔離web

4.LocalStackapache

5.flask上下文整理編程

6.多app應用flask

 

 

 

1.進程線程複習服務器

(1)進程session

進程是一個具備必定獨立功能的程序關於某個數據集合的一次運行活動。它是操做系統動態執行的基本單元,在傳統的操做系統中,進程既是基本的分配單元,也是基本的執行單元多線程

簡單說進程就是操做系統調度分配資源的單位,每個應用程序至少有一個進程

 

(2)線程

線程能夠說是進程的一部分,能夠有一個線程也能夠有多個線程

只用進程管理CPU資源,粒度太大了,須要更小的單元來管理,就出現了線程

 

(3)進程與線程的區別

  • 進程:是併發執行的程序在執行過程當中分配和管理資源的基本單位,是一個動態概念,競爭計算機系統資源的基本單位
  • 線程:是進程的一個執行單元,是進程內科調度實體。比進程更小的獨立運行的基本單位。線程也被稱爲輕量級進程
  • 地址空間:同一進程的線程共享本進程的地址空間,而進程之間則是獨立的地址空間
  • 資源擁有:同一進程內的線程共享本進程的資源如內存、I/O、cpu等,可是進程之間的資源是獨立的
  • 一個進程崩潰後,在保護模式下不會對其餘進程產生影響,可是一個線程崩潰整個進程都死掉。因此多進程要比多線程健壯
  • 進程切換時,消耗的資源大,效率高。因此涉及到頻繁的切換時,使用線程要好於進程。一樣若是要求同時進行而且又要共享某些變量的併發操做,只能用線程不能用進程
  • 執行過程:每一個獨立的進程有一個程序運行的入口、順序執行序列和程序入口。可是線程不能獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制
  • 線程是處理器調度的基本單位,可是進程不是

 

(4)多線程的好處

  • 多線程能夠更加充分的利用CPU的性能優點
  • 實現異步編程

 

(5)其餘

對於普通python(GIL)、node.js來講通常都是單進程單線程的

  • 對於CPU密集型程序,多線程無優點
  • 對於IO密集型程序,多線程有優點

 

 

2.flask多線程的問題

(1)flask與webserver

flask自帶的run方法實際上是本身的一個服務器,只是供咱們測試調試用的,真正上線後咱們要用webserver去運行flask而不是用flask自帶的運行方法

另外flask自帶的是單進程單線程的,不能同時處理多個請求,用webserver能夠有效提升性能

常見的webserver:nginx、apache、IIS等

 

(2)flask多線程的問題

flask中能夠開啓多線程,開啓方法:run方法中有一個threaded參數,設置爲True開啓多線程

開啓以後可能會致使這個問題:

flask中經過線程隔離來解決這個問題

 

 

3.線程隔離

(1)flask線程隔離基本原理

現有全局變量request=None

  • 在線程1中 request=Request()
  • 在線程2中 request=Request()
  • 在線程3中 request=Request()

想取線程1的request, 如今的狀況下確定沒法作到

解決辦法:全局變量改成dict, 每個線程都有對應的本身的key, 並將request做爲value存放。falsk的線程隔離技術也是基於相似的原理實現的。

(2)源碼

siet-package/werkzeug/local.py 中的Local類:

 1 class Local(object):
 2     __slots__ = ('__storage__', '__ident_func__')
 3  
 4     def __init__(self):
 5         object.__setattr__(self, '__storage__', {})   # __storge__ 實現空的字典
 6         object.__setattr__(self, '__ident_func__', get_ident) # get_ident是獲取當前線程id號的 函數
 7  
 8     def __iter__(self):
 9         return iter(self.__storage__.items())
10  
11     def __call__(self, proxy):
12         """Create a proxy for a name."""
13         return LocalProxy(self, proxy)
14  
15     def __release_local__(self):
16         self.__storage__.pop(self.__ident_func__(), None)
17  
18     def __getattr__(self, name):
19         try:
20             return self.__storage__[self.__ident_func__()][name]
21         except KeyError:
22             raise AttributeError(name)
23  
24     def __setattr__(self, name, value):
25         ident = self.__ident_func__()     # 取當前線程的 線程id號, 
26         storage = self.__storage__        # 類自己的字典
27         try:
28             storage[ident][name] = value   # 把當前線程id號存起來
29         except KeyError:
30             storage[ident] = {name: value}
31  
32     def __delattr__(self, name):
33         try:
34             del self.__storage__[self.__ident_func__()][name]
35         except KeyError:
36             raise AttributeError(name)

Local類用當前線程的id號看成key存儲不一樣的線程,不一樣線程操做該對象時互不影響,Local類就是線程隔離的對象

 

Local隔離實例:

 1 from werkzeug.local import Local
 2 import threading, time
 3  
 4 my_obj = Local()
 5 my_obj.b = 1
 6  
 7  
 8 def worker():
 9     my_obj.b = 2
10     print('in sub thread b is:' + str(my_obj.b))   # 2
11  
12  
13 subthread1 = threading.Thread(target=worker, name='subthread1')
14 subthread1.start()
15  
16 time.sleep(1)
17  
18 print('my_obj.b is : {}'.format(my_obj.b))  # 1
19  

 

(3)線程隔離的意義

使用線程隔離使當前線程能正確引用到本身建立的對象,而不是引用到其餘線程所建立的對象

 

 

4.LocalStack

(1)源碼

 1 class LocalStack(object):
 2     def __init__(self):
 3         self._local = Local()
 4 
 5     def __release_local__(self):
 6         self._local.__release_local__()
 7 
 8     def _get__ident_func__(self):
 9         return self._local.__ident_func__
10 
11     def _set__ident_func__(self, value):
12         object.__setattr__(self._local, '__ident_func__', value)
13     __ident_func__ = property(_get__ident_func__, _set__ident_func__)
14     del _get__ident_func__, _set__ident_func__
15 
16     def __call__(self):
17         def _lookup():
18             rv = self.top
19             if rv is None:
20                 raise RuntimeError('object unbound')
21             return rv
22         return LocalProxy(_lookup)
23 
24     def push(self, obj):
25         """Pushes a new item to the stack"""
26         rv = getattr(self._local, 'stack', None)
27         if rv is None:
28             self._local.stack = rv = []
29         rv.append(obj)
30         return rv
31 
32     def pop(self):
33         """Removes the topmost item from the stack, will return the
34         old value or `None` if the stack was already empty.
35         """
36         stack = getattr(self._local, 'stack', None)
37         if stack is None:
38             return None
39         elif len(stack) == 1:
40             release_local(self._local)
41             return stack[-1]
42         else:
43             return stack.pop()
44 
45     @property
46     def top(self):
47         """The topmost item on the stack.  If the stack is empty,
48         `None` is returned.
49         """
50         try:
51             return self._local.stack[-1]
52         except (AttributeError, IndexError):
53             return None

 

(2)LocalStack和Local關係

  • Local使用字典實現線程隔離
  • LocalStack封裝了Local,實現了線程隔離的棧結構

 

(3)使用LocalStack

 1 from werkzeug.local import LocalStack       # 棧 先進後出
 2 # LocalStack經常使用方法: push pop top
 3 
 4 
 5 s = LocalStack()
 6 s.push(1)
 7 print(s.top)        # top只會取出元素不會刪除
 8 print(s.top)
 9 print(s.pop())      # pop取出元素並刪除
10 print(s.top)
11 
12 s.push(1)
13 s.push(2)
14 print(s.top)
15 print(s.top)
16 print(s.pop())
17 print(s.top)
 1 from werkzeug.local import LocalStack       # 棧 先進後出
 2 from threading import Thread
 3 import time
 4 
 5 my_stack = LocalStack()
 6 my_stack.push(1)
 7 print("in main thread after push, value is: " + str(my_stack.top))      # 1
 8 
 9 
10 def worker():
11     # 新線程
12     print("in new thread before push, value is: " + str(my_stack.top))      # None
13     my_stack.push(2)
14     print("in new thread after push, value is: " + str(my_stack.top))       # 2
15 
16 
17 new_t = Thread(target=worker, name="new thread")
18 new_t.start()
19 time.sleep(1)
20 
21 # 主線程
22 print("finally, in main thread value is: " + str(my_stack.top))     # 1

 

 

5.flask上下文整理

(1)請求上下文

 1 from flask import Flask,request,session,g,current_app
 2 
 3 app = Flask(__name__)
 4 
 5 @app.route('/',methods=['GET',"POST"])
 6 def index():
 7     # request是 LocalProxy 的對象
 8     print(request)  # LocalProxy.__str__ --> str(LocalProxy._get_current_object) --> 調用偏函數 --> ctx.request
 9     request.method  # LocalProxy.__getattr__ -->
10                                             # str(LocalProxy._get_current_object) --> 調用偏函數 --> ctx.request
11                                             # getattr(self._get_current_object(), name)          --> ctx.request.method
12 
13     request.path   # ctx.request.path
14 
15     print(session) # LocalProxy.__str__ --> str(LocalProxy._get_current_object) --> 調用偏函數 --> ctx.session
16 
17 
18     print(g) # 執行g對象的__str__
19     return "index"
20 
21 
22 if __name__ == '__main__':
23     # 1. app.__call__
24     # 2. app.wsgi_app
25     app.wsgi_app
26     app.request_class
27     app.run()

 

(2)應用上下文

 1 from flask import Flask,request,session
 2 
 3 app = Flask(__name__)
 4 
 5 @app.route('/',methods=['GET',"POST"])
 6 def index():
 7     # request是 LocalProxy 的對象
 8     print(request)  # LocalProxy.__str__ --> str(LocalProxy._get_current_object) --> 調用偏函數 --> ctx.request
 9     request.method  # LocalProxy.__getattr__ -->
10                                             # str(LocalProxy._get_current_object) --> 調用偏函數 --> ctx.request
11                                             # getattr(self._get_current_object(), name)          --> ctx.request.method
12 
13     request.path   # ctx.request.path
14 
15     print(session) # LocalProxy.__str__ --> str(LocalProxy._get_current_object) --> 調用偏函數 --> ctx.session
16     return "index"
17 
18 
19 if __name__ == '__main__':
20     # 1. app.__call__
21     # 2. app.wsgi_app
22     app.wsgi_app
23     app.run()
 1 from flask import Flask,request,g
 2 
 3 app = Flask(__name__)
 4 
 5 @app.before_request
 6 def before():
 7     g.permission_code_list = ['list','add']
 8 
 9 
10 @app.route('/',methods=['GET',"POST"])
11 def index():
12     print(g.permission_code_list)
13     return "index"
14 
15 
16 if __name__ == '__main__':
17     app.run()

 

(3)請求上下文及應用上下文

 1 a. 請求上下文:
 2     - request
 3     - session 
 4 
 5 b. 應用上下文:
 6 請求流程:
 7         _request_ctx_stack.local = {
 8             
 9         }
10         
11         _app_ctx_stack.local = {
12             
13         }
14 
15 
16     1. 請求到來 ,有人來訪問
17         # 將請求相關的數據environ封裝到了RequestContext對象中
18         # 再講對象封裝到local中(每一個線程/每一個協程獨立空間存儲)
19         # ctx.app # 當前APP的名稱
20         # ctx.request # Request對象(封裝請求相關東西)
21         # ctx.session # 空
22         _request_ctx_stack.local = {
23             惟一標識:{
24                 "stack":[ctx, ]
25             },
26             惟一標識:{
27                 "stack":[ctx, ]
28             },
29         }
30         
31         
32         # app_ctx = AppContext對象
33         # app_ctx.app
34         # app_ctx.g
35             
36         _app_ctx_stack.local = {
37             惟一標識:{
38                 "stack":[app_ctx, ]
39             },
40             惟一標識:{
41                 "stack":[app_ctx, ]
42             },
43         }
44     
45     2. 使用 
46             from flask import request,session,g,current_app
47             
48             print(request,session,g,current_app)
49             
50             都會執行相應LocalProxy對象的 __str__
51             
52             current_app = LocalProxy(_find_app)
53                 request = LocalProxy(partial(_lookup_req_object, 'request'))
54                 session = LocalProxy(partial(_lookup_req_object, 'session'))
55                 
56                 current_app = LocalProxy(_find_app)
57                 g = LocalProxy(partial(_lookup_app_object, 'g'))
58             
59     3. 終止,所有pop
60 
61     問題1:多線程是如何體現?
62     問題2:flask的local中保存數據時,使用列表建立出來的棧。爲何用棧?
63            - 若是寫web程序,web運行環境;棧中永遠保存1條數據(能夠不用棧)。
64            - 寫腳本獲取app信息時,可能存在app上下文嵌套關係。
65                 from flask import Flask,current_app,globals,_app_ctx_stack
66 
67                 app1 = Flask('app01')
68                 app1.debug = False # 用戶/密碼/郵箱
69                 # app_ctx = AppContext(self):
70                 # app_ctx.app
71                 # app_ctx.g
72 
73                 app2 = Flask('app02')
74                 app2.debug = True # 用戶/密碼/郵箱
75                 # app_ctx = AppContext(self):
76                 # app_ctx.app
77                 # app_ctx.g
78 
79 
80 
81                 with app1.app_context():# __enter__方法 -> push -> app_ctx添加到_app_ctx_stack.local
82                     # {<greenlet.greenlet object at 0x00000000036E2340>: {'stack': [<flask.ctx.AppContext object at 0x00000000037CA438>]}}
83                     print(_app_ctx_stack._local.__storage__)
84                     print(current_app.config['DEBUG'])
85 
86                     with app2.app_context():
87                         # {<greenlet.greenlet object at 0x00000000036E2340>: {'stack': [<flask.ctx.AppContext object at 0x00000000037CA438> ]}}
88                         print(_app_ctx_stack._local.__storage__)
89                         print(current_app.config['DEBUG'])
90 
91                     print(current_app.config['DEBUG'])

 

 

6.多app應用

 1 from werkzeug.wsgi import DispatcherMiddleware
 2 from werkzeug.serving import run_simple
 3 from flask import Flask, current_app
 4 
 5 app1 = Flask('app01')
 6 app2 = Flask('app02')
 7 
 8 
 9 @app1.route('/index')
10 def index():
11     return "app01"
12 
13 
14 @app2.route('/index2')
15 def index2():
16     return "app2"
17 
18 # http://www.oldboyedu.com/index  --> app1
19 # http://www.oldboyedu.com/admin/index2  --> app2
20 app= DispatcherMiddleware(app1, {
21     '/admin': app2,
22 })
23 
24 if __name__ == "__main__":
25     run_simple('localhost', 5000, app)
相關文章
相關標籤/搜索