Přeskočit na obsah
Codedock
SlužbyJak pracujemeReferenceInsightsKariéraKontakt
Zpět na všechny články
Enterprise integrace

·

7 min čtení

·

Napsal Tomáš Mikeš

Zpracování GB/s síťového provozu v .NET: architektura bez droppování packetů

Pro Netigo zpracováváme IPFIX/NetFlow flows v objemech GB/s. Klíč je buffering, backpressure a horizontal scaling — žádný sync processing. Co musí sedět, aby systém nezačal ztrácet data pod zátěží.

High-performance.NETNetworkBackpressure

Sensor pošle 50 000 NetFlow flows v peaku za sekundu. Typický beginner system: sync parsing, sync DB insert. Výsledek — server nestíhá, UDP packety padají, 30 % dat zmizí bez stopy. Nikdo to nezjistí dokud někdo nemá důvod to kontrolovat.

Pro Netigo jsme postavili pipeline, která zvládá peaky 2 GB/s síťového provozu bez dropu i při dlouhodobém nárůstu. Architektura má několik load-bearing principů, a dodržení všech je podmínka.

Princip 1: UDP ingestion mimo hlavní vlákno

NetFlow/IPFIX jde přes UDP. UDP nemá retry, pokud ho neberu dostatečně rychle, ztratím ho. První věc — UDP socket listener nesmí dělat nic jiného než vzít bytes a shodit je do in-memory queue.

V .NET:

while (true) {
  var result = await udpClient.ReceiveAsync();
  await channel.Writer.WriteAsync(new RawPacket {
    Bytes = result.Buffer,
    ReceivedAt = DateTime.UtcNow,
    SourceEndPoint = result.RemoteEndPoint
  });
}

Žádné parsing. Žádná validace. Žádný DB call. Pouze přesun bytes do System.Threading.Channels bounded channel. Čas na jeden packet: ~15 μs. To znamená, že jedno vlákno zvládne ~60 000 packetů/s.

Pokud budeš chtít víc: druhý UDP listener na druhém portu + load balancer před tím. Horizontální scaling ingestion.

Princip 2: Bounded channel = implicit backpressure

Když processing pipeline zpomalí (DB je pomalá, network blip), in-memory queue roste. Neomezená queue = out of memory za minuty. Správné řešení: bounded channel s limitem.

var options = new BoundedChannelOptions(capacity: 100_000) {
  FullMode = BoundedChannelFullMode.Wait
};
var channel = Channel.CreateBounded<RawPacket>(options);

Když je queue plná, writer čeká. Co se stane s UDP packety? Drop — ale drop detekovaný (metric count increment). Nemá to sice vracet packety zpět, ale vidíš, kdy se to stane a můžeš scale-up.

Alternativa: kernel-level buffers (setsockopt SO_RCVBUF). Zvýšit socket buffer na 32 MB dává několik vteřin rezervy, než začne UDP drop na OS level.

Princip 3: Parser v dedicated vláknech, ne v thread pool

Parser čte z channelu, dekóduje IPFIX template, extrahuje fields. CPU-intensive práce. Pokud běží v default thread pool, konkuruje s HTTP API, logováním a vším ostatním.

Dedicated thread pool pro parser. Počet vláken = count CPU cores. V .NET:

var parserTasks = Enumerable.Range(0, Environment.ProcessorCount)
  .Select(_ => Task.Factory.StartNew(
    () => ParseLoop(channel.Reader),
    TaskCreationOptions.LongRunning))
  .ToArray();

Každý task pull-uje z channel.Reader, parsuje packet, generuje 1-30 parsed flows (IPFIX packet obsahuje multiple flow records) a shodí je do druhého channelu pro DB batch writer.

Princip 4: DB write v dávkách, ne po jednom

Insert jedné řádky do TimescaleDB = ~1 ms (network + parse + write). 500 000 rows/s jako single inserts je nemožné — 500 000 ms = 500 sekund práce na sekundu dat.

