內容:node
1.進程線程複習python
2.flask多線程的問題nginx
3.線程隔離web
4.LocalStackapache
5.flask上下文整理編程
6.多app應用flask
1.進程線程複習服務器
(1)進程session
進程是一個具備必定獨立功能的程序關於某個數據集合的一次運行活動。它是操做系統動態執行的基本單元,在傳統的操做系統中,進程既是基本的分配單元,也是基本的執行單元多線程
簡單說進程就是操做系統調度分配資源的單位,每個應用程序至少有一個進程
(2)線程
線程能夠說是進程的一部分,能夠有一個線程也能夠有多個線程
只用進程管理CPU資源,粒度太大了,須要更小的單元來管理,就出現了線程
(3)進程與線程的區別
(4)多線程的好處
(5)其餘
對於普通python(GIL)、node.js來講通常都是單進程單線程的
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, 如今的狀況下確定沒法作到
解決辦法:全局變量改成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關係
(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)