网站后端_Flask-第三方库.利用Flask-Socketio扩展构建实时流应用?

2024-06-04 22:18:25

模块简介:

说明: 此模块主要用于构建支持实时,双向基于事件的通信,将Websocket和Polling等其它实时通信方式封装成了通用接口,从而可在各个平台/浏览器/设备上稳定工作.

快速安装:

pip install flask-socketio
<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>

应用场景:

1. 实时分析, 服务端将数据推送到客户端,客户端可以为实时计数器,图表,日志等

2. 实时聊天, 通过命名空间和房间实现服务端Socket多路复用.

3. 流式传输, 已经支持任何二进制文件的传输,包括图片,视频,音频.

4. 文档合并, 运行多个用户同时编辑一个文档,并且能够看到每个用户做出的修改.

原理介绍:

wKiom1hs3QPRH_G_AACGnZkTY9w659.png

客户端: 利用基于flashsocket/websocket/iframe等封装的的socket对象和通用抽象方法(transport接口),包含数据编码/解码/心跳处理等

服务端: 利用namespace+room实现服务端socket多路复用,namespace基于客户端url中path部分区分应用,不同应用相互隔离,默认为/,room基于客户端指定namespace和room限制应用消息有效范围,如果没有指定,则除了自己外其它属于此namespace的socket都会收到消息.

常用事件:

服务端
connect 当客户端与服务端连接成功后被触发
message 当客户端使用send发送数据时被触发
disconnect 当客户端与服务端失去连接时被触发,如关闭浏览器,主动断开,掉线等任何断开连接的情况.
客户端
connect 当客户端与服务端连接成功后被触发
message 当服务端使用send发送数据时被触发
disconnect 当客户端主动断开连接时被触发.

接收消息: [客户端发送消息<- 回调确认 -> 服务接收消息]

# 方式一: 客户端通过send发送的未命名事件数据,服务端只能使用默认message事件接收处理, 客户端定义的事件回调函数接收的数据来自于服务端message事件处理函数的返回值

socket.send([data], ...., callback)


<script type="text/javascript">var socket = io.connect(location.protocol+'//'+document.domain+':'+location.port);socket.on('connect', function(){socket.send({'data': 'hello word!'}, function(data){console.log('#=> recive server data', data.data);});});
</script>

@io.on('message')
def message_handler(*args):print '#=> recive {0} data from client'.format(type(args[0])), argsreturn args

# 方式二: 客户端通过emit发送的命名事件数据,服务端只能使用对应自定义事件接收处理, 客户端定义的事件回调函数接收的数据来自于服务端对应事件处理函数的返回值

socket.emit(event_name, [data], ...., callback)


<script type="text/javascript">var socket = io.connect(location.protocol+'//'+document.domain+':'+location.port);socket.on('connect', function(){socket.emit('connect event', {'data': 'hello word!'}, function(data){console.log('#=> recive server data', data.data);});});
</script>

@io.on('connect event')
def connect_event_handler(*args):print '#=> recive {0} data from client'.format(type(args[0])), argsreturn args

发送消息: [服务端发送消息 <- 回调确认 -> 客户端接收消息]

# 方式一: 服务端通过send发送的未命名事件数据,可指定namespace, callback, broadcast, room, include_self额外参数,客户端只能使用默认message事件接收处理,服务端自定义的回调函数接收的数据来自于客户端对回调函数的调用,不是return的值.

send(message, namespace, callback, broadcast, room, include_self)


<script type="text/javascript">var socket = io.connect(location.protocol+'//'+document.domain+':'+location.port);socket.on('message', function(data, func){console.log('#=> recive server data', data.data);func();});
</script>

def message_event_callback(*args):print '#=> client called {0}'.format(inspect.stack()[0][-4:-2])
@io.on('connect')
def connect_event_handler():io.send({'data': 'hello word!'}, callback=message_event_callback)

# 方式二: 服务端通过emit发送的命名事件数据,可指定namespace, callback, broadcast, room, include_self额外参数,客户端只能使用对应自定义事件接收处理,服务端自定义的回调函数接收的数据来自于客户端对回调函数的调用,不是return的值.

emit(event, args, namespace, callback, broadcast, room, include_self)


<script type="text/javascript">var socket = io.connect(location.protocol+'//'+document.domain+':'+location.port);socket.on('connect event', function(data, func){console.log('#=> recive server data', data.data);func();});
</script>

def connect_event_callback(*args):print '#=> client called {0}'.format(inspect.stack()[0][-4:-2])
@io.on('connect')
def connect_event_handler():io.emit('connect event',{'data': 'hello word!'},callback=message_event_callback)

广播消息: [服务端发送消息 <- 回调确认 -> 客户端接收消息]

