Entwurfsmuster in Go: Fortgeschrittenes Niveau für professionelle Entwickler
\n\nGo (Golang) ist eine Sprache, die für effizientes Arbeiten mit Nebenläufigkeit und Microservice-Architekturen entwickelt wurde. Grundlegende Entwurfsmuster wie Singleton oder Factory sind gut bekannt, aber für die Erstellung hochbelastbarer und fehlertoleranter Systeme ist ein fortgeschrittenes Niveau der Beherrschung von Mustern erforderlich. In diesem Artikel werden wir vier Schlüsselmuster analysieren: Fan-Out/Fan-In, Pipeline, Worker Pool und Circuit Breaker. Jedes von ihnen löst praktische Probleme der Skalierung, der parallelen Verarbeitung und des Schutzes des Systems vor kaskadierenden Ausfällen.
\n\n1. Fan-Out / Fan-In Muster: Parallele Datenverarbeitung
\n\nDieses Muster ist die Grundlage für parallele Berechnungen in Go. Fan-Out verteilt die Last auf mehrere Goroutinen, während Fan-In die Ergebnisse wieder in einem einzigen Kanal sammelt.
\n\nImplementierung von Fan-Out
\nStellen wir uns eine Aufgabe vor: Es müssen 100 URLs verarbeitet werden. Anstatt sie sequentiell zu verarbeiten, starten wir 10 Goroutinen, von denen jede eine Aufgabe aus einem gemeinsamen Kanal nimmt.
\n\npackage main\n\nimport (\n "fmt"\n "sync"\n)\n\nfunc worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {\n defer wg.Done()\n for job := range jobs {\n // Simulation der Verarbeitung\n results <- job * 2\n fmt.Printf("Worker %d processed job %d\\n", id, job)\n }\n}\n\nfunc main() {\n const numJobs = 20\n const numWorkers = 5\n\n jobs := make(chan int, numJobs)\n results := make(chan int, numJobs)\n var wg sync.WaitGroup\n\n // Fan-Out: Worker starten\n for w := 1; w <= numWorkers; w++ {\n wg.Add(1)\n go worker(w, jobs, results, &wg)\n }\n\n // Aufgaben senden\n for j := 1; j <= numJobs; j++ {\n jobs <- j\n }\n close(jobs)\n\n // Fan-In: Auf Abschluss warten und results schließen\n go func() {\n wg.Wait()\n close(results)\n }()\n\n // Ergebnisse sammeln\n for result := range results {\n fmt.Println("Result:", result)\n }\n}\n\n\nWichtige Punkte:
\n- \n
- Der Kanal
jobsdient als Fan-Out-Punkt: Alle Worker lesen daraus. \n - Der Kanal
resultsist der Fan-In-Punkt: Alle Worker schreiben hinein. \n - Die Verwendung von
sync.WaitGroupstellt sicher, dass wir results erst schließen, nachdem alle Goroutinen abgeschlossen sind. \n
2. Pipeline Muster: Fließbandverarbeitung von Daten
\n\nPipeline ist eine Kette von Stufen, wobei jede Stufe Daten verarbeitet und an die nächste weitergibt. In Go wird dies durch eine Sequenz von Kanälen implementiert. Das Muster eignet sich ideal für ETL-Prozesse, Logverarbeitung oder Datentransformation.
\n\nBeispiel: Textverarbeitungspipeline
\n\npackage main\n\nimport "fmt"\n\n// Stufe 1: Zahlengenerierung\nfunc generate(nums ...int) <-chan int {\n out := make(chan int)\n go func() {\n for _, n := range nums {\n out <- n\n }\n close(out)\n }()\n return out\n}\n\n// Stufe 2: Quadrieren\nfunc square(in <-chan int) <-chan int {\n out := make(chan int)\n go func() {\n for n := range in {\n out <- n * n\n }\n close(out)\n }()\n return out\n}\n\n// Stufe 3: Multiplikation mit 2\nfunc multiply(in <-chan int, factor int) <-chan int {\n out := make(chan int)\n go func() {\n for n := range in {\n out <- n * factor\n }\n close(out)\n }()\n return out\n}\n\nfunc main() {\n // Pipeline zusammenbauen\n pipeline := multiply(square(generate(1, 2, 3, 4)), 10)\n\n // Ergebnisse konsumieren\n for result := range pipeline {\n fmt.Println(result) // 10, 40, 90, 160\n }\n}\n\n\nVorteile:
\n- \n
- Jede Stufe ist eine separate