返回文章列表

事件溯源與平行非同步設計模式

本文深入探討事件溯源模式的原理、實作與應用,並解析平行與非同步設計模式,包含執行緒池、工作者模型、Future 和 Promise,以及反應式程式設計中的觀察者模式,搭配 Python 程式碼範例與圖表說明,有效提升程式效能和回應速度。

軟體架構 程式設計

事件溯源模式透過記錄狀態變更事件,實作系統狀態管理和重建,尤其適用於處理複雜狀態和業務規則的系統。本文除了介紹事件溯源的核心概念、關鍵元件和實務應用外,也提供手動實作和使用 eventsourcing 函式庫的 Python 程式碼範例,說明如何在銀行帳戶管理和庫存管理中應用事件溯源模式。同時,本文也探討了平行與非同步設計模式的重要性,涵蓋執行緒池模式、工作者模型模式、Future 和 Promise 模式,以及反應式程式設計中的觀察者模式,並提供 Python 程式碼範例和圖表說明,幫助讀者理解如何提升程式效能和回應速度。這些模式能夠有效地管理和執行多個任務,提高程式的平行處理能力,並在非同步操作和資料流處理方面展現出優勢。

事件溯源模式深度解析與實作

事件溯源(Event Sourcing)模式是一種強大的架構設計模式,它透過將狀態變更記錄為一系列事件來實作系統狀態的管理和重建。這種模式在處理複雜狀態和業務規則的系統中特別有用。本文將深入探討事件溯源模式的原理、實作方式及其在不同領域的應用。

事件溯源模式的核心概念

事件溯源模式的核心在於將所有對系統狀態的變更以事件的形式記錄下來。這些事件不僅包含了變更的內容,還包含了變更的時間順序,從而可以重建出系統在任何時間點的狀態。

事件溯源模式的關鍵元件

  1. 事件(Event):代表系統狀態的變更,包含事件型別和相關資料。一旦事件被建立並應用,就不可更改。
  2. 聚合根(Aggregate):代表一個業務邏輯或資料的單元,負責追蹤狀態變更並記錄事件。
  3. 事件儲存(Event Store):儲存所有已發生的事件,是系統狀態變更的唯一事實來源。

事件溯源模式的實務應用

事件溯源模式在多個領域有廣泛的應用,包括但不限於:

  • 財務交易:記錄每筆交易的發生,以提供透明、可稽核的財務記錄。
  • 庫存管理:追蹤每個物品的生命週期,幫助企業維持準確的庫存記錄並預測未來需求。
  • 客戶行為追蹤:記錄客戶與平臺的每一次互動,為個人化行銷和使用者經驗改進提供資料支援。

手動實作事件溯源模式

以下是一個簡單的銀行帳戶管理範例,展示如何手動實作事件溯源模式:

class Account:
 def __init__(self):
 self.balance = 0
 self.events = []

 def apply_event(self, event):
 if event["type"] == "deposited":
 self.balance += event["amount"]
 elif event["type"] == "withdrawn":
 self.balance -= event["amount"]
 self.events.append(event)

 def deposit(self, amount):
 event = {"type": "deposited", "amount": amount}
 self.apply_event(event)

 def withdraw(self, amount):
 event = {"type": "withdrawn", "amount": amount}
 self.apply_event(event)

def main():
 account = Account()
 account.deposit(100)
 account.deposit(50)
 account.withdraw(30)
 account.deposit(30)
 for evt in account.events:
 print(evt)
 print(f"Balance: {account.balance}")

if __name__ == "__main__":
 main()

程式碼解析

此範例定義了一個Account類別作為聚合根,負責管理帳戶餘額和事件記錄。apply_event方法根據事件型別更新帳戶餘額並記錄事件。depositwithdraw方法則建立相應的事件並呼叫apply_event進行處理。

使用事件溯源函式庫實作

對於更複雜的系統,可以使用專門的事件溯源函式庫來簡化實作。以下是一個使用eventsourcing函式庫的庫存管理範例:

from eventsourcing.domain import Aggregate, event
from eventsourcing.application import Application

