目錄
- 前言
- 什么是 websocket
- websocket 通信原理和機制
- websocket 的特點
- 構建實時日志跟蹤的小例子
前言
websocket 是一種網絡傳輸協議。可在單個 TCP 連接上進行全雙工通信。基于此,websocket 使得客戶端與服務端的通信變得更加簡便和高效。
什么是 websocket
websocket 是獨立的、創建在 TCP 上的協議。該協議在 2008 年誕生,并在 2011 年成為國際標準。它的一個主要特點是——全雙工,即一旦建立連接,服務端或客戶端可以主動向對方推送消息。
在 websocket 出現之前,網站如果需要實現推送技術,都是采用輪詢的方式,即瀏覽器每隔一段時間就向服務器發出請求。這種模式的缺點在于,瀏覽器需要不斷向服務器發送請求,消耗很多的帶寬資源。比較新的 Comet 技術雖然也可以實現雙向通信,但依然需要反復發送請求,并且 Comet 中普遍采用的 HTTP 長連接也會消耗服務器資源。
基于以上的情況,HTML5 定義了 websocket 協議,能夠更好的節省服務器和帶寬資源。并且實現高效的實時通信。目前,所有的瀏覽器都支持它。
websocket 通信原理和機制
websocket 雖然是一種新的協議,但它不能夠脫離 http 單獨存在,當客戶端構建一個 websocket 實例,并且向服務端連接時,會首先發起一個 http 報文請求。告訴服務端需要將通信協議切換至 websocket。
如果服務端支持 websocket 協議,那么它會將通信協議切換至 websocket 并且返回響應報文。此時的返回狀態碼是 101,表示同意協議轉換請求,接下來便可以進行數據傳輸了。
websocket 之所以借助 HTTP 完成握手協議,是因為有良好的兼容性,默認端口是 80 和 443。握手階段不容易被防火墻屏蔽。
websocket 的特點
- 開銷小,服務器和客戶端交換數據時,協議包頭部包含較少的信息
- 實時性高,協議采用全雙工,相對于 http 請求客戶端發起請求,服務端才能響應的模式,延遲明顯更低
- 與 HTTP 有良好的兼容性,默認端口是 80 和 443。握手采用 HTTP 協議,不容易被防火墻屏蔽
- 支持文本和二進制數據傳輸
- 支持自定義拓展,用戶可以自己實現自定義的子協議
- 通過心跳機制保持服務端與客戶端的長連接
構建實時日志跟蹤的小例子
服務端開啟一個監聽日志腳本的服務,服務會限制允許訪問的路徑范圍(防止黑客利用程序漏洞,掃描整個服務器);服務器通過解析客戶端的請求,返回日志的消息內容給客戶端;服務器定時發送心跳檢測給客戶端,如果沒有收到客戶端的響應,則斷開連接
服務端核心程序代碼邏輯如下
with open(file_path) as f:
# 首次讀取指定行數(NUM_LINES)的日志文件,發送給客戶端
content = ''.join(deque(f, NUM_LINES))
content = conv.convert(content, full=False)
await websocket.send(content)
# 如果發現客戶端有 tail 請求,則進行 tail 日志追蹤
if tail:
# 首先創建發起這次請求的心跳時間
last_heartbeat = time.time()
while True:
# 每次 tail 服務端最新的日志記錄,返回給客戶端
content = f.read()
if content:
content = conv.convert(content, full=False)
await websocket.send(content)
else:
await asyncio.sleep(1)
# 檢測這次請求距上一次發起請求,是不是已經超過了最長心跳檢測時長,如果是,發起心跳檢測
if time.time() - last_heartbeat > HEARTBEAT_INTERVAL:
try:
await websocket.send('ping')
pong = await asyncio.wait_for(websocket.recv(), 5)
logger.info(f"pong:{pong}")
if pong != 'pong':
raise Exception()
except Exception:
raise Exception('Ping error')
else:
last_heartbeat = time.time()
else:
await websocket.close()
客戶端就非常簡單了,監聽服務端日志的文件,發現有新的日志產生則輸出日志或者直接將日志實時展示在前端頁面上。相應地,如果需要長期監聽,那么當服務端發送心跳檢測的信號過來,也需要回應響應的心跳反饋
客戶端核心代碼邏輯如下
async def consumer_handler(websocket: WebSocketClientProtocol) -> None:
async for message in websocket:
log_message(message)
if message == "ping":
await websocket.send("pong")
async def cousume(hostname: str, port: int, log_file: str, tail:bool=True) -> None:
websocket_resource_url = f"ws://{hostname}:{port}{log_file}"
if tail:
websocket_resource_url = f"{websocket_resource_url}?tail=1"
async with websockets.connect(websocket_resource_url) as websocket:
await consumer_handler(websocket)
def log_message(message: str) -> None:
logger.info(f"Message: {message}")
這里模擬一個日志生產文件
代碼邏輯如下
import os
from loguru import logger
class LoggerExtend(object):
# 存放目錄名稱
folder = '../logs'
def __init__(self, filename, folder=None):
self.folder = folder or self.folder
if not os.path.exists(self.folder):
os.mkdir(self.folder)
self.file = self.folder + '/' + filename
logger.add(self.file, rotation="100 MB")
@property
def get_logger(self):
return logger
if __name__ == '__main__':
logger = LoggerExtend(os.path.basename(__file__).replace(".py", ".log")).get_logger
import time
while True:
logger.info("你好aaa")
最后依次啟動日志生產程序→服務端程序→客戶端程序
日志生產文件啟動后,運行效果如下

服務端啟動程序運行,無運行日志產生
這時候啟動客戶端程序,運行效果如下

完整代碼請移步至 GitHub 查看
https://github.com/hacksman/l...
日志生產程序路徑:
common/logger_extend.py
服務端程序路徑:
websoctet_lab/log_server.py
客戶端程序路徑:
websoctet_lab/cousumer_log_view.py
以上就是python和websocket構建實時日志跟蹤器的步驟的詳細內容,更多關于python 構建實時日志跟蹤器的資料請關注腳本之家其它相關文章!
您可能感興趣的文章:- python實現自定義日志的具體方法
- Python 第三方日志框架loguru使用
- 解決python logging遇到的坑 日志重復打印問題
- python 實現多進程日志輪轉ConcurrentLogHandler
- python 實現logging動態變更輸出日志文件名
- python (logging) 日志按日期、大小回滾的操作
- 詳解python日志輸出使用配置文件格式
- python基于pexpect庫自動獲取日志信息
- Python日志打印里logging.getLogger源碼分析詳解
- python subprocess pipe 實時輸出日志的操作
- Python中logging日志的四個等級和使用
- 如何在Python項目中引入日志