分散式監控系統的核心在於資料函式庫設計和程式間通訊。本文介紹的系統採用 SQLite3 資料函式庫,並利用系統參數列和主機特定參數列實作彈性的引數管理。系統參數列儲存全域設定,而主機特定參數列允許針對個別主機覆寫預設值,達成客製化組態。系統採用 XML-RPC 作為程式間通訊協定,控制器透過 XML-RPC 向監控代理傳送指令,並接收代理回傳的監控資料。文章提供了 XML-RPC 訊息結構範例和 Python 程式碼片段,展示客戶端和伺服器端的實作方式。為了提高系統的擴充套件性,採用 CherryPy 框架構建 XML-RPC 服務,以支援更多連線和併發請求。最後,文章提供了 SQLite3 資料函式庫初始化的 SQL 指令,方便開發者快速搭建資料函式庫環境,並搭配 Python 程式碼示範資料函式庫的存取操作。
管理與監控子系統的資料函式庫設計與通訊機制
資料函式庫結構設計
在管理與監控子系統中,資料函式庫的設計至關重要。為了實作系統引數和主機特定引數的管理,採用了兩張表格來儲存相關資訊:系統參數列和主機特定參數列。
系統參數列
系統參數列儲存了系統級別的引數設定,採用鍵值對(key-value pair)的形式進行儲存。這種設計提供了極大的靈活性,可以根據應用程式的需求動態地新增自定義引數。
| 欄位名稱 | 資料型別 | 描述 |
|---|---|---|
| id | 整數 | 唯一識別符 |
| name | 文字 | 系統引數設定的唯一名稱 |
| value | 文字 | 鍵的預設值,可以作為系統級別的設定,或針對特定主機進行覆寫。所有值都以文字形式儲存,需要在執行時轉換為適當的資料型別。 |
主機特定參數列
主機特定參數列則參考了系統參數列,並允許對特定主機的引數進行覆寫。這種設計實作了雙層繼承機制,與感測器閾值條目的設計類別似。
| 欄位名稱 | 資料型別 | 描述 |
|---|---|---|
| id | 整數 | 唯一識別符 |
| param_id | 整數 | 參數列中記錄的ID |
| host_id | 整數 | 主機表記錄的ID,允許為每個特定主機套用特定的設定 |
| value | 文字 | 主機特定的引數值 |
ER 圖表示
為了更好地理解資料函式庫中各表格之間的關係,繪製了實體關係圖(ER 圖)。ER 圖清晰地展示了表格之間的關聯,使得撰寫 SQL 查詢陳述式更加容易。
通訊機制
該監控系統本質上是一個分散式計算系統。控制器程式(排程器元件)負責向處理節點(監控代理)傳送作業請求,並最終將資訊回饋給資料處理元件(監控伺服器)。
XML-RPC 通訊協定
為了實作程式間的通訊,選擇了 XML-RPC 方法。XML-RPC 是一種遠端程式呼叫的方法,透過將訊息編碼為 XML 並使用 HTTP 作為傳輸機制,實作了不同程式之間的資訊交換。
XML-RPC 訊息結構
XML-RPC 呼叫訊息具有相對簡單的結構,僅允許一種資料序列化方法。以下是一個 XML-RPC 程式呼叫訊息的範例:
<methodCall>
<methodName>cmd_get_sensor_code</methodName>
<params>
<param>
<value><string>disk</string></value>
</param>
</params>
</methodCall>
XML-RPC 協定支援多種資料型別,包括陣列、base64 編碼的資料流、布林值、日期時間物件、雙精確度浮點數、整數、字串、結構體和空物件。
回應訊息範例
以下是一個對感測器更新請求的回應範例,回應物件是一個以 base64 編碼的二進位資料物件:
<methodResponse>
<params>
<param>
<value><base64>
QlpoOTFBWSZTWbXv/NUAAad/hP6YQIB+9v/vOw5fCv/v3+4AAQAIQAIdVWSrWEoknonqmYpo0wmT
TEANBoPUGI0NB6nqMmnqCVNBMSaGp6Jp6I9IAAGgAZNABoADhppghkNNMjJhANNAGE0aZMACBoJJ
Knp6JPJNlD0T1PU0bUADQAaGQABoDT1Mfhn03axWSSsQghGnU545FVU08YoQcAwgFBiiK7+M3lmm
9b2lcEqqqb5TUIVrK2vGUFTK6AEqDJIMQwCK7At2EVF6xHAj3e5I33xZm8d8+FQEApNQvgxJEflD
nwilZzqaPMelGNtGl27o7Ss51Fl0ebZuhJZOQ5aVjg6gZIyrzq6MNttwJpbNuJHGMzNiJQ4RMSkQ
23GVRwYVCyti8yqZ1ppjGGBr6lG4QY328gCTLALIZNlYNq01p8U48MsCHPFLznOVKisKYsE7nubL
K1tdUnEQ4XKbibYRsVQSsDnwYtshI+I1gkr2DWoihkgeB4fejEhqPRLzISHihEn0F5Ge4sqCpMgt
8IAyfCEqEyEetRVc/QnBQOrV6dA18m9GHtJOGkikwdjGTpgGdAMTw5FqKHHMHT1ucTvZcRWOurze
q2ndOEjXSliyjqWyXlD5/aWSwKy5UhjUKjbGhyRbVUHIEZQSekThXKgZNUq1Mi7eXZddjBdKRigi
F+RgMBo1LwT5iqJoUSZtCokLR/T5dLx2ySEQZA+ZaARBHaPwlDRNtiF25NTtoLgTsWpDJQRoKwSI
</base64></value>
</param>
</params>
</methodResponse>
程式碼實作與解析
在實作 XML-RPC 通訊時,需要使用特定的程式函式庫來處理訊息的編碼和解碼。以下是一個簡單的 Python 範例,展示如何使用 xmlrpc.server 模組建立一個 XML-RPC 伺服器:
from xmlrpc.server import SimpleXMLRPCServer
def cmd_get_sensor_code(sensor_type):
# 根據 sensor_type 傳回對應的感測器程式碼
if sensor_type == 'disk':
return 'disk_sensor_code'
else:
return 'unknown_sensor_code'
server = SimpleXMLRPCServer(('localhost', 8000))
server.register_function(cmd_get_sensor_code)
server.serve_forever()
內容解密:
此範例展示瞭如何建立一個 XML-RPC 伺服器,並註冊一個名為 cmd_get_sensor_code 的函式。該函式根據輸入的 sensor_type 傳回對應的感測器程式碼。在客戶端,可以使用 xmlrpc.client 模組來呼叫這個遠端程式。
from xmlrpc.client import ServerProxy
client = ServerProxy('http://localhost:8000')
print(client.cmd_get_sensor_code('disk')) # 輸出: disk_sensor_code
內容解密:
此客戶端程式碼建立了一個到 XML-RPC 伺服器的連線,並呼叫了 cmd_get_sensor_code 函式,傳入 'disk' 作為引數。伺服器接收到請求後,執行對應的函式並傳回結果,客戶端接收並列印出結果。
管理與監控子系統(Management and Monitoring SubSystem)
XML-RPC 協定支援
Python 內建支援 XML-RPC 協定,讓開發者能夠輕鬆編寫客戶端與伺服器端應用程式,而無需額外安裝套件。客戶端函式庫 xmlrpclib 提供了基本功能來存取服務並建立 XML-RPC 請求訊息。
使用 xmlrpclib 建立客戶端
首先,需要建立一個代理物件(proxy object),然後使用它來呼叫遠端程式。大部分情況下,開發者需要事先知道遠端程式的名稱,因為 XML-RPC 不使用像 WSDL 這樣的正式服務定義語言。
import xmlrpclib
# 建立代理物件
proxy = xmlrpclib.ServerProxy('http://192.168.1.65:8081/xmlrpc/')
# 呼叫遠端程式
url = proxy.cmd_get_new_monitor_url('myhost')
print(url)
使用 SimpleXMLRPCServer 建立伺服器
Python 也提供了基本的 XML-RPC 伺服器,讓開發者能夠將函式註冊為遠端程式。建立 XML-RPC 伺服器的流程非常簡單:匯入伺服器類別、建立伺服器物件、註冊函式、啟動伺服器。
from SimpleXMLRPCServer import SimpleXMLRPCServer as s
# 定義一個簡單的函式
def hello(name):
return "Hello, %s!" % name
# 建立伺服器物件
server = s(('localhost', 8080))
# 註冊函式
server.register_function(hello, 'hello')
# 啟動伺服器
server.serve_forever()
使用 xmlrpclib 連線伺服器
客戶端可以使用 xmlrpclib 連線並呼叫伺服器上的遠端程式。
import xmlrpclib
# 建立代理物件
proxy = xmlrpclib.ServerProxy('http://localhost:8080/')
# 呼叫遠端程式
print(proxy.hello('John'))
封裝基本資料型別
xmlrpclib 的 dumps 方法可以用來將基本 Python 資料型別封裝成 XML 結構,以便傳送給遠端伺服器。
import xmlrpclib
# 封裝資料
print(xmlrpclib.dumps(('temperature', 20)))
CherryPy:一個可擴充套件的 Web 框架
雖然內建的 XML-RPC 伺服器非常簡單易用,但對於需要支援大量分散式系統的情況,CherryPy 提供了一個更具擴充套件性的解決方案。CherryPy 是一個 Python Web 應用框架,支援多執行緒和多個 socket 連線,使得它非常適合用於建立可擴充套件的 XML-RPC 服務。
使用 CherryPy 建立 XML-RPC 服務
使用 CherryPy 建立 XML-RPC 服務非常簡單,只需要繼承 _cptools.XMLRPCController 類別,並使用 @cherrypy.expose 裝飾器來註冊函式。
import cherrypy
from cherrypy import _cptools
class Root(_cptools.XMLRPCController):
@cherrypy.expose
def hello(self, name):
return "Hello, %s" % name
cherrypy.quickstart(Root(), '/')
SQLite3:輕量級資料函式庫引擎
伺服器程式使用 SQLite3 作為輕量級資料函式庫引擎來儲存探針讀數和客戶端組態資料。SQLite3 不需要組態,可以直接連線到資料函式庫檔案。
初始化資料函式庫檔案
可以使用 Python 應用程式或 SQLite3 命令列工具來初始化資料函式庫檔案。下面是初始化資料函式庫的 SQL 陳述式範例:
-- 初始化資料函式庫的 SQL 陳述式範例
CREATE TABLE clients (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE probe_readings (
id INTEGER PRIMARY KEY,
client_id INTEGER NOT NULL,
reading REAL NOT NULL,
FOREIGN KEY(client_id) REFERENCES clients(id)
);
在 Python 中使用 SQLite3
Python 內建支援 SQLite3,可以直接匯入 sqlite3 模組來使用。
import sqlite3
# 連線到資料函式庫檔案
conn = sqlite3.connect('example.db')
# 建立遊標物件
cur = conn.cursor()
# 執行 SQL 陳述式
cur.execute('SELECT * FROM clients')
# 取得查詢結果
results = cur.fetchall()
# 關閉連線
conn.close()
管理與監控子系統的資料函式庫初始化
本章節主要介紹管理與監控子系統的資料函式庫設計與初始化過程。該資料函式庫採用 SQLite3 作為儲存引擎,並透過一系列的 SQL 命令來建立所需的資料表結構。
資料函式庫結構設計
該資料函式庫包含多個資料表,分別用於儲存不同型別的監控資料和系統組態資訊。主要的資料表包括:
- SENSOR:用於儲存所有可用的感測器列表
- PROBE:定義了與感測器相關的引數列表及預設的門檻值
- HOST:儲存所有監控代理的資訊
- HOSTPROBE:將可用的探針對映到主機,並可覆寫預設的門檻值
- TICKETQUEUE:儲存所有待處理和已傳送的票據
- PROBEREADING:儲存從監控代理取得的所有讀數
- PROBINGSCHEDULE:定義探針的執行間隔
- SYSTEMPARAMS:定義系統組態引數
- HOSTPARAMS:將系統引數指派給主機,並允許覆寫預設值
初始化 SQL 命令詳解
以下是建立資料函式庫結構的 SQL 命令範例:
-- 建立 SENSOR 資料表
DROP TABLE IF EXISTS sensor;
CREATE TABLE sensor (
id INTEGER PRIMARY KEY,
name TEXT
);
-- 建立 PROBE 資料表
DROP TABLE IF EXISTS probe;
CREATE TABLE probe (
id INTEGER PRIMARY KEY,
sensor_id INTEGER,
name TEXT,
parameter TEXT,
warning FLOAT,
error FLOAT,
FOREIGN KEY (sensor_id) REFERENCES sensor(id)
);
-- 建立 HOST 資料表
DROP TABLE IF EXISTS host;
CREATE TABLE host (
id INTEGER PRIMARY KEY,
name TEXT,
address TEXT,
port TEXT
);
-- 建立 HOSTPROBE 資料表
DROP TABLE IF EXISTS hostprobe;
CREATE TABLE hostprobe (
id INTEGER PRIMARY KEY,
probe_id INTEGER,
host_id INTEGER,
warning FLOAT,
error FLOAT,
FOREIGN KEY (probe_id) REFERENCES probe(id),
FOREIGN KEY (host_id) REFERENCES host(id)
);
-- 建立 TICKETQUEUE 資料表
DROP TABLE IF EXISTS ticketqueue;
CREATE TABLE ticketqueue (
id INTEGER PRIMARY KEY,
hostprobe_id INTEGER,
timestamp TEXT,
dispatched INTEGER,
FOREIGN KEY (hostprobe_id) REFERENCES hostprobe(id)
);
-- 建立 PROBEREADING 資料表
DROP TABLE IF EXISTS probereading;
CREATE TABLE probereading (
id INTEGER PRIMARY KEY,
hostprobe_id INTEGER,
timestamp TEXT,
probe_value FLOAT,
ret_code INTEGER,
FOREIGN KEY (hostprobe_id) REFERENCES hostprobe(id)
);
-- 建立 PROBINGSCHEDULE 資料表
DROP TABLE IF EXISTS probingschedule;
CREATE TABLE probingschedule (
id INTEGER PRIMARY KEY,
hostprobe_id INTEGER,
probeinterval INTEGER,
FOREIGN KEY (hostprobe_id) REFERENCES hostprobe(id)
);
-- 建立 SYSTEMPARAMS 資料表
DROP TABLE IF EXISTS systemparams;
CREATE TABLE systemparams (
id INTEGER PRIMARY KEY,
name TEXT,
value TEXT
);
-- 建立 HOSTPARAMS 資料表
DROP TABLE IF EXISTS hostparams;
CREATE TABLE hostparams (
id INTEGER PRIMARY KEY,
host_id INTEGER,
param_id INTEGER,
value TEXT,
FOREIGN KEY (host_id) REFERENCES host(id),
FOREIGN KEY (param_id) REFERENCES systemparams(id)
);
內容解密:
DROP TABLE IF EXISTS用於刪除已存在的資料表,避免初始化時發生錯誤。CREATE TABLE陳述式定義了各個資料表的結構,包括欄位名稱、資料型別及主鍵、外部索引鍵約束。FOREIGN KEY約束確保了資料表之間的參照完整性,例如HOSTPROBE資料表中的probe_id和host_id分別參照PROBE和HOST資料表的主鍵。
Python 存取 SQLite3 資料函式庫
使用 Python 的 sqlite3 模組可以輕鬆地存取 SQLite3 資料函式庫。以下是一個簡單的範例:
import sqlite3
# 連線到資料函式庫
con = sqlite3.connect('monitor.db')
# 執行 SQL 查詢
for row in con.execute('SELECT * FROM hostprobe'):
print(row)
# 關閉連線(在此範例中未顯示,但實際使用時應加上)
# con.close()
內容解密:
sqlite3.connect('monitor.db')用於建立與資料函式庫的連線。con.execute('SELECT * FROM hostprobe')執行了一個 SQL 查詢,選取hostprobe資料表中的所有記錄。- 查詢結果透過迴圈逐一列印出來。