django搭建一个小型的服务器运维网站-基于websocket的实时日志实现

news/2024/4/28 13:44:17/文章来源:https://blog.csdn.net/weixin_34292924/article/details/88815469

目录

  1. 项目介绍和源码;
  2. 拿来即用的bootstrap模板;
  3. 服务器SSH服务配置与python中paramiko的使用;
  4. 用户登陆与session;
  5. 最简单的实践之修改服务器时间;
  6. 查看和修改服务器配置与数据库的路由;
  7. 基于websocket的实时日志实现;
  8. 查看服务器中的日志与前端的datatable的利用;
  9. 重启服务器进程。

前言

  实时日志的查看需要用到websocket,这篇文章会说说如何利用websocket实现一个实时日志查看页面。页面如图1所示。在这个功能里,网页的页面是通过server/views.py中的函数渲染的,但是服务器是单独用python写的websocket服务器,客户端浏览器单独进行链接。

图1 实时日志

Websocket原理

  文章WebSocket 通信过程与实现已经把websocket的原理和和使用方法介绍的很详细了。项目尝试过利用HTTP去实现一个实时日志的功能,但是由于HTTP是被动的,客户端要不停的发起HTTP请求到服务端,然后服务端从存储日志临时内容的中间件(redis等)中拿给客户端刚更新的日志,如图2的实现逻辑,这样不仅浪费资源而且实现起来也挺费劲。

图2

  WebSocket便可以做到服务器向客户端主动推送数据,这样服务器一旦更新了日志,就可以主动推送日志到客户端上,浏览器的客户端通过一些轻量封装的socket函数实现创建、传输、关闭等功能。WebSocket是HTML5中的协议,现在一般主流的浏览器都会支持该协议。
  WebSocket协议借用了HTTP的协议来完成一部分和服务端的握手,握手之后客户端和服务端就可以相互传输数据了(全双工通信)。客户端发送的握手协议如下:

GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13

  与HTTP报文不一样的是加入的websocket独有的部分:

客户端发起的是websocket连接
Upgrade: websocket   
Connection: Upgradewebsocket连接安全和版本
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13

  websocket客户端(浏览器)和服务端交互过程如图3,由于websocket是基于TCP的,这里的握手只是应用层的关系,传输层已经保证了三次握手和四次挥手,每个客户端都可以主动暂停传输或者关闭传输。

图3 websocket连接示意

实时日志

  服务器一旦产生日志,就会传输给需要接收的客户端滚动显示,这样的逻辑利用websocket再好不过。这里给出图1所示的页面的html代码如下:

{% extends "./base.html" %}{% block othercss %}
<link href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css" rel="stylesheet" />
{% endblock %}
{% block title %}{{ title }}{% endblock %}
{% block log %}{{ title }}{% endblock %}
{% block username %}{{ username }}{% endblock %}{% block mainbody %}
<section class="wrapper site-min-height"><h3><i class="fa fa-angle-right"></i>实时日志 <i class="fa fa-desktop"></i></h3><div class="row mt"><div class="form-panel"><div class="col-lg-12 row mt"><div class="col-sm-6"><h4 class="mb" style="float:left;dispaly:block;">实时日志</h4></div><div class="col-sm-6"><button type="button" class="btn btn-theme02" style="float:right" onclick="cleartable()"> 清空日志</button><input type="checkbox" onchange="isCheck(this)" style="float:left" data-toggle="switch"></div></div><div><table id="logtable" class="table-striped dataTable table-advance table-hover" style="word-break:break-all;"><thead><tr><th style="width:25%;">时间</th><th style="width:15%;">名字</th><th>内容</th></tr></thead><tbody id="log"></tbody></table></div></div></div>
</section>
{% endblock %}

  它的javascipt代码主要是两部分逻辑,一部分是websocket相关的函数。另一部分是动态响应表格Datatable的控制代码,关于Datatable的使用会在文章查看服务器中的日志与前端的datatable的利用文章中介绍:

