分散式監控系統的核心在於資料函式庫設計和程式間通訊。本文介紹的系統採用 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'))

封裝基本資料型別

xmlrpclibdumps 方法可以用來將基本 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 命令來建立所需的資料表結構。

資料函式庫結構設計

該資料函式庫包含多個資料表,分別用於儲存不同型別的監控資料和系統組態資訊。主要的資料表包括:

  1. SENSOR:用於儲存所有可用的感測器列表
  2. PROBE:定義了與感測器相關的引數列表及預設的門檻值
  3. HOST:儲存所有監控代理的資訊
  4. HOSTPROBE:將可用的探針對映到主機,並可覆寫預設的門檻值
  5. TICKETQUEUE:儲存所有待處理和已傳送的票據
  6. PROBEREADING:儲存從監控代理取得的所有讀數
  7. PROBINGSCHEDULE:定義探針的執行間隔
  8. SYSTEMPARAMS:定義系統組態引數
  9. 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)
);

內容解密:

  1. DROP TABLE IF EXISTS 用於刪除已存在的資料表,避免初始化時發生錯誤。
  2. CREATE TABLE 陳述式定義了各個資料表的結構,包括欄位名稱、資料型別及主鍵、外部索引鍵約束。
  3. FOREIGN KEY 約束確保了資料表之間的參照完整性,例如 HOSTPROBE 資料表中的 probe_idhost_id 分別參照 PROBEHOST 資料表的主鍵。

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()

內容解密:

  1. sqlite3.connect('monitor.db') 用於建立與資料函式庫的連線。
  2. con.execute('SELECT * FROM hostprobe') 執行了一個 SQL 查詢,選取 hostprobe 資料表中的所有記錄。
  3. 查詢結果透過迴圈逐一列印出來。