在分散式系統中,有效彙整各節點資料至關重要。本文將探討如何建構一個集中式的資料收集系統,從 Python 套件建立、專案範本生成,到資料函式庫選型與 Schema 設計,提供一個完整的解決方案。首先,我們將使用 Cookiecutter 工具簡化 Python 專案的初始化流程,確保程式碼結構的一致性。接著,我們將探討資料函式庫的選擇,比較 SQL 與 NoSQL 的優缺點,並考量到感測器資料的多樣性與查詢需求。最後,我們將提出一個根據 JSONB 欄位的關聯式資料函式庫設計方案,以兼顧資料的靈活性與查詢效率。
彙整流程:建立集中式資料收集系統
在具備穩健的程式碼基礎後,我們能夠從電腦收集資料並透過 HTTP 介面進行報告。現在,是時候開始記錄和分析這些資料了。我們需要建立一個中央彙整流程,連線到每個感測器並下載資料。這樣的流程將使我們能夠觀察不同感測器之間的相關性以及隨時間變化的趨勢。
建立新的 Python 套件
首先,我們需要建立一個新的 Python 套件。將彙整流程的所有程式碼與資料收集程式碼一起分發是不合理的;我們預計感測器的佈署次數將遠多於彙整流程。對於程式設計師來說,很少從零開始一個新專案並自行編寫所有樣板程式碼。更常見的做法是使用範本,或是複製其他專案並移除其功能。從一個現有的但不具備任何功能的程式碼開始,比從一個空目錄開始要容易得多。
使用 Cookiecutter 工具
雖然可以透過複製目錄來從範本建立新專案,但有一些工具可以使這個過程變得更加簡單。雖然複製範本目錄並修改它看起來很簡單,但通常需要將檔案和目錄從「skeleton」或「example」重新命名,以符合正在建立的專案名稱。像 Cookiecutter 這樣的工具可以自動化這個過程,允許建立使用變數的範本,這些變數在首次建立專案時提供。
Cookiecutter 的基本使用方法
Cookiecutter 可以透過以下簡單的步驟來使用:
安裝 Cookiecutter:首先,需要在系統上安裝 Cookiecutter。可以使用 pip 進行安裝:
pip install cookiecutter選擇範本:選擇一個適合專案需求的 Cookiecutter 範本。可以從 GitHub 或其他來源取得範本。
執行 Cookiecutter:執行 Cookiecutter 並指定所選的範本 URL 或路徑:
cookiecutter https://github.com/username/cookiecutter-template.git或是本地路徑:
cookiecutter path/to/cookiecutter-template填寫變數:Cookiecutter 將提示輸入範本中定義的變數值,例如專案名稱、作者等。
生成專案:根據提供的變數,Cookiecutter 將生成新的專案目錄,包含所有必要的檔案和結構。
使用 Cookiecutter 的好處
使用 Cookiecutter 的好處包括:
- 節省時間:避免了從頭開始建立新專案的繁瑣過程。
- 保持一致性:透過使用標準化的範本,可以在多個專案中保持一致的結構和命名約定。
- 靈活性:可以根據需要自定義範本,以適應不同的專案需求。
使用Cookiecutter建立專案範本
在開發過程中,建立新的專案時,使用Cookiecutter可以大幅簡化流程。Cookiecutter是一個強大的工具,能夠幫助開發者快速生成專案的基本結構和必要檔案。建議將Cookiecutter安裝在系統的Python環境中,就像安裝Pipenv一樣。
安裝Cookiecutter
要安裝Cookiecutter,可以使用以下命令:
pip install --user cookiecutter
這樣就可以在系統範圍內使用Cookiecutter,而不需要針對每個專案進行安裝。
使用現有的Cookiecutter範本
Cookiecutter提供了許多現成的範本,可以用於建立不同型別的Python專案。這些範本涵蓋了從簡單的Python套件到複雜的Web應用程式等各種專案型別。使用範本時,不需要事先安裝,只要指定範本的路徑或Git儲存函式庫的URL,Cookiecutter就會自動下載並使用該範本。
例如,要使用特定的範本,可以執行:
cookiecutter gh:MatthewWilkes/cookiecutter-simplepackage
這將從GitHub下載並使用指定的範本。
內容解密:
- gh:MatthewWilkes/cookiecutter-simplepackage:這是一種簡化的GitHub倉函式庫參照方式,告訴Cookiecutter從MatthewWilkes的GitHub帳戶下載
cookiecutter-simplepackage倉函式庫作為範本。 - 自動下載範本:Cookiecutter會檢查本地是否有該範本的快取,如果有,會提示是否更新到最新版本。
建立自訂的Cookiecutter範本
雖然現有的範本很有用,但它們可能無法完全符合特定專案的需求。因此,建立自訂的範本是一個好主意。首先,需要建立一個新的Git儲存函式庫來存放範本。在倉函式庫中,需要新增一個cookiecutter.json檔案,用於定義使用者在生成專案時需要輸入的變數及其預設值。
cookiecutter.json範例
{
"full_name": "Advanced Python Development reader",
"email": "[email protected]",
"project_name": "Example project",
"project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}",
"project_short_description": "An example project.",
"version": "1.0.0",
"open_source_license": ["BSD", "GPL", "Not open source"]
}
內容解密:
cookiecutter.json的作用:定義了生成專案時需要的變數,如專案名稱、版本等。project_slug的生成邏輯:根據project_name自動生成,將空格和短橫線替換為下劃線,並轉換為小寫。open_source_license的選項:提供了一個列表,讓使用者選擇適合的開源許可證。
接下來,需要在倉函式庫中建立一個目錄,用於存放專案範本檔案。目錄名稱可以使用{{ cookiecutter.project_slug }},這樣生成的專案目錄名稱就會與專案slug一致。在這個目錄中,可以新增專案所需的檔案和目錄結構,如setup.py、README.md等。
處理名稱空間套件的特殊情況
對於名稱空間套件(如apd.sensors),需要在範本中進行特殊處理,以確保生成的目錄結構正確。這需要使用Cookiecutter的後生成鉤子(post-generation hook)來實作自訂邏輯。
後生成鉤子的作用
後生成鉤子允許在專案生成後執行自訂指令碼,以進行額外的處理,如根據slug中的.建立巢狀目錄結構。
資料聚合處理的 Cookiecutter 範本與資料函式庫選擇
在開發資料聚合處理的過程中,我們可以利用 Cookiecutter 工具來建立專案範本,從而簡化專案的初始化流程。Cookiecutter 允許我們定義範本,並在生成專案時根據使用者輸入替換變數。
使用 Cookiecutter 範本
首先,我們需要在範本中加入 hooks 目錄,並在其中放置 post_gen_project.py 檔案。這個檔案將在專案生成後執行,用於對生成的專案進行後處理。
# hooks/post_gen_project.py
import os
package_name = "{{ cookiecutter.project_slug }}"
*namespaces, base_name = package_name.split(".")
if namespaces:
directory = "src"
existing_inner_directory = os.path.join("src", package_name)
innermost_namespace_directory = os.path.join("src", *namespaces)
os.mkdir(innermost_namespace_directory)
os.rename(
existing_inner_directory,
os.path.join(innermost_namespace_directory, base_name)
)
內容解密:
package_name = "{{ cookiecutter.project_slug }}":取得 Cookiecutter 範本中定義的project_slug變數,並將其指定給package_name。*namespaces, base_name = package_name.split("."):使用擴充套件解封裝將package_name按照.分割成多個部分,最後一部分指定給base_name,之前的部分組成namespaces列表。if namespaces::檢查是否包含名稱空間(即package_name中是否包含.)。os.mkdir(innermost_namespace_directory):建立最內層的名稱空間目錄。os.rename(existing_inner_directory, os.path.join(innermost_namespace_directory, base_name)):將原有的內部目錄重新命名並移動到名稱空間目錄中。
建立聚合處理套件
使用上述 Cookiecutter 範本,我們可以建立一個名為 apd.aggregation 的套件。執行以下命令:
> cookiecutter gh:MatthewWilkes/cookiecutter-simplepackage
full_name [Advanced Python Development reader]: Matthew Wilkes
email [[email protected]]: [email protected]
project_name [Example project]: APD Sensor aggregator
project_slug [apd_sensor_aggregator]: apd.aggregation
project_short_description [An example project.]: A programme that queries apd.sensor endpoints and aggregates their results.
version [1.0.0]:
Select license:
1 - BSD
2 - MIT
3 - Not open source
Choose from 1, 2, 3 (1, 2, 3) [1]:
> cd apd.aggregation
> git init
Initialized empty Git repository in /apd.aggregation/.git/
> git add .
> git commit -m "Generated from skeleton"
資料函式庫選擇
在決定如何儲存資料時,我們面臨著多種資料函式庫的選擇。圖 6-2 提供了一個決策樹,幫助我們根據需求選擇合適的資料函式庫型別。
資料函式庫需求
- 即時收集來自多個端點的感測器資料
- 自動記錄特定時間間隔的感測器資料
- 查詢特定時間點或時間範圍內的感測器資料
- 支援多種感測器型別,無需修改伺服器端儲存邏輯
- 支援資料匯出與匯入,用於資料可移植性和備份
- 支援按時間或端點刪除資料,確保使用者隱私
資料函式庫選擇決策樹
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title 建構集中式資料收集系統
package "資料庫架構" {
package "應用層" {
component [連線池] as pool
component [ORM 框架] as orm
}
package "資料庫引擎" {
component [查詢解析器] as parser
component [優化器] as optimizer
component [執行引擎] as executor
}
package "儲存層" {
database [主資料庫] as master
database [讀取副本] as replica
database [快取層] as cache
}
}
pool --> orm : 管理連線
orm --> parser : SQL 查詢
parser --> optimizer : 解析樹
optimizer --> executor : 執行計畫
executor --> master : 寫入操作
executor --> replica : 讀取操作
cache --> executor : 快取命中
master --> replica : 資料同步
note right of cache
Redis/Memcached
減少資料庫負載
end note
@enduml
此圖示展示了選擇資料函式庫型別的決策流程。
內容解密:
- 是否需要事務支援?:如果應用需要確保資料的一致性和完整性,應選擇支援事務的關系型資料函式庫。
- 是否需要處理大量非結構化資料?:如果應用涉及大量非結構化或半結構化資料,NoSQL 資料函式庫可能是更好的選擇。
- 是否需要複雜查詢?:如果應用需要執行複雜查詢,關系型資料函式庫提供了更強大的查詢能力。
- 鍵值對儲存或檔案儲存:對於簡單的資料儲存需求,鍵值對儲存或檔案儲存方案足夠且高效。
資料函式庫技術選擇的關鍵考量
在選擇適當的資料函式庫技術時,需要考慮多個關鍵因素。首先,我們要排除一些特殊的資料函式庫技術,例如僅限追加(append-only)的資料函式庫。這類別資料函式庫適用於日誌記錄,如交易日誌或稽核日誌,但它們與區塊鏈技術有著本質上的不同。區塊鏈技術提供了去中心化的信任機制,允許一群人共同維護資料,只有當大多數使用者同意時,才能編輯或刪除資料。
資料函式庫型別的選擇
大多數情況下,我們會在 SQL 和 NoSQL 資料函式庫之間進行選擇。NoSQL 資料函式庫曾經非常流行,但關係型資料函式庫已經採用了一些 NoSQL 的特性作為擴充套件。兩者的主要區別在於是否具有 schema。具有 schema 的資料函式庫會驗證資料以確保符合預期,而無 schema 的資料函式庫則允許儲存任意形狀的資料。
無 Schema 資料函式庫的優缺點
無 schema 的資料函式庫看似更具彈性,但實際上會使查詢或遷移資料變得更加困難。如果沒有保證欄位存在及其型別,就可能儲存看似正確但實際上有問題的資料。例如,假設我們有一個溫度日誌表格,用於儲存溫度的時間、感測器和值。如果感測器提供了一個字串,如 “21.2c” 而不是 21.2,那麼在具有 schema 的資料函式庫中,這將引發錯誤,而在無 schema 的資料函式庫中,這個插入操作將成功,但後續的聚合操作(如計算平均值)可能會失敗。
查詢支援的考量
在選擇資料函式庫時,還需要考慮如何查詢資料。查詢支援是這三個問題中最難概括的,因為不同類別的資料函式庫之間存在著顯著的差異。關係型資料函式庫通常被認為更適合查詢,而 NoSQL 資料函式庫則更依賴自然鍵,如物件儲存中的路徑或鍵值儲存中的鍵。
我們的案例
在我們的案例中,我們很難為感測器的值決定一個單一的型別,除了它們都是 JSON 可序列化的。我們希望能夠存取這個型別的內部結構,例如溫度值的大小或 IP 位址清單的長度。如果使用標準的關係型資料函式庫結構,我們將很難以未來可擴充套件的方式表示這些選項。
CREATE TABLE sensor_values(
id SERIAL PRIMARY KEY,
sensor_name TEXT NOT NULL,
value JSONB NOT NULL,
endpoint_url TEXT NOT NULL,
creation_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
內容解密:
CREATE TABLE sensor_values: 建立一個名為sensor_values的表格,用於儲存感測器的值。id SERIAL PRIMARY KEY:id欄位是一個自動遞增的整數,作為主鍵,用於唯一標識每筆記錄。sensor_name TEXT NOT NULL:sensor_name欄位用於儲存感測器的名稱,型別為文字,且不可為空。value JSONB NOT NULL:value欄位用於儲存感測器的值,型別為 JSONB,可以儲存 JSON 格式的資料,並支援對其內部結構建立索引。endpoint_url TEXT NOT NULL:endpoint_url欄位用於儲存端點的 URL,型別為文字,且不可為空。creation_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP:creation_time欄位用於儲存記錄的建立時間,型別為時間戳,預設值為當前時間。