最近在公司闲着没事研究了几天,终于搞定了SSE从理论到实际应用,中间还是有一些坑的。

1.SSE简介

SSE(Server-sent events)翻译过来为:服务器发送事件。是基于http协议,和WebSocket的全双工通道(web端和服务端相互通信)相比,SSE只是单通道(服务端主动推送数据到web端),但正是由于此特性,在不需要客户端频繁发送消息给服务端,客户端却需要实时或频繁显示服务端数据的业务场景中可以使用。如:新邮件提示,在浏览网页时提示有新信息或新博客,监控系统实时显示数据。。。

在web端消息推送功能中,由于传统的http协议需要客户端主动发送请求,服务端才会响应;基本的ajax轮寻技术便是如此,但是此方法需要前端不停的发送ajax请求给后端服务,无论后端是否更新都要执行相应的查询,无疑会大大增加服务器压力,浪费不必要的资源。而SSE解决了这种问题,不需前端主动请求,后端如果有更新便会主动推送消息给web端。

在SSE中,浏览器发送一个请求给服务端,通过响应头中的Content-Type:text/event-stream;等 向客户端证明这是一个长连接,发送的是流数据,这时客户端不会关闭连接,一直等待服务端发送数据。

关于SSE的前端用法请自行百度或参考一下连接:

http://www.ruanyifeng.com/blog/2017/05/server-sent_events.html

2.python框架flask中SSE的包flask_sse的使用

坑点:刚开始根据,自信的以为在服务器返回数据时只要是response头部添加这三个字段便实现了SSE功能,但是在flask启动自带服务器后,发现浏览器总是触发error事件,并且从新连接。这样的话和ajax轮询没有任何区别。

后来找到flask框架的flask_sse文档 http://flask-sse.readthedocs.io/en/latest/quickstart.html  其中发现:

Server-sent events do not work with Flask’s built-in development server, because it handles HTTP requests one at a time. The SSE stream is intended to be an infinite stream of events, so it will never complete. If you try to run this code on with the built-in development server, the server will be unable to take any other requests once you connect to this stream. Instead, you must use a web server with asychronous workers. Gunicorn can work with gevent to use asychronous workers: see gunicorn’s design documentation.

  flask内置服务器不适合SSE功能,一次只能处理一个请求。所以只能使用具有异步功能的服务器来完成此项功能。所以本人想在不引入任何包的情况下完成此功能是不可能的了。

在官方给出的flask_sse 文档中,使用 gunicorn(WSGI协议的一个容器,和uWSGI一样的功能) + gevent 作为异步功能的服务器。

ubuntu系统中安装:pip install flask-sse gunicorn gevent

由于官方文档中给出的实例代码是MTV(model-template-view)模式,前后端代码杂糅在一起,看着不舒服,于是改成了restful风格的代码。

下面给出restful风格的flask_sse实现的实时聊天(消息推送)功能。

后端主要文件

sse.py

 #coding:utf8
# 将程序转换成可以使用gevent框架的异步程序
from gevent import monkey
monkey.patch_all() from flask import Flask, send_from_directory, redirect, url_for, request, jsonify
from flask_sse import sse app = Flask(__name__)
#redis路径
app.config["REDIS_URL"] = "redis://localhost"
#app注册sse的蓝图,并且访问路由是/stream1
app.register_blueprint(sse, url_prefix='/stream1') #重定向到发送消息页面
@app.route('/')
def index():
return redirect(url_for('.index', _external=True) + 'upload/'+'send_messages.html') #接收send_messages.html文件中接口发送的数据,并且通过sse实时推送给用户
@app.route('/messages',methods=['POST'])
def send_messages():
channel=request.values.get('channel')
message=request.values.get('message') #关于channel的使用==> http://flask-sse.readthedocs.io/en/latest/advanced.html
#如channel是channel_bob,则只有channel_bob.html才能接收数据
#sse推送消息
sse.publish({"message": message}, type='social', channel=channel)
return jsonify({'code': 200, 'errmsg': 'success', 'data': None}) @app.route('/upload/<path:path>')
def send_file(path):
return send_from_directory('upload/', path) if __name__=='__main__':
app.run()

前端接收消息文件

channel_bob.html

 <!DOCTYPE html>