# 方式一: 服务端通过send发送的未命名事件数据,指定broadcast=True额外参数,可配合namespace/room/include_self额外参数来控制消息发往的应用,发往的房间,是否发给自己,客户端只能使用默认message事件接收处理


<script type="text/javascript">var socket = io.connect(location.protocol+'//'+document.domain+':'+location.port);var user = {name: ['zmanman', 'qmanman', 'smanman', 'lmanman'][Math.ceil(Math.random()*3)],};console.log('#=> current user: ', user);socket.on('connect', function(){socket.send(user);});socket.on('message', function(data){var $content = $('<p>广播: '+ data.name +'上线.</p>');$('#socketio').append($content);});
</script>

@io.on('message', namespace='/')
def online_notify_handler(*args):print '#=> recive {0} data from client'.format(type(args[0])), argssend(args[0], broadcast=True)

# 方式二: 服务端通过emit发送的命名事件数据,指定broadcast=True额外参数,可配合namespace/room/include_self额外参数来控制消息发往的应用,发往的房间,是否发给自己,客户端只能使用对应自定义事件接收处理


<script type="text/javascript">var socket = io.connect(location.protocol+'//'+document.domain+':'+location.port);var user = {name: ['zmanman', 'qmanman', 'smanman', 'lmanman'][Math.ceil(Math.random()*3)],};console.log('#=> current user: ', user);socket.on('connect', function(){socket.emit('online notify', user);});socket.on('online notify', function(data){var $content = $('<p>广播: '+ data.name +'上线.</p>');$('#socketio').append($content);});
</script>

@io.on('online notify', namespace='/')
def online_notify_handler(*args):print '#=> recive {0} data from client'.format(type(args[0])), argsemit('online notify', args[0], broadcast=True)

分组广播: [服务端发送消息 <- 回调确认 -> 客户端接收消息]

# 方式一: 服务端通过send发送的未命名事件数据,指定broadcast=True和room=xxoo额外参数,可配合namespace/include_self额外参数来控制消息发往的应用,发往的房间,是否发给自己,服务端提供了join_room和leave_room函数来对请求分组,客户端只能使用默认message事件接收处理


<script type="text/javascript">var socket = io.connect(location.protocol+'//'+document.domain+':'+location.port);var name = ['zmanman', 'qmanman', 'smanman', 'lmanman',][Math.ceil(Math.random()*3)];socket.on('connect', function(){socket.send('user join', {'room': 'room1', 'user': name});});socket.on('message', function(data){if(data.action=='sys boradcast'){console.log('公告: ' + data.message);};});
</script>

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2016-12-27 19:22:04
# @Author  : 李满满 (xmdevops@vip.qq.com)
# @Link    : http://xmdevops.blog.51cto.com/
# @Version : $Id$
from __future__ import absolute_import
# 说明: 导入公共模块
from flask import request
from datetime import datetime
from flask_socketio import join_room, leave_room
# 说明: 导入其它模块
from . import io
io.room_users = {}
io.reqid_info = {}
@io.on('connect')
def connect_event_handler():@io.on('message')def user_join_handler(action, data):if action == 'user join':room = data['room']user = data['user']susr = '{0}_{1}'.format(user, request.sid)io.room_users.setdefault(room, []).append(susr)join_room(room)io.reqid_info.setdefault(request.sid, {}).update({'room': room,'user': user,})message = '#=> [{0}] user: {1} action: {2} room {3}.'.format(datetime.now(), susr, 'join', room)print messageprint '#=> current rooms:', io.room_users.keys()users = []for item_users in io.room_users.itervalues():users.extend(item_users)print '#=> current users:', usersio.send({'action': 'sys boradcast', 'message': message})@io.on('disconnect')def disconnect_handler():user = io.reqid_info[request.sid]['user']susr = '{0}_{1}'.format(user, request.sid)room = io.reqid_info[request.sid]['room']io.room_users[room].remove(susr)message = '#=> [{0}] user: {1} action: {2} room {3}.'.format(datetime.now(), susr, 'leave', room)print messageprint '#=> current rooms:', io.room_users.keys()users = []for item_users in io.room_users.values():users.extend(item_users)print '#=> current users:', usersio.send({'action': 'sys boradcast', 'message': message})leave_room(room)

说明: 如上定义io.room_users = {},io.reqid_info = {}分别存储房间用户和请求信息,主要是为了存储在线用户以及用户信息,实际生产中可用Redis等后端替换.

扩展: 此插件运行时内存中也维护了一份儿request.sid属于哪个namespace哪个room,但由于connect时没有指定namespace和room而room并没有默认值,作者使用了None代替,并且为了后续的双向回调跟踪,所以在room中也包含了request.sid,而且在接口方面也做的不尽人意,所以还是推荐自己手动实现几个全局对象来存储这些信息.


