websocket/dwebsocket 实现前后端的实时通信
1. 用bottle框架,自己写一个服务端实现:
转载 :http://www.linuxyw.com/813.html
功能:用websocket技术,在运维工具的浏览器上实时显示远程服务器上的日志信息
一般我们在运维工具部署环境的时候,需要实时展现部署过程中的信息,或者在浏览器中实时显示程序日志给开发人员看。你还在用ajax每隔段时间去获取服务器日志?out了,试试用websocket方式吧
我用bottle框架,写了个websocket服务端,浏览器连接到websocket server,再用python subprocess获取远程服务器的日志信息,subprocess,就是用Popen调用shell的shell命令而已,这样可以获取到实时的日志了,然后再send到websocket server中,那连接到websocket server的浏览器,就会实时展现出来了
用二台服务器来实现这个场景,A服务器是websocket服务端,B服务器是日志端
A服务器是我浏览器本机,websocket服务端也是这台机,IP是:192.168.1.221
B服务器是要远程查看日志的服务器,我这里用:192.168.1.10
以下是A服务器的websocket servet的python代码:
#!/usr/bin/env python
#coding=utf-
# __author__ = '戴儒锋'
# http://www.linuxyw.com
"""
执行代码前需要安装
pip install bottle
pip install websocket-client
pip install bottle-websocket
"""
from bottle import get, run
from bottle.ext.websocket import GeventWebSocketServer
from bottle.ext.websocket import websocket
users = set() # 连接进来的websocket客户端集合
@get('/websocket/', apply=[websocket])
def chat(ws):
users.add(ws)
while True:
msg = ws.receive() # 接客户端的消息
if msg:
for u in users:
u.send(msg) # 发送信息给所有的客户端
else:
break
# 如果有客户端断开连接,则踢出users集合
users.remove(ws)
run(host='0.0.0.0', port=, server=GeventWebSocketServer)
记得安装bottle、websocket-client 、bottle-websocket 模块,服务端允许所有的IP访问其8000端口
在电脑桌面,写一个简单的HTML5 javascripts页面,随便命名了,如web_socket.html,这个页面使用了websocket连接到websocket服务端:
<!DOCTYPE html>
<html>
<head>
</head>
<style>
#msg{
width:400px; height:400px; overflow:auto; border:2px solid #;background-color:#;color:#ffffff;
}
</style>
</head>
<body>
<p>实时日志</p>
<div id="msg"></div>
<script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
/* !window.WebSocket、window.MozWebSocket检测浏览器对websocket的支持*/
if (!window.WebSocket) {
if (window.MozWebSocket) {
window.WebSocket = window.MozWebSocket;
} else {
$('#msg').prepend("<p>你的浏览器不支持websocket</p>");
}
}
/* ws = new WebSocket 创建WebSocket的实例 注意设置对以下的websocket的地址哦*/
ws = new WebSocket('ws://192.168.1.221:8000/websocket/');
/*
ws.onopen 握手完成并创建TCP/IP通道,当浏览器和WebSocketServer连接成功后,会触发onopen消息
ws.onmessage 接收到WebSocketServer发送过来的数据时,就会触发onmessage消息,参数evt中包含server传输过来的数据;
*/
ws.onopen = function(evt) {
$('#msg').append('<li>websocket连接成功</li>');
}
ws.onmessage = function(evt) {
$('#msg').prepend('<li>' + evt.data + '</li>');
}
});
</script>
</body>
</html>
注意:WebSocket('ws://192.168.1.221:8000/websocket/'); 这里的192.168.1.221一定要改成你的websocket服务端IP,切记!!!
到这里,就搞定浏览器连接到websocket服务端的场景了,现在要A服务器里写一段代码,去采集B服务器的实时信息了,其实采集原理很简单,就是使用shell中的tailf命令,实时显示最新的信息而已,我们在这段脚本中,使用subprocess.Popen()来远程查看日志信息:
python代码如下:
#!/usr/bin/python
# encoding=utf-
import subprocess
import time
from websocket import create_connection
# 配置远程服务器的IP,帐号,密码,端口等,因我做了双机密钥信任,所以不需要密码
r_user = "root"
r_ip = "192.168.1.10"
r_port =
r_log = "/tmp/web_socket.log" # 远程服务器要被采集的日志路径
# websocket服务端地址
ws_server = "ws://192.168.1.221:8000/websocket/"
# 执行的shell命令(使用ssh远程执行)
cmd = "/usr/bin/ssh -p {port} {user}@{ip} /usr/bin/tailf {log_path}".format(user=r_user,ip=r_ip,port=r_port,log_path=r_log)
def tailfLog():
"""获取远程服务器实时日志,并发送到websocket服务端"""
popen = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
print('连接成功')
ws = create_connection(ws_server) # 创建websocket连接
while True:
line = popen.stdout.readline().strip() #获取内容
if line:
ws.send(line) #把内容发送到websocket服务端
print time.time()
if __name__ == '__main__':
tailfLog()
文章最后再解析subprocess.Popen的原理和功能
执行websocket服务端脚本和上面这个websocket客户端采集脚本,再打开用浏览器打开上面的html5页面后,环境就基本部署好了,双websocket客户端连接到websocket服务端中
上面脚本指定的r_log = "/tmp/web_socket.log"日志路径,我们需要生成这个日志文件,并不停地往里面写入日志,这样才能在浏览器中实时显示效果(真实场景中,可以指定服务器某日志,如apache,nginx日志等)
我们在B服务器写一段python代码,然后每隔一秒就往r_log = "/tmp/web_socket.log"日志中写入内容:
python代码如下:
#!/usr/bin/env python
#coding=utf-
import time
import random
log_path = '/tmp/web_socket.log'
while :
with open(log_path,'a') as f:
f.write('[%s] %s \n' % (time.ctime(),random.random()))
time.sleep()
2. 使用dwebsocket
实验环境:
django 2.1
python 3.5
ansible 2.7
dwebsocket 0.5.5
参考: https://www.cnblogs.com/huguodong/p/6611602.html
安装:
pip install dwebsocket
一些方法和属性:
1.request.is_websocket() 如果是个websocket请求返回True,如果是个普通的http请求返回False,可以用这个方法区分它们。 2.request.websocket 在一个websocket请求建立之后,这个请求将会有一个websocket属性,用来给客户端提供一个简单的api通讯,如果request.is_websocket()是False,这个属性将是None。 3.WebSocket.wait() 返回一个客户端发送的信息,在客户端关闭连接之前他不会返回任何值,这种情况下,方法将返回None 4.WebSocket.read() 如果没有从客户端接收到新的消息,read方法会返回一个新的消息,如果没有,就不返回。这是一个替代wait的非阻塞方法 5.WebSocket.count_messages() 返回消息队列数量 6.WebSocket.has_messages() 如果有新消息返回True,否则返回False 7.WebSocket.send(message) 向客户端发送消息 8.WebSocket.__iter__() websocket迭代器
客户端:
{% extends 'base.html' %}
{% load widget_tweaks %}
{% load staticfiles %} {% block title %}
<title>游戏脚本上传</title>
{% endblock %} {% block content_header %}
游戏脚本上传
{% endblock %} {% block morejs %}
<script>
$(function() {
$('#id_log_start').click(function () {
if (window.s) {
window.s.close()
}
/*创建socket连接*/
var socket = new WebSocket("ws://" + window.location.host + "/ansible/websocket/");
socket.onopen = function () {
console.log('WebSocket open');//成功连接上Websocket
};
socket.onmessage = function (e) {
console.log('message: ' + e.data);//打印出服务端返回过来的数据
$('#id_log').append( e.data + '\n');
document.getElementById('id_log').scrollTop = document.getElementById('id_log').scrollHeight //testarea自动向下滚动
};
// Call onopen directly if socket is already open
if (socket.readyState == WebSocket.OPEN) socket.onopen();
window.s = socket;
}); $('#id_log_stop').click(function () {
if (window.s) {
window.s.close();//关闭websocket
console.log('websocket closed');
}
}); }); </script>
{% endblock %} {% block onload %}
onload="business_batch_update_dis()"
{% endblock %} {% block content %}
<section class="content">
<div class="row">
<div class="col-xs-12">
<div class="box">
<div class="box-header">
<div class="row">
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
<div>
<div class="col-md-2">
<select class="form-control" name="name_game" id="id_game" onchange="getcenter('{% url 'business:getcenter' %}','{{csrf_token}}')" >
<option value="" selected="selected">--选择游戏--</option>
{% for i in game %}
<option value="{{i.id}}">{{i}}</option>
{% endfor %}
</select>
</div>
</div> <div>
<div class="col-md-2">
<select class="form-control" name="name_center" id="id_center" onchange="getarea('{% url 'business:getarea' %}','{{csrf_token}}')" >
<option value="" selected="selected">--选择中心--</option>
</select>
</div>
</div> <div>
<div class="col-md-2">
<select class="form-control" name="name_area" id="id_area" onchange="getserver('{% url 'business:getserver' %}','{{csrf_token}}')" >
<option value="" selected="selected">--选择大区--</option>
</select>
</div>
</div> </form>
</div>
<br/>
<br/> <div class="row">
<div class="col-md-2">
<form class="form-horizontal" id="id_batch_update" method="post" action="" onsubmit="return update_version_check()">
{% csrf_token %}
<div >
<select class="form-control" size="" id="id_server" name="name_server" multiple="multiple" >
</select> </div>
</form>
</div> <div class="col-md-10"> <div class="row">
<label class="col-md-2 " >参数配置:</label>
</div> <div class="row form-horizontal">
<div class="col-md-3 ">
<input name="name_script" type="text" class="form-control" placeholder="src:源文件" required="required">
</div> <div class="col-md-3">
<input name="name_path" type="text" class="form-control" placeholder="dest:目标路径" required="required">
</div> <div class="col-md-2">
<button class="btn btn-success" type="button" id="id_submit_upload" onclick="script_upload_check('{{csrf_token}}')">上传</button>
</div> <div class="col-md-2">
<button class="btn btn-success" type="button" id="id_log_start" >查看日志</button>
</div> <div class="col-md-2">
<button class="btn btn-success" type="button" id="id_log_stop" >停止查看</button>
</div> </div> <br/>
<br/> <div class="row">
<label class="col-md-2 " >执行结果:</label>
</div> <div class="row">
<div class="col-md-12"> <textarea name="content" id="id_result" rows="" cols="" class="form-control">
</textarea>
</div>
</div>
<br/>
<br/> <div class="row">
<label class="col-md-2 " >Ansible日志:</label>
</div> <div class="row">
<div class="col-md-12">
<textarea name="content" id="id_log" rows="" cols="" class="form-control">
</textarea>
</div>
</div>
</div> </div> </div> </div> </div> <div class="box-body">
<div class="dataTables_wrapper form-inline dt-bootstrap" id="example2_wrapper"> </div>
</div>
<!-- /.box-body --></div>
<!-- /.box -->
<!-- /.box --></div>
<!-- /.col --></div>
<!-- /.row --></section>
{% endblock %}
app 的views
from dwebsocket import accept_websocket
import subprocess @accept_websocket
def an_websocket(request):
if request.is_websocket():
print('ssssssssssssssssssssssssss')
print ('socket..............')
r_log = "/tmp/ansible.log" # 远程服务器要被采集的日志路径
# 执行的shell命令(使用ssh远程执行)
cmd = "/usr/bin/tail -f {log_path}".format(log_path=r_log)
popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
while True:
line = popen.stdout.readline().strip() #获取内容
print (line)
if line:
# print('进入发送流程.........')
# for i in line:
request.websocket.send(line) print (time.time())
# request.websocket.send('a') else:
return HttpResponse('点个赞')
app 的urls.py
url(r'^websocket/$', an_websocket, name="an_websocket"),u 用的是namespaace+name的方式.
出现的问题:
1. 上面查看日志用的是 tail -f 命令。 在 客户端关闭后发现 socket 连接是 CLOSE_WAIT 状态。并且 tail -f 的任务也没有结束。估计的等待 CLOSE_WAIT 状态释放(有待考证).
websocket/dwebsocket 实现前后端的实时通信的更多相关文章
- 前后端常用通讯方式-- ajax 、websocket
一.前后端常用通讯方式 1. ajax 浏览器发起请求,服务器返回数据,服务器不能主动返回数据,要实现实时数据交互只能是ajax轮询(让浏览器隔个几秒就发送一次请求,然后更新客户端显示.这种方式实际 ...
- WebSocket实现前后端通讯
WebSocket实现前后端通讯 长安如梦里,何日是归期. 简介:我们上线了一个商城项目,移交运营团队使用之后,他们要求商城有新订单来的时候同时加上声音提示,让她们可以及时知道有单来了.我这边想了想, ...
- Nginx + uwsgi + django + websocket(dwebsocket)环境部署
1.安装nginx(/export/servers/nginx/) 保证/export/servers/nginx/是nginx的安装目录 /export/servers/nginx/conf/dom ...
- 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 二十一║Vue实战:开发环境搭建【详细版】
缘起 哈喽大家好,兜兜转转终于来到了Vue实战环节,前边的 6 篇关于Vue基础文章我刚刚简单看了看,感觉写的还是不行呀,不是很系统,所以大家可能看上去比较累,还是得抽时间去润润色,修改修改语句和样式 ...
- 从零开始搭建django前后端分离项目 系列一(技术选型)
前言 最近公司要求基于公司的hadoop平台做一个关于电信移动网络的数据分析平台,整个项目需求大体分为四大功能模块:数据挖掘分析.报表数据查询.GIS地理化展示.任务监控管理.由于页面功能较复杂,所以 ...
- 如何简单区分Web前后端与MVC
MVC是开发所有软件所必须涉及的基本几个划分 M主要负责数据与模型,V主要负责显示C主要负责交互与业务所以不管是前端还是后端,都是有MVC的.MVC是一个对于软件简单的抽象,不管是M还是V,还是C都是 ...
- 基于flask和百度AI接口实现前后端的语音交互
话不多说,直接怼代码,有不懂的,可以留言 简单的实现,前后端的语音交互. import os from uuid import uuid4 from aip import AipSpeech from ...
- 前后端API交互数据加密——AES与RSA混合加密完整实例
前言 前段时间看到一篇文章讲如何保证API调用时数据的安全性(传送门:https://blog.csdn.net/ityouknow/article/details/80603617),文中讲到利用R ...
- Java 前后端分离项目:微人事
本文适合刚学习完 Java 语言基础的人群,跟着本文可了解和运行项目,本示例是在 Windows 操作系统下演示. 本文作者:HelloGitHub-秦人 大家好!这里是 HelloGitHub 推出 ...
随机推荐
- 51nod--1212 最小生成树
题目: 1212 无向图最小生成树 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 N个点M条边的无向连通图,每条边有一个权值,求该图的最小生成树. Inpu ...
- Linux命令之top、ulimit、free
1.[ulimit命令] ulimit命令用来限制系统用户对shell资源的访问. 假设有这样一种情况,当一台 Linux 主机上同时登陆了 10 个人,在系统资源无限制的情况下,这 10 个用户同时 ...
- 解决layui选中项下一页清空问题
项目中遇到给用户在所有产品中匹配一部分产品.用layui 第一页选好之后到第二页再选,等回到第一页时之前选择的都没了,解决这个问题的办法如下: //勾选的产品id集合 var chooseAdids ...
- JsRender 前端渲染模板基础学习
JsRender前端渲染模板 使用模板,可以预先自定义一些固定格式的HTML标签,在需要显示数据时,再传入真实数据组装并展示在Web页中:避免了在JS中通过“+”等手动分割.连接字符串的复杂过程:针对 ...
- 对象的宽度、top位置,x坐标属性
DOM对象 DOM对象属性 对应css 说明 读/写 width obj.clientWidth=20 1. 内联样式 <p style="width:20px"&g ...
- PID控制器开发笔记之一:PID算法原理及基本实现
在自动控制中,PID及其衍生出来的算法是应用最广的算法之一.各个做自动控制的厂家基本都有会实现这一经典算法.我们在做项目的过程中,也时常会遇到类似的需求,所以就想实现这一算法以适用于更多的应用场景. ...
- PL\SQL 随学笔记
一.在PL\SQL语句块begin...end;中,不能直接使用select,必须与into结合查询. 例如: declare aa:=22; id2 integer; begin select * ...
- linux三剑客
grep grep "oldboy" test.txt 过滤掉文件中oldboy的字符串 -v ...
- MongoDB----提升
文档之间的联系 一对一:通过文档内嵌的形式体现一对一的关系 db.user.insert({name:"xiaoming",frind:{name:"xiahua&quo ...
- noip 2018.10.14 模拟赛 砍树
数学问题... 根据题意,有: 移项,整理,得: 记 于是 那么 可以看到,最多只会有2*个取值(显而易见) 于是对应的,可能产生效果的d也只会有个,于是我们把他们找出来,扔进一个数组里然后排序,去重 ...