1、flask請求上下文源碼解讀
經過上篇源碼分析( ---Flask中的CBV和上下文管理--- ),咱們知道了有請求發來的時候就執行了app(Flask的實例化對象)的__call__方法,而__call__方法返回了app的wsgi_app(environ, start_response)方法的執行結果,而wsgi_app方法中有這樣一句話:ctx = self.request_context(environ),還分析除了ctx是RequestContext類的實例化對象,並且ctx中含有有本次請求的request對象和session對象。javascript
接下來咱們重點分析flask是如何作到把request對象當成全局變量,而又保證了數據安全,即請求信息互不影響的。html
一、flask請求上文源碼解讀 java
上篇咱們分析到了如何獲得RequestContext實例化對象ctx,接下來ctx對象執行push方法,以下:web
RequestContext類中的push方法源碼以下:json
_request_ctx_stack是LocalStack類的實例化對象:flask
LocalStack類中的__init__方法以下:後端
Local類的__init__方法以下:安全
get_ident是Local類所在文件中導入的一個方法名,該方法執行後會獲得線程或協程ID,以下:服務器
LocalStack類中的top是一個屬性方法,源碼以下:websocket
下一步Local類中的__getattr__方法源碼以下:
到此,分析得出top = _request_ctx_stack.top中的top爲None。
接下來分析 _request_ctx_stack.push(self)作了什麼?LocalStack類中的push方法源碼以下:
Local類中的__setattr__方法源碼以下:
由於rv.append(obj),因此最後LocalStack對象,即_request_ctx_stack對象字典化後以下:
{'_local':{'__storage__':{9527:{stack:[ctx]}}, '__ident_func__':get_ident}} # 說明:9527假設是獲取到的線程或者協程號,ctx包含request對象和session對象。
到此,flask請求上文結束,也就是完成了將一個request和session對象存儲到某個地方。
二、下文
咱們知道flask的request對象和session對象是全局變量,上文已經解讀了如何存儲。接下來解讀如何在保證數據安全的狀況下取出來,即只取到本身的請求信息而非其餘人的。
咱們還知道request對象中存儲了不少信息,如request.method存儲請求方式、request.json存儲json標準字符串等等。下面以request.method爲例,分析如何獲得請求方式信息。
導入request方式以下:
from flask import request
源碼以下:
LocalProxy類的__init__方法以下:
偏函數中的原函數_lookup_req_object源碼以下:
當執行request.method的時候,執行LocalProxy的__getattr__方法,源碼以下:
查看類LocalProxy中的_get_current_object方法是如何獲得本次請求的request對象,源碼以下:
至此,咱們已經分析出瞭如何獲得本次請求的request對象,從而取出request對象中的相關信息。
2、http聊天室(單聊/羣聊)
一、準備知識
http協議特色:短鏈接,無狀態保存;
輪詢:先後端一秒交互屢次,壓力極大,而且消耗帶寬,資源浪費極其嚴重;
長輪詢:即讓服務器保存個人一個鏈接狀態,用於快速傳遞消息,節省帶寬,釋放壓力,數據實時性強;
長鏈接:服務端及客戶端節省極大的資源,能保證數據實時性;
帶寬:1Mbps = 128KB/s
二、http聊天室
準備工做:下載gevent-websocket模塊
pip3 install gevent-websocket
代碼示例:
manage.py代碼:
from flask import Flask, request, render_template from geventwebsocket.handler import WebSocketHandler from geventwebsocket.websocket import WebSocket # 提示用 from gevent.pywsgi import WSGIServer import json app = Flask(__name__) user_socket_dict = {} # 用戶字典 @app.route('/ws/<username>') def ws(username): print(request.environ) # 有個wsgi.websocket,經過它能夠發消息 user_socket = request.environ.get('wsgi.websocket') #type:WebSocket if user_socket: user_socket_dict[username] = user_socket print(user_socket_dict) while 1: msg = user_socket.receive() msg_dict = json.loads(msg) msg_dict['from_user'] = username to_user = msg_dict.get('to_user') # chat = msg_dict.get('msg') u_socket = user_socket_dict.get(to_user) #type:WebSocket u_socket.send(json.dumps(msg_dict)) @app.route('/') def index(): return render_template('ws.html') if __name__ == '__main__': http_serv = WSGIServer(('0.0.0.0',9527), app, handler_class=WebSocketHandler) http_serv.server_forever()
ws.html代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <input id="username"type="text"><button onclick="login()">登陸聊天室</button> 給<input id="to_user"type="text"> <input id="msg"type="text"><button onclick="send_msg()">發送</button> <div id="chat_list"style="width:500px; height:500px; border:1px solid red;"></div> </body> <script type="text/javascript"> var ws = null; // 因其餘函數也可能會用到ws,因此不能放在某一個函數中 function login() { var username = document.getElementById('username').value; var ws = new WebSocket('ws://192.168.13.172:9527/ws'+username); // ws請求協議 ws.onmessage = function (data) { console.log(data.data); var recv_msg = JSON.parse(data.data); var ptag = document.createElement('p'); ptag.innerText = recv_msg.from_user + ':' + recv_msg.msg; document.getElementById('caht_list').appendChild(ptag) }; } function send_msg() { var to_user = document.getElementById('to_user').value; var msg = document.getElementById('msg').value; var send_dict = { 'to_user':to_user, 'msg':msg }; ws.send(JSON.stringify(send_dict)); } </script> </html>