Django3+websocket+paramiko实现web页⾯实时输出⼀、概述
但是,我们最终的效果是web页⾯上,能够实时输出结果,⽐如执⾏⼀个shell脚本。
以母鸡下蛋的例⼦,来演⽰⼀下,先来看效果:
⼆、代码实现
环境说明
操作系统:windows 10
python版本:3.7.9
操作系统:centos 7.6
ip地址:192.168.31.196
说明:windows10⽤来运⾏django项⽬,centos系统⽤来执⾏shell脚本。脚本路径为:/opt/test.sh,内容如下:
#!/bin/bash
for i in {1..10}
do
sleep0.1
echo母鸡⽣了$i个鸡蛋;
done
新建项⽬
新建项⽬:django3_websocket,应⽤名称:web
安装paramiko模块
pip3 install paramiko
编辑 settings.py
将Channels库添加到已安装的应⽤程序列表中。编辑 settings.py ⽂件,并将channels添加到INSTALLED_APPS设置中。
INSTALLED_APPS = [
# ...
'channels', # 【channels】(第1步)pip install -U channels 安装
# ...
]
创建默认路由(主WS路由)
Channels路由配置类似于Django URLconf,因为当通道服务器接收到HTTP请求时,它告诉通道运⾏什么代码。
将从⼀个空路由配置开始。在web⽬录下,创建⼀个⽂件 routing.py ,内容如下:
from django.urls import re_path,path
from . import consumers
websocket_urlpatterns = [
# 前端请求websocket连接
path('ws/result/', consumers.SyncConsumer),
]
设置执⾏路由对象(指定routing)
最后,将ASGI_APPLICATION设置为指向路由对象作为根应⽤程序,修改 settings.py ⽂件,最后⼀⾏添加:
ASGI_APPLICATION = 'uting.application'django前端模板
就是这样!⼀旦启⽤,通道就会将⾃⼰集成到Django中,并控制runserver命令。
启动channel layer
信道层是⼀种通信系统。它允许多个消费者实例彼此交谈,以及与Django的其他部分交谈。
通道层提供以下抽象:
通道是⼀个可以将邮件发送到的邮箱。每个频道都有⼀个名称。任何拥有频道名称的⼈都可以向频道发送消息。
⼀组是⼀组相关的通道。⼀个组有⼀个名称。任何具有组名称的⼈都可以按名称向组添加/删除频道,并向组中的所有频道发送消息。⽆法枚举特定组中的通道。
每个使⽤者实例都有⼀个⾃动⽣成的唯⼀通道名,因此可以通过通道层进⾏通信。
这⾥为了⽅便部署,直接使⽤内存作为后备存储的通道层。有条件的话,可以使⽤redis存储。
配置CHANNEL_LAYERS
修改 settings.py 最后⼀⾏增加配置
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer",
}
}
应⽤下创建 consumers.py(类似Django视图)
同步消费者很⽅便,因为他们可以调⽤常规的同步I / O函数,例如那些在不编写特殊代码的情况下访问Django模型的函数。但是,异步使⽤者可以提供更⾼级别的性能,因为他们在处理请求时不需要创建其他线程。
这⾥使⽤同步消费,因为我测试异步消费时,web页⾯并不能实时展⽰结果。只能使⽤同步模式才⾏。
在web⽬录下,创建⽂件consumers.py
import json
ic.websocket import AsyncWebsocketConsumer
import paramiko
ic.websocket import WebsocketConsumer, AsyncWebsocketConsumer
from asgiref.sync import async_to_sync
# 同步⽅式,仅作⽰例,不使⽤
class SyncConsumer(WebsocketConsumer):
def connect(self):
self.username = "xiao"# 临时固定⽤户名
print('WebSocket建⽴连接:', self.username)
# 直接从⽤户指定的通道名称构造通道组名称
self.channel_group_name = 'msg_%s' % self.username
# 加⼊通道层
# async_to_sync(…)包装器是必需的,因为ChatConsumer是同步WebsocketConsumer,但它调⽤的是异步通道层⽅法。(所有通道层⽅法都是异步的。) async_to_sync(self.up_add)(
self.channel_group_name,
self.channel_name
)
# 接受WebSocket连接。
self.accept()
async_to_sync(self.up_send)(
self.channel_group_name,
{
'type': 'get_message',
}
)
def disconnect(self, close_code):
print('WebSocket关闭连接')
# 离开通道
async_to_sync(self.up_discard)(
self.channel_group_name,
self.channel_name
)
# 从WebSocket中接收消息
def receive(self, text_data=None, bytes_data=None):
print('WebSocket接收消息:', text_data,type(text_data))
text_data_json = json.loads(text_data)
message = text_data_json['message']
# print("receive message",message,type(message))
# 发送消息到通道
async_to_sync(self.up_send)(
self.channel_group_name,
{
'type': 'get_message',
'message': message
}
)
# 从通道中接收消息
def get_message(self, event):
# print("event",event,type(event))
('message'):
message = event['message']
# 判断消息
if message == "close":
# 关闭websocket连接
self.disconnect(self.channel_group_name)
print("前端关闭websocket连接")
# 判断消息,执⾏脚本
if message == "laying_eggs":
# 执⾏的命令或者脚本
command = 'bash /opt/test.sh'
# 远程连接服务器
hostname = '192.168.31.196'
username = 'root'
password = 'root'
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
stdin, stdout, stderr = _command(command, get_pty=True) # result = ad()
# 循环发送消息给前端页⾯
while True:
nextline = adline().strip() # 读取脚本输出内容
# print(nextline.strip())
# 发送消息到客户端
self.send(
text_data=nextline
)
print("已发送消息:%s" % nextline)
# 判断消息为空时,退出循环
if not nextline:
break
ssh.close() # 关闭ssh连接
# 关闭websocket连接
self.disconnect(self.channel_group_name)
print("后端关闭websocket连接")
View Code
注意:修改⾥⾯的服务器,⽤户名,密码,脚本名称。
应⽤下创建 routing.py (类似Django路由)
在web⽬录下,创建⽂件routing.py
添加Channels⼦路由的配置
from django.urls import re_path,path
from . import consumers
websocket_urlpatterns = [
# 前端请求websocket连接
path('ws/result/', consumers.SyncConsumer),
]
前端页⾯连接WebSocket
在templates⽬录下,新建⽂件index.html,内容如下:
<!DOCTYPE html >
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>测试demo</title>
<!-- 最新版本的 Bootstrap 核⼼ CSS ⽂件 -->
<link rel="stylesheet" href="cdn.jsdelivr/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <script src="cdn.bootcdn/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<body>
<div class="container">
<div ></div>
<button type="button" id="execute_script"class="btn btn-success">查看⽇志</button>
<h4>⽇志内容:</h4>
<div id="content_logs">
<div id="messagecontainer" >
</div>
</div>
</div>
</body>
<script type="text/javascript">
// 点击按钮
$('#execute_script').click(function () {
// 新建websocket连接
const chatSocket = new WebSocket(
'ws://'
+ window.location.host
+ '/ws/result/'
);
// 连接建⽴成功事件
console.log('WebSocket open');
//发送字符: laying_eggs到服务端
chatSocket.send(JSON.stringify({
'message': 'laying_eggs'
}));
console.log("发送完字符串laying_eggs");
};
/
/ 接收消息事件
{#if (e.data.length > 0) {#}
//打印服务端返回的数据
console.log('message: ' + e.data);
// 转换为字符串,防⽌卡死testestt
$('#messagecontainer').append(String(e.data) + '<br/>');
//滚动条⾃动到最底部
$("#content_logs").scrollTop($("#content_logs")[0].scrollHeight); {# }#}
};
// 关闭连接事件
console.log("connection closed (" + e.code + ")");
chatSocket.send(JSON.stringify({
'message': 'close'
}));
}
});
</script>
</html>
View Code
修改urls.py,增加⾸页
ib import admin
from django.urls import path
from web import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
]
修改web⽬录下的views.py,内容如下:
from django.shortcuts import render
# Create your views here.
def index(request):
return render(request,'index.html')
使⽤Pycharm直接启动项⽬,或者使⽤命令⾏启动
python manage.py runserver
访问⾸页
127.0.0.1:8000/index/
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论