class InventoryItem(Aggregate):
 @event("ItemCreated")
 def __init__(self, name, quantity=0):
 self.name = name
 self.quantity = quantity

 @event("QuantityIncreased")
 def increase_quantity(self, amount):
 self.quantity += amount

 @event("QuantityDecreased")
 def decrease_quantity(self, amount):
 self.quantity -= amount

class InventoryApp(Application):
 def create_item(self, name, quantity):
 item = InventoryItem(name, quantity)
 self.save(item)
 return item.id

程式碼解析

此範例使用eventsourcing函式庫定義了InventoryItem類別作為聚合根,並使用@event裝飾器標註了不同的方法對應的事件型別。InventoryApp類別則繼承自Application,提供了建立庫存物品的方法。

圖表翻譯

此圖示展示了庫存管理系統中處理物品數量的基本流程。首先檢查物品是否存在,若存在則更新其數量;若不存在,則建立新物品。無論哪種情況,都會記錄相應的事件並儲存。這個流程清晰地展示了事件溯源模式在庫存管理中的應用。

事件溯源模式的優勢與挑戰

事件溯源模式提供了諸多優勢,如:

  • 系統狀態可重建:透過事件序列可以重建系統在任何時間點的狀態。
  • 稽核軌跡:所有狀態變更都被記錄下來,提供了完整的稽核軌跡。
  • 業務邏輯靈活性:透過事件處理可以輕鬆新增新的業務邏輯或修改現有邏輯。

然而,事件溯源模式也帶來了一些挑戰,如:

  • 事件版本管理:隨著系統演進,可能需要對事件格式進行變更,需要妥善管理不同版本的事件。
  • 事件儲存管理:大量的事件需要有效的儲存和查詢機制。
  • 系統複雜度:事件溯源模式引入了額外的抽象層,可能增加系統的複雜度。

平行與非同步設計模式

在前一章中,我們探討了架構設計模式,這些模式有助於解決複雜專案中特有的挑戰。接下來,我們需要討論平行和非同步模式,這是我們解決方案目錄中的另一個重要類別。

平行與非同步程式設計的重要性

平行允許程式同時管理多個操作,充分利用現代處理器的能力。這類別似於廚師同時準備多道菜,每一步都精心安排,以便所有菜餚同時準備就緒。另一方面,非同步程式設計允許應用程式在等待操作完成時轉移到其他任務,例如將食物訂單傳送到廚房並在訂單準備好之前為其他客戶提供服務。

本章將涵蓋以下主要主題:

  • 執行緒池模式
  • 工作者模型模式
  • Future 和 Promise 模式
  • 反應式程式設計中的觀察者模式
  • 其他平行和非同步模式

技術需求

請參閱第1章中提出的技術需求。本章討論的程式碼額外技術需求如下:

  • Faker,使用 pip install faker 安裝
  • ReactiveX,使用 pip install reactivex 安裝

執行緒池模式

執行緒池模式是一種管理執行緒的技術,能夠重複使用已建立的執行緒來執行多個任務,避免了頻繁建立和銷毀執行緒的開銷。以下是一個使用 Python concurrent.futures 模組實作執行緒池的範例:

import concurrent.futures
import time

def task(n):
    print(f"Task {n} started")
    time.sleep(2)
    print(f"Task {n} finished")
    return n

def main():
    with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
        futures = [executor.submit(task, n) for n in range(5)]
        for future in concurrent.futures.as_completed(futures):
            result = future.result()
            print(f"Task {result} result received")

if __name__ == "__main__":
    main()

程式碼解析:

此範例程式碼展示瞭如何使用 concurrent.futures 模組中的 ThreadPoolExecutor 類別來建立一個執行緒池。ThreadPoolExecutor 能夠管理一組執行緒,並將任務分配給這些執行緒執行。在這個範例中,我們定義了一個名為 task 的函式,模擬了一個耗時 2 秒的任務。main 函式建立了一個最多包含 3 個執行緒的執行緒池,並提交了 5 個任務給執行緒池執行。透過 as_completed 方法,我們能夠在每個任務完成時取得其結果。

圖表解析:

此圖示展示了執行緒池模式的工作流程。首先,建立一個執行緒池並提交任務。執行緒池中的執行緒執行這些任務。當任務完成時,取得其結果。這個流程能夠有效地管理和執行多個任務,提高程式的平行處理能力。

工作者模型模式

工作者模型模式是一種設計模式,將任務分配給多個工作者(worker)執行,每個工作者獨立完成分配給自己的任務。以下是一個簡單的工作者模型範例:

import threading
import queue
import time

class Worker(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue

    def run(self):
        while True:
            task = self.queue.get()
            if task is None:
                break
            print(f"Worker {self.name} started task {task}")
            time.sleep(2)
            print(f"Worker {self.name} finished task {task}")
            self.queue.task_done()

def main():
    q = queue.Queue()
    threads = []

    for i in range(3):
        t = Worker(q)
        t.start()
        threads.append(t)

    for task in range(5):
        q.put(task)

    q.join()

    for i in range(3):
        q.put(None)
    for t in threads:
        t.join()

if __name__ == "__main__":
    main()

程式碼解析:

此範例程式碼展示瞭如何使用工作者模型模式來執行多個任務。我們定義了一個名為 Worker 的類別,繼承自 threading.Thread。每個 Worker 例項從佇列中取得任務並執行。main 函式建立了多個 Worker 執行緒,並將任務放入佇列中。工作者執行緒從佇列中取得任務並執行,直到佇列中的任務全部完成。

圖表解析:

此圖示展示了工作者模型模式的工作流程。首先,建立多個工作者執行緒並將任務放入佇列中。工作者執行緒從佇列中取得任務並執行,直到佇列中的任務全部完成。這個流程能夠有效地利用多執行緒來執行多個任務。

Future 和 Promise 模式

Future 和 Promise 模式是一種設計模式,用於處理非同步操作的結果。Future 代表一個可能尚未完成的非同步操作,而 Promise 則代表對該操作的承諾,提供了一種在操作完成時取得結果的機制。

import concurrent.futures

def task(n):
    return n * n

def main():
    with concurrent.futures.ThreadPoolExecutor() as executor:
        future = executor.submit(task, 5)
        result = future.result()
        print(f"Result: {result}")

if __name__ == "__main__":
    main()

程式碼解析:

此範例程式碼展示瞭如何使用 concurrent.futures 模組中的 Future 物件來處理非同步操作。我們定義了一個名為 task 的函式,並使用 ThreadPoolExecutor 將其提交給執行緒池執行。Future 物件的 result 方法用於取得非同步操作的結果。

圖表解析:

此圖示展示了 Future 和 Promise 模式的工作流程。首先,提交一個非同步任務並取得一個 Future 物件。然後,檢查任務是否完成。如果任務完成,取得其結果;否則,繼續等待直到任務完成。這個流程能夠有效地處理非同步操作的結果。

反應式程式設計中的觀察者模式

觀察者模式是一種設計模式,定義了物件之間的一對多依賴關係,當一個物件的狀態發生變化時,所有依賴它的物件都會收到通知並自動更新。在反應式程式設計中,觀察者模式被廣泛應用於處理非同步資料流。

import reactivex as rx
from reactivex import operators as ops

def main():
    observable = rx.from_iterable([1, 2, 3, 4, 5])
    observable.pipe(
        ops.map(lambda x: x * x),
        ops.filter(lambda x: x % 2 == 0)
    ).subscribe(
        on_next=lambda x: print(f"Received: {x}"),
        on_error=lambda e: print(f"Error: {e}"),
        on_completed=lambda: print("Completed")
    )

if __name__ == "__main__":
    main()

程式碼解析:

此範例程式碼展示瞭如何使用 ReactiveX 函式庫來實作反應式程式設計。我們建立了一個可觀察序列(observable),並對其進行了對映(map)和過濾(filter)操作。訂閱(subscribe)該可觀察序列後,我們能夠接收到處理後的資料。

圖表解析:

此圖示展示了反應式程式設計中的觀察者模式的工作流程。首先,建立一個可觀察序列並對其進行資料處理。然後,訂閱該可觀察序列並接收處理後的資料。這個流程能夠有效地處理非同步資料流。

執行緒池模式與工作者模型模式:提升平行處理效能

在現代軟體開發中,為了提升系統效能和回應速度,經常需要處理平行任務和非同步操作。其中,執行緒池模式(Thread Pool pattern)和工作者模型模式(Worker Model pattern)是兩種常見且有效的平行處理技術。本文將深入探討這兩種模式的原理、實作方法及其在實際應用中的優勢。

執行緒池模式

執行緒池模式的核心思想是預先建立一定數量的執行緒,並將它們儲存在一個池中。當有任務需要執行時,從池中取出一個空閒的執行緒來執行任務,任務完成後,執行緒傳回池中等待下一個任務。這種模式避免了頻繁建立和銷毀執行緒所帶來的效能開銷。

執行緒池的工作原理

  1. 初始化:應用程式啟動時,建立一定數量的執行緒儲存在執行緒池中。
  2. 任務提交:當有任務需要執行時,將任務提交給執行緒池。
  3. 任務執行:執行緒池中的執行緒執行提交的任務。如果所有執行緒都忙碌,新任務將被放入佇列中等待。
  4. 執行緒回收:任務完成後,執行緒傳回執行緒池,等待下一個任務。

程式碼範例:使用Python實作執行緒池

from concurrent.futures import ThreadPoolExecutor
import time

def task(n):
    print(f"正在執行任務 {n}")
    time.sleep(1)
    print(f"任務 {n} 完成")

# 建立一個包含5個執行緒的執行緒池
with ThreadPoolExecutor(max_workers=5) as executor:
    for i in range(10):
        executor.submit(task, i)

內容解密:

此範例展示瞭如何使用Python的ThreadPoolExecutor類別建立一個執行緒池,並提交10個任務給執行緒池執行。執行緒池中包含5個執行緒,任務完成後,執行緒會傳回池中等待下一個任務。這種方式有效地控制了系統資源的使用,避免了無限制地建立執行緒。

工作者模型模式

工作者模型模式是一種將大任務分解為多個小任務,並由多個工作者平行處理的模式。工作者可以是執行緒、行程或分散式系統中的不同機器。

工作者模型模式的優勢

  • 可擴充套件性:容易擴充套件,可以根據需求增加或減少工作者的數量。
  • 效率:透過平行處理任務,可以充分利用系統資源,提高處理效率。
  • 靈活性:適用於不同的處理策略,從簡單的執行緒級平行到複雜的分散式系統。

實際應用範例

  • 資料處理:將大規模資料集分割成小塊,並由多個工作者平行處理。
  • 任務平行:在應用程式中,將獨立的任務分配給不同的工作者平行執行。

Plantuml執行緒池運作流程

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title 事件溯源與平行非同步設計模式

package "機器學習流程" {
    package "資料處理" {
        component [資料收集] as collect
        component [資料清洗] as clean
        component [特徵工程] as feature
    }

    package "模型訓練" {
        component [模型選擇] as select
        component [超參數調優] as tune
        component [交叉驗證] as cv
    }

    package "評估部署" {
        component [模型評估] as eval
        component [模型部署] as deploy
        component [監控維護] as monitor
    }
}

collect --> clean : 原始資料
clean --> feature : 乾淨資料
feature --> select : 特徵向量
select --> tune : 基礎模型
tune --> cv : 最佳參數
cv --> eval : 訓練模型
eval --> deploy : 驗證模型
deploy --> monitor : 生產模型

note right of feature
  特徵工程包含:
  - 特徵選擇
  - 特徵轉換
  - 降維處理
end note

note right of eval
  評估指標:
  - 準確率/召回率
  - F1 Score
  - AUC-ROC
end note

@enduml

圖表翻譯:

此圖示展示了執行緒池的運作流程。首先,系統初始化執行緒池並提交任務。如果有空閒執行緒,則立即執行任務;否則,任務將被加入佇列中等待。當任務完成後,執行緒傳回池中,等待下一個任務。這種機制有效地管理了執行緒資源,提高了系統的平行處理能力。