# 方式二: 服务端通过emit发送的命名事件数据,指定broadcast=True和room=xxoo额外参数,可配合namespace/include_self额外参数来控制消息发往的应用,发往的房间,是否发给自己,服务端提供了join_room和leave_room函数来对请求分组,客户端只能使用自定义事件接收处理


<script type="text/javascript">var socket = io.connect(location.protocol+'//'+document.domain+':'+location.port);var name = ['zmanman', 'qmanman', 'smanman', 'lmanman',][Math.ceil(Math.random()*3)];socket.on('connect', function(){socket.emit('user join', {'room': 'room1', 'user': name});});socket.on('sys broadcast', function(data){console.log('公告: ' + data.message);});
</script>

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2016-12-27 19:22:04
# @Author  : 李满满 (xmdevops@vip.qq.com)
# @Link    : http://xmdevops.blog.51cto.com/
# @Version : $Id$
from __future__ import absolute_import
# 说明: 导入公共模块
import inspect
from flask import request
from datetime import datetime
from flask_socketio import join_room, leave_room
# 说明: 导入其它模块
from . import io
io.room_users = {}
io.reqid_info = {}
@io.on('connect')
def connect_event_handler():@io.on('user join')def user_join_handler(data):user = data['user']room = data['room']susr = '{0}_{1}'.format(user, request.sid)io.room_users.setdefault(room, []).append(susr)io.reqid_info.setdefault(request.sid, {}).update({'room': room,'user': user,})join_room(room)message = '#=> [{0}] user: {1} action: {2} room: {3}.'.format(datetime.now(), susr, inspect.stack()[0][-4:-2], room)print messageusers = []for item_users in io.room_users.itervalues():users.extend(item_users)print '#=> current users: ', usersio.emit('sys broadcast', {'message': message})@io.on('disconnect')def disconnect_handler():user = io.reqid_info[request.sid]['user']susr = '{0}_{1}'.format(user, request.sid)room = io.reqid_info[request.sid]['room']io.room_users[room].remove(susr)message = '#=> [{0}] user: {1} action: {2} room: {3}.'.format(datetime.now(), susr, inspect.stack()[0][-4:-2], room)print messageio.emit('sys broadcast', {'message': message})leave_room(room)

错误处理:


# 说明: 处理指定NameSpace的异常
@io.on_error()
def error_handler(e):print request.event['message']print request.event['args']
# 说明: 处理所有NameSpace的异常
@io.on_error_default
def default_error_handler(e):print request.event['message']print request.event['args']

说明: request.event是在被io.on装饰的时候被附加上去的属性,是一个字典,默认包含message和args,也就是事件名和事件相关的参数,在出现异常时可通过打印它们来获取异常请求信息.

异步切换:


说明: flask-socketio和客户端和服务端的交互是双向的,当你循环send/emit的时候会出现缓冲区阻塞,可通过io.sleep(0.1)或import eventlet;eventlet.monkey_patch()打补丁来实现异步IO,但是这样发送极快,如果使用eventlet或基于eventlet的gevent异步协程库的时候使用eventlet.sleep(0.1),减慢切换时间,这样防止浏览器端卡死.

全局对象:


说明: 作为FLASK插件,基于程序上下文可使用current_app, g全局对象,基于请求上下文可使用request, session全局对象, 所有的会话是基于request.sid, 且运行时会自动注册event和namespace到request对象.

用户验证:


说明: flask-socketio默认是不支持用户验证的,可借助flask-login插件通过login_user()函数来将用户ID信息存储到本地,然后通过程序上下文中的current_user对象来还原用户对象,判断是否登录是否有权限访问等.

消息队列:


说明: flask-socketio还支持多消费者分布式横向扩展,只需在实例化SocketIO时指定async_mode和message_queue,message_queue目前只支持redis://和amqp://,接口分别使用的是redis和kombu.由于每个节点都维护着一份儿客户端连接集,所以如果使用了前端Nginx负载均衡,需要Session同步或IP_HASH算法负载,节点启动时会自动订阅flask-socketio频道,所以我们可以用PY-REDIS客户端去发布消息,那么所有节点都可以获取到消息然后再分发给指定的客户端.

转载于:https://blog.51cto.com/xmdevops/1889053