<html>
<head>
<title>Flask-SSE Quickstart</title>
</head>
<body>
<h1>Channel:channel_bob</h1>
<div id="get_message"></div>
<script src="jquery-3.1.1.js" type="text/javascript" charset="utf-8"></script>
<script>
$(function () {
//只接收channel为channel_bob的消息
var source = new EventSource("/stream1?channel=channel_bob");
source.addEventListener('social', function (event) {
var data = JSON.parse(event.data);
$('#get_message').append(data.message+'&nbsp;&nbsp;&nbsp;&nbsp;');
}, false);
source.addEventListener('error', function (event) {
console.log('reconnected service!')
}, false);
})
</script>
</body>
</html>

前端发送消息文件

send_messages.html

 <!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
channel:<input type="text" id="channel" value=""/>
<div>You can choise these channels: channel_bob,channel_tom,channel_public</div>
<br/>
message:<input type="text" id="message" value=""/>
<br/>
<button id="button">send message</button>
<div id="success"></div>
<script src="jquery-3.1.1.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
<!--发送消息页面,发送给三个不同的channel,点击发送按钮后,对于的channel页面会接收到数据-->
$(function () {
$("#button").click(function () {
var channel = $('#channel').val();
var message = $('#message').val();
var json_data = {
'channel': channel,
'message': message
}
var http_url = 'http://127.0.0.1:5000/';
$.ajax({
url: http_url + "messages",
type: 'post',
dataType: "json",
data: json_data,
success: function (data) {
if (data.code == 200) {
$('#success').text('Send message success!')
}
},
error: function (jqXHR, textStatus, errorThrown) {
console.log(textStatus)
hide_popover('#user_deatil_submit', '程序错误,请联系管理员')
}
});
});
})
</script>
</body>
</html>

项目上传到github上,有详细注释。

https://github.com/Rgcsh/sse_chait

坑点:

1.uWSGI配置时,在sse_chait.ini配置文件中,socket参数是给在搭建nginx+uWSGI服务时用的,http参数是uWSGI服务(浏览器直接访问网址)时用的

2.在服务启动时,如果使用uWSGI+gevent启动服务时,要在sse.py顶部添加

from gevent import monkey
monkey.patch_all()

和sse_chait.ini添加

gevent = 100

3.真正的SSE长连接,是一个连接持续工作,并非http请求一样,收到回复就断开连接,如果每次收到响应后,便触发error事件,说明开发的SSE功能有问题。

真正的SSE连接应该如下,响应时间和请求头,响应头如下

参考网址:

http://flask-sse.readthedocs.io/en/latest/index.html

https://www.cnblogs.com/ajianbeyourself/p/3970603.html

www.bubuko.com/infodetail-1028284.html

https://www.cnblogs.com/franknihao/p/7202253.html

http://gunicorn.readthedocs.io/en/latest/getstart.html

http://heipark.iteye.com/blog/1847421

www.ruanyifeng.com/blog/2017/05/server-sent_events.html

我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan

SSE(Server-sent events)技术在web端消息推送和实时聊天中的使用的更多相关文章

  1. Asp.net SignalR 实现服务端消息推送到Web端

              之前的文章介绍过Asp.net SignalR,  ASP .NET SignalR是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.  今天我 ...

  2. python 全栈开发,Day131(向app推送消息,玩具端消息推送)

    先下载github代码,下面的操作,都是基于这个版本来的! https://github.com/987334176/Intelligent_toy/archive/v1.4.zip 注意:由于涉及到 ...

  3. Spring mvc服务端消息推送(SSE技术)

    SSE技术是基于单工通信模式,只是单纯的客户端向服务端发送请求,服务端不会主动发送给客户端.服务端采取的策略是抓住这个请求不放,等数据更新的时候才返回给客户端,当客户端接收到消息后,再向服务端发送请求 ...

  4. 使用SignalR实现服务端消息推送

    概述 这篇文章参考的是Server Broadcast with SignalR 2这篇教程,很不错的一篇教程,如果有兴趣的话可以查看原文,今天记录下来作为一个学习笔记,这样今后翻阅会更方便一点. 这 ...

  5. SignalR Self Host+MVC等多端消息推送服务(1)

    一.概述 由于项目需要,最近公司项目里有个模块功能,需要使用到即时获得审批通知:原本的设计方案是使用ajax对服务器进行定时轮询查询,刚刚开始数据量和使用量不大的时候还好,后来使用量的增加和系统中各种 ...

  6. SignalR Self Host+MVC等多端消息推送服务(3)

    一.概述 最近项目确实太忙,而且身体也有点不舒服,慢性咽炎犯了,昨晚睡觉时喘不过气来,一直没休息好,也没什么时间写博客,今天朋友问我什么时候能出web端的消息发送的文章时,我还在忙着改项目的事,趁着中 ...

  7. SignalR Self Host+MVC等多端消息推送服务(2)

    一.概述 上次的文章中我们简单的实现了SignalR自托管的服务端,今天我们来实现控制台程序调用SignalR服务端来实现推送信息,由于之前我们是打算做审批消息推送,所以我们的demo方向是做指定人发 ...

  8. VDN For PB Web实现消息推送

    利用VesnData.Net(VDN)的互联网数据驱动功能我们实现了PB连接互联网数据库的功能.在互联网开发的过程中我们往往有些消息或者数据希望即时能够通知到各个客户端,现在比较流行的一种技术就是消息 ...

  9. Web端服务器推送技术原理分析及dwr框架简单的使用

    1 背景 “服务器推送技术”(ServerPushing)是最近Web技术中最热门的一个流行术语.它是继“Ajax”之后又一个倍受追捧的Web技术.“服务器推送技术”最近的流行跟“Ajax ”有着密切 ...

