• 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__方法如下:

      Local类的__init__方法如下:

      get_ident是Local类所在文件中导入的一个方法名,该方法执行后会得到线程或协程ID,如下:

      LocalStack类中的top是一个属性方法,源码如下:

      下一步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对象存储到某个地方。

    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模块

    1
    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="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>
    复制代码
  • 相关阅读:
    Hadoop 无法实现HA
    Hive 常见设置
    Hive 查看表的文件个数(用于分析小文件)
    Hive 窄表转宽表 , 长表转宽表
    MapReduce 实现分片取TopN 再Reduce取TopN
    Hbase 如何用对rowkey加盐
    VMware虚拟机安装Ubuntu操作系统
    使用springboot进行文件的上传下载操作(有源码)
    使用图片定位图片拍摄的具体位置
    vue实现Element-ui省市区三级联动(包含省市区文件和省市文件)
  • 原文地址:https://www.cnblogs.com/xintiao-/p/10447065.html
Copyright © 2020-2023  润新知