Zum Hauptinhalt springen
IngenieurwesenMar 28, 2026

Deep EVM #21: Ereignisgesteuerte Architektur in Rust — Bus-Muster fuer Echtzeitsysteme

OS
Open Soft Team

Engineering Team

Warum ereignisgesteuerte Architektur?

In Hochleistungssystemen wie MEV-Bots, Blockchain-Indexern und Echtzeit-Trading-Engines muessen Komponenten mit minimaler Latenz auf Ereignisse reagieren. Das traditionelle Request-Response-Modell kann nicht mithalten. Sie brauchen eine ereignisgesteuerte Architektur, bei der Komponenten ueber asynchrones Message Passing kommunizieren.

tokio-Channel-Typen

mpsc (Multi-Producer, Single-Consumer)

use tokio::sync::mpsc;

let (tx, mut rx) = mpsc::channel::<BlockEvent>(1000);

// Producer 1
let tx1 = tx.clone();
tokio::spawn(async move {
    tx1.send(BlockEvent::NewBlock(block)).await.unwrap();
});

// Producer 2
let tx2 = tx.clone();
tokio::spawn(async move {
    tx2.send(BlockEvent::Reorg(depth)).await.unwrap();
});

// Single Consumer
tokio::spawn(async move {
    while let Some(event) = rx.recv().await {
        match event {
            BlockEvent::NewBlock(b) => process_block(b).await,
            BlockEvent::Reorg(d) => handle_reorg(d).await,
        }
    }
});

broadcast (Multi-Producer, Multi-Consumer)

use tokio::sync::broadcast;

let (tx, _) = broadcast::channel::<PriceUpdate>(1000);

// Mehrere Consumer
let mut rx1 = tx.subscribe();
let mut rx2 = tx.subscribe();

tokio::spawn(async move {
    while let Ok(update) = rx1.recv().await {
        strategy_a.on_price(update).await;
    }
});

tokio::spawn(async move {
    while let Ok(update) = rx2.recv().await {
        strategy_b.on_price(update).await;
    }
});

watch (Single-Value, Multiple-Readers)

use tokio::sync::watch;

let (tx, rx) = watch::channel(GasPrice::default());

// Writer: Aktualisiert den neuesten Wert
tokio::spawn(async move {
    loop {
        let price = fetch_gas_price().await;
        tx.send(price).unwrap();
        tokio::time::sleep(Duration::from_secs(1)).await;
    }
});

// Reader: Erhaelt immer den neuesten Wert
let current_price = *rx.borrow();

Das Bus-Muster

Ein zentraler Event-Bus, der Nachrichten an alle registrierten Handler weiterleitet:

struct EventBus {
    block_tx: broadcast::Sender<BlockEvent>,
    price_tx: broadcast::Sender<PriceUpdate>,
    trade_tx: mpsc::Sender<TradeSignal>,
}

impl EventBus {
    fn new() -> Self {
        let (block_tx, _) = broadcast::channel(1000);
        let (price_tx, _) = broadcast::channel(1000);
        let (trade_tx, _) = mpsc::channel(1000);
        Self { block_tx, price_tx, trade_tx }
    }
    
    fn subscribe_blocks(&self) -> broadcast::Receiver<BlockEvent> {
        self.block_tx.subscribe()
    }
}

Backpressure-Strategien

Wenn ein Consumer langsamer ist als ein Producer:

  1. Bounded Channels — Producer blockiert, wenn der Puffer voll ist
  2. Dropping — Aelteste Nachrichten verwerfen (broadcast::channel macht das automatisch)
  3. Sampling — Nur jede N-te Nachricht verarbeiten
  4. Batching — Nachrichten buendeln und als Batch verarbeiten

Fazit

Ereignisgesteuerte Architektur mit tokio-Channels ermoeglicht hochperformante, entkoppelte Systeme in Rust. Die Wahl des richtigen Channel-Typs — mpsc, broadcast oder watch — haengt von Ihrem Kommunikationsmuster ab. Das Bus-Muster bietet eine saubere Abstraktion fuer komplexe Systeme mit vielen Komponenten.