随机推荐

  1. mysql常用基础操作语法(十)~~子查询【命令行模式】

    mysql中虽然有连接查询实现多表连接查询,但是连接查询的性能很差,因此便出现了子查询. 1.理论上,子查询可以出现在查询语句的任何位置,但实际应用中多出现在from后和where后.出现在from后 ...

  2. ClientToScreen 和ScreenToClient 用法

    ClientToScreen( )是把窗口坐标转换为屏幕坐标 ScreenToClient( )是把屏幕坐标转换为窗口坐标 屏幕坐标是相对于屏幕左上角的,而窗口坐标是相对于窗口用户区左上角的 VC下, ...

  3. SQL注入攻击三部曲之进阶篇

    SQL注入攻击三部曲之进阶篇 通过入门篇的学习,我们知道了SQL注入攻击的判断方法,但是如果想侵入网站,获取网站的机密内容,那么仅靠入门篇的知识是无法达到的.本篇文章我们将进一步的分析SQL注入攻击. ...

  4. Apache Hive 基本理论与安装指南

    一.Hive的基本理论 Hive是在HDFS之上的架构,Hive中含有其自身的组件,解释器.编译器.执行器.优化器.解释器用于对脚本进行解释,编译器是对高级语言代码进行编译,执行器是对java代码的执 ...

  5. springboot集成Actuator

    Actuator监控端点,主要用来监控与管理. 原生端点主要分为三大类:应用配置类.度量指标类.操作控制类. 应用配置类:获取应用程序中加载的配置.环境变量.自动化配置报告等与SpringBoot应用 ...

  6. 标注-隐马尔可夫模型HMM的探究

    1 HMM基本概念1.1 定义1.2 观测序列生成过程1.3 HMM的三个问题2 概率计算算法2.1 直接计算算法2.2 前向算法forward algorithm2.3 后向算法2.4 一些概率与期 ...

  7. 【CF235C】Cyclical Quest(后缀自动机)

    [CF235C]Cyclical Quest(后缀自动机) 题面 洛谷 题解 大致翻译: 给定一个串 然后若干组询问 每次也给定一个串 这个串可以旋转(就是把最后一位丢到最前面这样子) 问这个串以及其 ...

  8. 【BZOJ3196】二逼平衡树(树状数组,线段树)

    [BZOJ3196]二逼平衡树(树状数组,线段树) 题面 BZOJ题面 题解 如果不存在区间修改操作: 搞一个权值线段树 区间第K大--->直接在线段树上二分 某个数第几大--->查询一下 ...

  9. 【BZOJ1058】【ZJOI2007】报表统计(链表,堆,Splay)

    [BZOJ1058][ZJOI2007]报表统计 题面 题目描述 Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工作,作为她的生日礼物之一. 经过仔细观 ...

  10. [BZOJ1543] 生成树计数 (Kruskal)

    Description 给定一个连通的带边权的图(允许自环和重边),求不同的最小生成树个数.两个生成树不同当它们所用的边的序号不同,换句话说,重边算多次. Input 第一行n,m,表示点数和边数(1 ...