python-flask复习——- flask请求上下文源码解读http聊天室单聊/群聊(基于gevent-websocket)
Posted 胖虎是只mao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python-flask复习——- flask请求上下文源码解读http聊天室单聊/群聊(基于gevent-websocket)相关的知识,希望对你有一定的参考价值。
一、flask请求上下文源码解读
通过上篇源码分析,我们知道了有请求发来的时候就执行了app
(Flask的实例化对象)的__call__
方法,而__call__方法返回了app的wsgi_app(environ, start_response)
方法的执行结果,而wsgi_app方法中有这样一句话:ctx = self.request_context(environ)
,还分析除了ctx是RequestContext类的实例化对象,而且ctx中含有有本次请求的request
对象和session
对象。
接下来我们重点分析flask是如何做到把request对象当成全局变量,而又保证了数据安全,即请求信息互不影响的。
1、flask请求上文源码解读
上篇我们分析到了如何得到RequestContext实例化对象ctx,接下来ctx对象执行push方法,如下:
RequestContext类中的push方法源码如下:
_request_ctx_stack是LocalStack类的实例化对象:
LocalStack类中的__init__方法如下:
class Local(object):
__slots__ = ('__storage__', '__ident_func__')
def __init__(self):
object.__setattr__(self, '__storage__', {})
object.__setattr__(self, '__ident_func__', get_ident)
def __iter__(self):
return iter(self.__storage__.items())
Local类的__init__方法如下:
get_ident是Local类所在文件中导入的一个方法名,该方法执行后会得到线程或协程ID,如下:
LocalStack类中的top是一个属性方法,源码如下:
下一步Local类中的__getattr__方法源码如下:
到此,分析得出top = _request_ctx_stack.top
中的top为None。
接下来分析 _request_ctx_stack.push(self)
做了什么?LocalStack类中的push方法源码如下:
def push(self, obj):
"""Pushes a new item to the stack"""
rv = getattr(self._local, 'stack', None)
if rv is None:
self._local.stack = rv = []
rv.append(obj)
return rv
因为rv.append(obj),所以最后LocalStack对象,即_request_ctx_stack对象字典化后如下:
{'_local':{'__storage__':{9527:{stack:[ctx]}}, '__ident_func__':get_ident}}
# 说明:9527假设是获取到的线程或者协程号,ctx包含request对象和session对象。
# 说明:9527假设是获取到的线程或者协程号,ctx包含request对象和session对象。
到此,flask请求上文结束,也就是完成了将一个request和session对象存储到某个地方。
2、下文
我们知道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对象中的相关信息。
二、http聊天室(单聊/群聊)- 基于gevent-websocket
1、准备知识
http协议特点:短连接,无状态保存;
轮询:前后端一秒交互多次,压力极大,并且消耗带宽,资源浪费极其严重;
长轮询:即让服务器保存我的一个连接状态,用于快速传递消息,节省带宽,释放压力,数据实时性强;
长连接:服务端及客户端节省极大的资源,能保证数据实时性;
带宽:1Mbps = 128KB/s
2、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.serve_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>
以上是关于python-flask复习——- flask请求上下文源码解读http聊天室单聊/群聊(基于gevent-websocket)的主要内容,如果未能解决你的问题,请参考以下文章
python-flask复习——Flask —SQLAlchemy
python-flask复习——- flask中的CBVwerkzeug+上下文初步解读偏函数和线程安全
python-flask复习—— Flask蓝图目录Flask-SQLAlchemyFlask-ScriptFlask-Migrate
python-flask复习——Flask-Session组件WTForms组件数据库连接池(POOL)
python-flask复习—— 装饰器的坑及解决办法flask中的路由/实例化配置/对象配置/蓝图/特殊装饰器(中间件重定义错误页面)