Řešení: COPY FROM (Postgres bulk insert). Batchujeme 5 000-10 000 rows do jednoho COPY statement. Latence ~30 ms per batch. Throughput ~200 000 rows/s per writer, a writery jsou horizontálně scalovatelné.

V .NET přes NpgsqlBinaryImporter:

using var writer = conn.BeginBinaryImport(
  "COPY flows (ts, src_ip, dst_ip, bytes, ...) FROM STDIN BINARY");
foreach (var flow in batch) {
  writer.StartRow();
  writer.Write(flow.Timestamp, NpgsqlDbType.TimestampTz);
  writer.Write(flow.SrcIp, NpgsqlDbType.Inet);
  // ...
}
await writer.CompleteAsync();

Rozdíl oproti individual INSERTs: 100× vyšší throughput.

Princip 5: Metriky z každé fáze, ne jen end-to-end

Když system zpomalí, musíš vědět KDE. Naše metriky v každé fázi:

  • UDP receive: packets/s, bytes/s, drop count (ze socket stats)
  • Raw channel: current depth, enqueue wait time (když je full)
  • Parser: packets parsed/s, parse errors/s, parse time p99
  • Parsed channel: current depth, enqueue wait time
  • DB writer: batches/s, rows/s, COPY time p99, DB connection pool utilization
  • End-to-end latency: od UDP receive po DB commit (p99)

Když p99 end-to-end latence rostoucí, ale UDP drop count stále 0 — řekneš mi: backpressure funguje, někde je pomalá fáze. Podívej se na per-stage metriky. Vidíš, že parser p99 se zhoršil? Tam je problém. DB writer batches/s klesl? DB je pomalá.

Princip 6: Horizontální scale namísto vertikálního

Když hit limit single-node (~1M packets/s s naším HW), neškáluj RAM/CPU. Scale out.

Každý sensor posílá svoje flows na jeden ze čtyř ingestion nodes (stateless, load-balanced UDP přes consistent hashing podle sensor IP). Každý node má kompletní pipeline (parser + writer) a píše do sdílené TimescaleDB.

4 nodes × 500k packets/s = 2M packets/s kapacita. Pokud DB začne být úzké hrdlo, přidáme multi-node TimescaleDB nebo shardujeme podle sensor ID.

Výsledek pro Netigo po 12 měsících provozu

  • Peak throughput: 850 000 flows/s (1,7 GB/s síťových dat)
  • Average: 350 000 flows/s
  • UDP drop rate: < 0,001 % (méně než 1 v milionu)
  • End-to-end latence P99: 2,3 s (UDP → DB committed)
  • Single TimescaleDB single-node, 4 ingestion nodes
  • Downtime za rok: 0

Co si z toho vzít

Zpracování velkého objemu network dat je hodně o nesnažit se dělat moc na jednom místě. Každá fáze pipeline má svoji velikost queue, svoje vlákno, svoje metriky. Když některá fáze zpomalí, backpressure ji izoluje od zbytku.

Sync end-to-end pipeline (UDP receive → parse → insert) pracuje do 20-50 MB/s. Nad tím se musíš rozdělit. Princip nezávisí na jazyce — v Javě, Go, Rustu bude stejný. V .NET máš dobré nástroje (Channels, NpgsqlBinaryImporter), ale ne výrazně lepší nebo horší než jiné mainstream prostředí.

Řešíš něco podobného?

Domluvme si 30min technický call. Bez obchodních procesů — přímá architekturní zpětná vazba.

Vybrat termín

Architektura, cloud a integrace pro komplexní systémy. Senior architekt na každém projektu.

Navigace

SlužbyJak pracujemeReferenceInsightsKariéraKontaktSrovnání s agenturou

Služby

VývojCloudDevOpsAI & DataKonzultaceŘízení

Kontakt

CodeDock s.r.o.

Zlenická 863/9, 104 00 Praha 22

Česká republika

info@codedock.com

IČO: 14292769

DIČ: CZ14292769


© 2026 Codedock

KontaktOchrana osobních údajů
Domluvit call