Patrones de diseño en Go: nivel avanzado para desarrolladores profesionales
\n\nGo (Golang) es un lenguaje creado para trabajar eficientemente con concurrencia y arquitectura de microservicios. Los patrones de diseño básicos, como Singleton o Factory, son bien conocidos, pero para crear sistemas de alta carga y tolerantes a fallos se requiere un nivel avanzado de dominio de los patrones. En este artículo analizaremos cuatro patrones clave: Fan-Out/Fan-In, Pipeline, Worker Pool y Circuit Breaker. Cada uno resuelve problemas prácticos de escalado, procesamiento paralelo y protección del sistema contra fallos en cascada.
\n\n1. Patrón Fan-Out / Fan-In: procesamiento paralelo de datos
\n\nEste patrón es la base de los cálculos paralelos en Go. Fan-Out (salida en abanico) distribuye la carga entre varias gorutinas, mientras que Fan-In (entrada en abanico) recoge los resultados de vuelta en un solo canal.
\n\nImplementación de Fan-Out
\nImaginemos una tarea: hay que procesar 100 direcciones URL. En lugar de un procesamiento secuencial, lanzamos 10 gorutinas, cada una de las cuales toma una tarea del canal común.
\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 // Simulación de procesamiento\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: lanzamos los workers\n for w := 1; w <= numWorkers; w++ {\n wg.Add(1)\n go worker(w, jobs, results, &wg)\n }\n\n // Enviamos las tareas\n for j := 1; j <= numJobs; j++ {\n jobs <- j\n }\n close(jobs)\n\n // Fan-In: esperamos a que terminen y cerramos results\n go func() {\n wg.Wait()\n close(results)\n }()\n\n // Recogemos los resultados\n for result := range results {\n fmt.Println("Result:", result)\n }\n}\n\n\nPuntos clave:
\n- \n
- El canal
jobssirve como punto de Fan-Out: todos los workers leen de él. \n - El canal
resultses el punto de Fan-In: todos los workers escriben en él. \n - El uso de
sync.WaitGroupgarantiza que cerremos results solo después de que todas las gorutinas hayan terminado. \n
2. Patrón Pipeline: procesamiento de datos en cadena
\n\nPipeline es una cadena de etapas, donde cada etapa procesa los datos y los pasa a la siguiente. En Go esto se implementa mediante una secuencia de canales. El patrón es ideal para procesos ETL, procesamiento de logs o transformación de datos.
\n\nEjemplo: cadena de procesamiento de texto
\n\npackage main\n\nimport "fmt"\n\n// Etapa 1: generación de números\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// Etapa 2: elevar al cuadrado\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// Etapa 3: multiplicar por 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 // Construcción del pipeline\n pipeline := multiply(square(generate(1, 2, 3, 4)), 10)\n\n // Consumo de resultados\n for result := range pipeline {\n fmt.Println(result) // 10, 40, 90, 160\n }\n}\n\n\nVentajas:
\n- \n
- Cada etapa es una unidad independiente \n