{% block scripts %}
<script>
$(document).ready(function (){// 动态响应表格的控制$('#logtable').DataTable({"scrollY": "670px",   //让表格上下滚动,右边会出现滚动滑条"scrollCollapse": true,'columnDefs':[{'targets' : [1,2],    //除时间列以外都不排序'orderable' : false}],"order": [[0 , "desc" ]],"paging": false,      // 禁止分页"bInfo": false,       //页脚信息"oLanguage": {"sZeroRecords": "打开按钮可以开始接收日志,日志默认为时间降序排列!","sSearch": "日志过滤:",},});
});
// 客户端websocket
var socket;
function init(){var host = "ws://127.0.0.1:8889/";try{// 建立一个websocketsocket = new WebSocket(host);// 打开websocketsocket.onopen = function(){console.log('Connected');server_tag = $('.logo').text();socket.send(server_tag);};// 监听接收服务端的消息socket.onmessage = function(msg){// 如果收到服务端的Bye,关闭客户端的if(eval(msg.data) == 'Bye'){socket.close();socket = null;return ;}var table = $('#logtable').DataTable();var log = eval(msg.data);for(i=0; i<log.length; ++i){var logtime = log[i][0];var logname = log[i][1];var logcontent = log[i][2];table.row.add([logtime,logname,logcontent]).draw(true);}}// websocket关闭socket.onclose = function(){console.log('Lose Connection!');}}catch(ex){console(ex);}
}
function isCheck(obj){if($(obj).prop("checked")){init();}else{// 客户端发起关闭连接请求socket.send('quit');// 清空表格var table = $('#logtable').DataTable().clear().draw();}
}
function cleartable(){// 清空表格var table = $('#logtable').DataTable().clear().draw();
}
</script>
<!--custom switch-->
<script src="/templates/servermaterial/assets/js/bootstrap-switch.js"></script>
<!--custom tagsinput-->
<script src="/templates/servermaterial/assets/js/jquery.tagsinput.js"></script>
<!--custom checkbox & radio-->
<script src="/templates/servermaterial/assets/js/form-component.js"></script>
<script src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js"></script> 
{% endblock %}

  分别添加一个url和view函数用来显示这个页面,分别写在server/urls.py和server/views.py中,最后显示的界面就是图1的页面了:

  • url转到views中的realtimelog渲染函数
url(r'^realtimelog', views.realtimelog),
  • server/views.py的realtimelog
# -*- coding: utf-8 -*-
from __future__ import unicode_literalsfrom django.contrib.auth import logout
from django.shortcuts import render_to_response
from django.http import JsonResponse
from django.contrib.auth.decorators import login_required
import json
import time@login_required(login_url='/loginpage')
def realtimelog(request):username = request.session.get('username')pagedict = {'title': htmltitle, 'username': username}return render_to_response("servermaterial/realtimelog.html", pagedict)

客户端界面

  • Datatable使用

  使用Datatable的好处是,这个现成的动态响应表格几乎已经集成好了表格中需要的所有功能,我们把Datatable改造一下就能够支持实时的滚动显示日志新内容这个需求,Datatable的使用可以参考这篇Datetable 中文网和这篇CSDN博客,这里说下如何改造一下现有的Datatable变成页面需求的样子。
  注意到html代码中的table属性里面有个style="word-break:break-all;",其目的是怕日志内容过多,超出表格,这里可以实现换行。

<table id="logtable" class="table-striped dataTable table-advance table-hover" style="word-break:break-all;">

  其javascript代码中,字段含义已经注释。

$(document).ready(function (){// 动态响应表格的控制$('#logtable').DataTable({"scrollY": "670px",       //让表格上下滚动,右边会出现滚动滑条,限定表格的高度为670px,如图4蓝框"scrollCollapse": true,'columnDefs':[{'targets' : [1,2],    //除时间列以外都不排序'orderable' : false   //1列、2列不排序(名字、内容列)}],"order": [[0 , "desc" ]], //按日志时间降序,如图4红框"paging": false,          //禁止分页"bInfo": false,           //页脚信息"oLanguage": {            "sZeroRecords": "打开按钮可以开始接收日志,日志默认为时间降序排列!",    //表格为空时的默认显示信息,如图5绿框"sSearch": "日志过滤:",                                             //右上角的搜索,图5红框},});
});

图4

图5

  • websocket客户端编写

  其中用到了三个函数,分别是socket.onopensocket.onmessagesocket.onclose,分别用于打开、传输和关闭,这里把他们写到了日志开关(图6红框所示)上,打开开关的时候执行init()函数创建一个websocket进行通信,关闭的时候给服务端发送一个quit,并清空表格中的数据。

var socket;
function init(){var host = "ws://127.0.0.1:8889/";try{// 建立一个websocketsocket = new WebSocket(host);// 打开websocketsocket.onopen = function(){console.log('Connected');server_tag = $('.logo').text();socket.send(server_tag);};// 监听接收服务端的消息socket.onmessage = function(msg){// 如果收到服务端的Bye,关闭客户端的if(eval(msg.data) == 'Bye'){socket.close();socket = null;return ;}var table = $('#logtable').DataTable();var log = eval(msg.data);for(i=0; i<log.length; ++i){var logtime = log[i][0];var logname = log[i][1];var logcontent = log[i][2];table.row.add([logtime,logname,logcontent]).draw(true);}}// websocket关闭socket.onclose = function(){console.log('Lose Connection!');}}catch(ex){console(ex);}
}
function isCheck(obj){if($(obj).prop("checked")){init();}else{// 客户端发起关闭连接请求socket.send('quit');// 清空表格var table = $('#logtable').DataTable().clear().draw();}
}

图6 日志开关

  上面基本上已经把客户端写好了,下面来写下服务端。

服务器

  服务器首先是接受浏览器的握手请求,然后解析数据。这里的服务端用多线程实现,服务端文件放在和funtions.py同级的目录下,即WebTool/WebTool,如图7红框。

图7 websocket服务端

  通过websocket,每一个客户端的请求都会被服务端线程处理,每一个线程中都会利用paramiko在服务器相应的log目录下tail -f日志获得刷新。

图8 基于websocket的实时日志

  为了服务器有日志的输出,我们在Linux服务器的home/logs目录下写一个不断生成日志新内容伪造日志生成源的shell脚本autogenlog.sh,生成的日志的格式是:[时间][名字],{日志内容},控制其每两秒钟在log.txt中追加一条日志记录。形如:
  [2018-05-06 23:05:28][Error],{这里是一段测试的内容,服务器的日志内容通过websocket主动推送到浏览器上}

#!/bin/sh
while true
do# 获取系统的时间logDate=$(date "+%Y-%m-%d %H:%M:%S")echo [$logDate][Error],{这里是一段测试的内容,服务器的日志内容通过websocket主动推送到浏览器上} >> log.txtsleep 2
done

  服务器代码如下,recv_data函数用于解析浏览器的信息,send_data用于发送给浏览器的信息,handshake函数用来和浏览器之间握手建立连接。在函数getlog里面,command变量存放执行的命令tail -f /home/logs/log.txt,而这个log.txt中增加的日志记录是通过上面给出的shell脚本追加的。正则表达式"\[(.*?)\]\[(.*?)\],({.*})"用来提取日志的时间,名字和内容,经由send_data传递给浏览器滚动显示,关于paramiko的使用请移步至文章服务器SSH服务配置与python中paramiko的使用。最后服务器的输出为图9所示。

图9 服务器输出

# -*- coding: utf-8 -*-
import struct
import base64
import hashlib
import socket
import threading
import re
import sys
import jsonreload(sys)
sys.setdefaultencoding('utf-8')# 服务器解析浏览器发送的信息
def recv_data(conn):try:all_data = conn.recv(1024)if not len(all_data):return Falseexcept:passelse:code_len = ord(all_data[1]) & 127if code_len == 126:masks = all_data[4:8]data = all_data[8:]elif code_len == 127:masks = all_data[10:14]data = all_data[14:]else:masks = all_data[2:6]data = all_data[6:]raw_str = ""i = 0for d in data:raw_str += chr(ord(d) ^ ord(masks[i % 4]))i += 1return raw_str# 服务器处理发送给浏览器的信息
def send_data(conn, data):if data:data = str(data)else:return Falsetoken = "\x81"length = len(data)if length < 126:token += struct.pack("B", length)elif length <= 0xFFFF:token += struct.pack("!BH", 126, length)else:token += struct.pack("!BQ", 127, length)# struct为Python中处理二进制数的模块,二进制流为C,或网络流的形式。data = '%s%s' % (token, data)conn.send(data)return True# 握手
def handshake(conn, address, thread_name):headers = {}shake = conn.recv(1024)if not len(shake):return Falseprint ('%s : Socket start handshaken with %s:%s' % (thread_name, address[0], address[1]))header, data = shake.split('\r\n\r\n', 1)for line in header.split('\r\n')[1:]:key, value = line.split(': ', 1)headers[key] = valueif 'Sec-WebSocket-Key' not in headers:print ('%s : This socket is not websocket, client close.' % thread_name)conn.close()return FalseMAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'HANDSHAKE_STRING = "HTTP/1.1 101 Switching Protocols\r\n" \"Upgrade:WebSocket\r\n" \"Connection: Upgrade\r\n" \"Sec-WebSocket-Accept: {1}\r\n" \"WebSocket-Location: ws://{2}/chat\r\n" \"WebSocket-Protocol:chat\r\n\r\n"sec_key = headers['Sec-WebSocket-Key']res_key = base64.b64encode(hashlib.sha1(sec_key + MAGIC_STRING).digest())str_handshake = HANDSHAKE_STRING.replace('{1}', res_key).replace('{2}', headers['Origin']).replace('{3}',headers['Host'])conn.send(str_handshake)print ('%s : Socket handshaken with %s:%s success' % (thread_name, address[0], address[1]))print 'Start transmitting data...'print '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -'return Truedef getlog(conn, address, thread_name):handshake(conn, address, thread_name)  # 握手server_name = recv_data(conn)print 'connect to ' + unicode(server_name)conn.setblocking(0)  # 设置socket为非阻塞from functions import login_server_by_pwdssh = login_server_by_pwd()# open channel pipelinetransport = ssh.get_transport()channel = transport.open_session()channel.get_pty()# execute commandcommand = 'tail -f /home/logs/log.txt'# out command into pipelinechannel.exec_command(command)while True:try:clientdata = recv_data(conn)if clientdata is not None and 'quit' in clientdata:print ('%s : Socket close with %s:%s' % (thread_name, address[0], address[1]))send_data(conn, json.dumps('Bye'))ssh.close()channel.close()conn.close()breakwhile channel.recv_ready():recvfromssh = channel.recv(16371)log = re.findall("\[(.*?)\]\[(.*?)\],({.*})", recvfromssh)if len(log):# log_time, log_name, log_content = log[0][0], log[0][1], log[0][2]# print log_time, log_name, log_contentsend_data(conn, json.dumps(log))if channel.exit_status_ready():breakexcept:print ('%s : Socket close with %s:%s' % (thread_name, address[0], address[1]))ssh.close()channel.close()conn.close()channel.close()ssh.close()def wbservice():sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.bind(("0.0.0.0", 8889))sock.listen(100)index = 1print ('Websocket server start, wait for connect!')print '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -'while True:connection, address = sock.accept()thread_name = 'thread_%s' % indexprint ('%s : Connection from %s:%s' % (thread_name, address[0], address[1]))t = threading.Thread(target=getlog, args=(connection, address, thread_name))t.start()index += 1if __name__ == '__main__':wbservice()

  其实这样的服务器实现方法存在很多的问题,因为每一个进程都会在服务器中开一个tail -f的进程来处理实时日志,这里也没有用线程池处理,并且多线程并不是python中实现socket最好的方式,因为python中的多线程比较消耗资源,一般可以用协程或者epoll去解决(python中应尽量避免使用select,因为上限句柄1024很容易用完,上限改起来很麻烦),关于协程可以移步至文章协程及Python中的协程。因为这个工具的并发量很小,没多少人用,就没有对其优化了。

结语

  本文简单的介绍了websocket的原理和基于websocket实时日志的实现,附带说了下前端怎么把动态响应表格改造成实时日志的滚动效果。希望对有需要的童鞋有帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_786700.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分享21个超棒的单页面网站设计

单页面的网站设计对于结构简单的网站来说无疑是一个最合理的选择&#xff0c;使用单页面网站&#xff0c;不需要维护过多的页面&#xff0c;复杂的CSS文件结构&#xff0c;它们能够很直观的表单网站需要表达的基调。 在以前的文章中&#xff0c;我们介绍过制作单页面网站的插件…

flash 结构_Flash网站的最佳结构

flash 结构Adobe’s (formerly Macromedia’s) Flash application is, according to them, "The industry’s most advanced authoring environment for creating interactive web sites and digital experiences." However, many users have trouble structuring th…

源码值多少钱_您的网站值多少钱?

源码值多少钱Every day, we hear of yet another enormous company that’s paid an even more unbelievable sum for an online entity that — it often seems from where you and I are sitting — doesn’t actually generate much of an income. In the last few years, Y…

终极网站评估指南

Google claims an amazing 10 million pages for the search term how much is my web site worth. If this is the question you’re asking, you’re not alone! This article will let you in on some valuation secrets to help you judge with accuracy the value of your…

国外自由职业者网站_6个适合自由职业者的最佳众包网站

国外自由职业者网站Crowdsourcing is one of those wonderful concepts that only exists because of the internet. A decade ago it would have been unthinkable that a small company in Brisbane could have tapped into the huge pool of brilliant computer programmers…

网站架构演变_8个观看英语演变的网站

网站架构演变In one of my favorite television shows, “Futurama,” there is a running gag that in the future, the word “ask” has been replaced by the more colloquial “aks” (pronounced axe). It might be a long time before the accepted spellings and pronu…

adobe心理测试_Adobe Meermeer将改变您测试网站的方式

adobe心理测试Each year at their annual MAX event, Adobe shows off some of their most compelling projects during the “Sneaks” keynote. At the 2007 conference, the most talked about sneak was “Thermo,” recently made official as Flash Catalyst. This year,…

谷歌个性化地图瓦片_Google测试“首选网站”个性化

谷歌个性化地图瓦片According to Google Operating System, Google is testing a new search customization option for select users called Google Preferred Sites. If you have access to the service, you’ll find the option on your preferences page when logged in (…

saas价格敏感性分析_SaaS Yahoo! 网站分析超越Google

saas价格敏感性分析Google Analytics is no longer the only free SaaS analytics that is fit for enterprise level use. Yahoo!’s Web Analytics service surpassed Google in several key categories according to a 470-page web analytics report released today by CMS…

删除网站版权网站打不开_版权网站的含义

删除网站版权网站打不开This article was written in 2009 and remains one of our most popular posts. If you’re keen to learn more about copyright, you may find this recent article on copycat startups of great interest. 本文写于2009年&#xff0c;至今仍是我们最…

网站内容百度不收录?2013年最新策略

这个是绿萝有N多方式方法以及经验式的文章都有说到过不收录的问题&#xff0c;总结起来无怪乎是服务器问题&#xff0c;网站内容问题&#xff0c;链接问题。其中服务器问题要找空间商解决&#xff0c;确保服务器要稳定&#xff0c;速度快&#xff0c;不能屏蔽搜索引擎蜘蛛。不放…

从 MAX 网站中获取模型,一秒开始你的深度学习应用

雷锋网按&#xff1a;本文为 AI 研习社编译的技术博客&#xff0c;原标题 Ready-to-Use Deep-Learning Models&#xff0c;作者为 Patrick Titzler。 翻译 | 老周 整理 | MY 您是否想过对图像进行分类、识别图像中的人脸或位置、处理自然语言或文本&#xff0c;或者根据应…

网站优化怎么获取到搜索数据更新动态?

搜索数据更新&#xff0c;我这里说的不是搜索引擎新算法上线导致的数据更新&#xff0c;是说那种日常性的数据刷新。所以&#xff0c;是网站优化人员必须经常观察搜索引擎的数据有没有刷新。观察这个有什么作用呢。通过观察搜索引擎的数据刷新&#xff0c;你就能够知道搜索引擎…

网站服务器 南阳,河南南阳DNS服务器地址

河南南阳DNS服务器地址 内容精选换一换普通的域名解析只为用户返回解析记录&#xff0c;不会考虑访问者的来源&#xff0c;这样所有的访问者都被解析到相同的IP地址上&#xff0c;容易出现由跨运营商访问引起网络延迟。运营商线路解析是云解析服务提供的按运营商维度来区分访问…

七个鲜为人知的搜索网站_12个鲜为人知CSS事实(续集)

七个鲜为人知的搜索网站Over a year ago I published the original 12 Little-known CSS Facts and, to this day, it has been one of SitePoint’s most popular articles ever. Since that post was published, I’ve been collecting more little CSS tips and tidbits for…

大型网站架构之架构模式

转自微信公众号&#xff1a;Java后端技术 原文&#xff1a;大型网站架构之架构模式 作者&#xff1a; Justin 今天讲下架构的模式&#xff0c;什么是模式呢&#xff1f;每一个模式描述了一个再我们周围不断重复发生的问题及问题解决方案的核心&#xff0c;这样你就能一次次重用…

mongodb 社交网站_使用PHP,MongoDB和jQuery进行社交网络样式发布-第1部分

mongodb 社交网站Post mechanisms similar to Facebook are nowadays very common within any application. The concept of Post-Like-Comment is familiar to everyone who ever used a social network. In this article, we will learn how to create a similar working mod…

mongodb 社交网站_使用PHP,MongoDB和jQuery进行社交网络样式发布-第2部分

mongodb 社交网站In the previous part of the series, we explained the database architecture, post stream design and application flow required for developing our post mechanism wherein the user will be able to post a status, like/unlike other peoples statuse…

wordpress插件_如何使用Weglot插件翻译WordPress网站

wordpress插件This article on how to translate WordPress websites was originally published by Torque Magazine, and is reproduced here with permission. 这篇有关如何翻译WordPress网站的文章最初由Torque Magazine出版&#xff0c;经许可转载于此。 Translating your…