网站后端_Flask-第三方库.利用Flask-Socketio扩展构建实时流应用?相关推荐

  1. 前端学习(2739):重读vue电商网站49之第三方库使用CDN

    通过 externals 加载外部 CDN 资源 默认情况下,通过 import 语法导入的第三方依赖包,最终会被打包合并到同一个文件中,从而导致打包成功后,单文件体积过大的问题. 例如上述 chun ...

  2. 如何利用Tensorflow和OpenCV构建实时对象识别程序?

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 引言 在本文中,将逐步介绍如何使用Tensorflow(TF)的新 ...

  3. 利用Kafka和Cassandra构建实时异常检测实验

    导言 异常检测是一种跨行业的方法,用于发现事件流中的异常事件 - 它适用于物联网传感器,财务欺诈检测,安全性,威胁检测,数字广告欺诈以及许多其他应用程序.此类系统检查流数据以检查异常或不规则,并在检测 ...

  4. 下列不属于python第三方库的是-测验9: Python计算生态纵览 (第9周)-单选题

    1.以下选项不是Python数据分析方向第三方库是:‪‬‪‬‪‬‪‬‪‬‮‬‭‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‭‬‪‬‪‬‪‬‪‬‪‬ ...

  5. pip安装更新、第三方库对应的python解释器版本、pip安装第三方库,压缩包离线安装,pycharm快捷安装及pycharm中terminal的使用,timeout超时报错

    文章有点长,但是很详细,还望大家耐心看 之前在安装python的文章中已经给大家配置了pip环境变量(不了解的点这里去看看),这里就能直接在cmd窗口,不用切换路径,就能使用pip了. 首先 使用pi ...

  6. 总结Python语言程序设计课程-推荐的Python第三方库

    从数据处理到人工智能 python数据分析方向第三方库有:Numpy, SciPy, Pandas Python数据可视化方向的第三方库有: Seaborn, Matplotblib, Mayavi, ...

  7. 不是python中用于开发用户界面的第三方库-模拟试卷C

    原标题:模拟试卷C 一.单项选择题 1. 按照"后进先出"原则组织数据的数据结构是____ 队列 栈 双向链表 二叉树 2. 以下选项的叙述中,正确的是 循环队列有队头和队尾两个指 ...

  8. 不属于python开发用户界面第三方库的是-模拟试卷C【单项选择题】

    原标题:模拟试卷C[单项选择题] 1. 按照"后进先出"原则组织数据的数据结构是____ 队列 栈 双向链表 二叉树 2. 以下选项的叙述中,正确的是 循环队列有队头和队尾两个指针 ...

  9. 第三方库自动安装脚本(复习)

    第三方库自动安装脚本 一."第三方库自动安装脚本"问题分析 1.1 问题分析第三方库自动安装脚本需求:批量安装第三方库需要人工干预,能否自动安装?自动执行pip逐一根据安装需求安装 ...

最新文章

  1. 外部链接linux下的mysql,Linux下mysql实现远程链接
  2. Python3 中 random模块
  3. tabnavigator_使用TabNavigator在Firefox中享受桌面Alt-Tab样式导航
  4. 下列不是unix linux,下列软件中,不是操作系统的是______。A) LinuxB) UNIXC) MS-DOSD) MS-OfficeA.B.C.D._考题宝...
  5. 《如何搭建小微企业风控模型》第五节 特征工程(上)
  6. 你在微信漂流瓶里遇到过哪些有意思的人和事?
  7. ASP.NET 经典60道面试题
  8. html怎么把字转换为行内元素,什么是行内元素?
  9. 三维模型等大长方体剖分算法实现
  10. DNS协议及Bind应用
  11. 设计原则-依赖倒置原则
  12. 大学计算机思维导论第七讲答案,中国大学MOOC计算思维导论网课答案
  13. Unity VR开发教程 OpenXR+XR Interaction Toolkit 2.1.1 (一) 安装和配置
  14. 6.1 静态路由及默认路由的基本配置
  15. 数字信号处理之数字混频
  16. JDBC学习笔记(SQL语句的执行)
  17. 依据数据简单分析,发掘潜在客户
  18. 视觉软件 VisionPro 定位引导3(卡尺CogFindLineTool工具以及辅助工具)
  19. JavaWeb-RequestResponse
  20. AMD发布22.11.2驱动,支持《极品飞车:不羁》等游戏

热门文章

  1. 微信小程序开发--数据绑定
  2. Java——String类中的compareTo方法总结
  3. [BZOJ]2563: 阿狸和桃子的游戏
  4. aspose.words for java操作文档doc,设置一级二级三级标题以及段落表格等详情
  5. 「NOI2017」泳池
  6. 02.Python基础
  7. C#之Directory类、DirectoryInfo类和Fileinfo,File以及FilesSystemInfo
  8. 【BZOJ 3729】3729: Gty的游戏 (Splay维护dfs序+博弈)
  9. Sql server Always On 读写分离配置方法
  10. 卷积神经网络模型如何辨识裸体图片