Creación de un Web Server con Worker Pool en Go
Clase 28 de 30 • Curso de Go Intermedio: Programación Orientada a Objetos y Concurrencia
Contenido del curso
- 5

Programación Orientada a Objetos en Go: Análisis y Comparativa
06:51 - 6

Equivalente de Clases en Go: Uso de Structs y Propiedades
06:41 - 7

Métodos en Structs: Implementación de Receiver Functions en Go
08:54 - 8

Implementación de Constructores en Go: Ejemplos y Mejores Prácticas
10:48 - 9

Herencia y Composición en Programación Orientada a Objetos
14:50 - 10

Interfaces y Polimorfismo en TypeScript y Go
12:35 - 11

Patrón Abstract Factory: Interfaces y Polimorfismo en Go
07:37 - 12

Implementación del Patrón Factory en Go para Notificaciones
10:10 - 13

Funciones Anónimas en Go: Uso y Consideraciones Prácticas
06:56 - 14

Funciones Variádicas y Retornos con Nombre en Go
10:51
- 22

Canales con y sin buffer en Go: diferencias y uso práctico
06:23 - 23

Sincronización de Rutinas en Go con Wait Group
07:26 - 24

"Uso de Canales con Buffer como Semáforos en Go"
09:00 - 25

Manejo de Canales de Lectura y Escritura en Go
09:34 - 26

Concurrencia en Go: Creación de Worker Pools y Fibonacci
11:23 - 27

Multiplexación de Canales en Go con Select y Case
07:13
¿Cómo crear un servidor web para gestionar trabajos con Go?
Crear un servidor web que gestione trabajos utilizando Go puede ser un proyecto fascinante y muy educativo. Este análisis te guiará paso a paso para generar un "worker pool" que te permita procesar trabajos en paralelo de manera eficiente. Aquí exploraremos cómo estructurar el código y los conceptos clave detrás de esta implementación.
¿Cuál es la estructura principal de nuestro proyecto?
El punto de partida es crear una carpeta para el proyecto, dentro de la cual vamos a definir un archivo principal llamado main.go. El primer paso es definir el paquete principal y comenzar a diseñar los structs fundamentales para nuestro proyecto.
package main
import (
"time"
)
// Definición del struct Job
type Job struct {
Name string
Delay time.Duration
Number int
}
// Definición del struct Worker
type Worker struct {
ID int
JobQueue chan Job
WorkerPool chan chan Job
QuitChan chan bool
}
// Definición del struct Dispatcher
type Dispatcher struct {
WorkerPool chan chan Job
MaxWorkers int
JobQueue chan Job
}
Aquí hemos definido tres componentes principales: Job, que representa el trabajo a procesar; Worker, que ejecuta los trabajos; y Dispatcher, que se encarga de distribuir los trabajos entre los workers disponibles.
¿Cómo implementar constructores para workers y dispatchers?
Para poner en práctica los patrones de diseño recomendados en Go, es útil implementar constructores que inicialicen nuestros structs.
func NewWorker(id int, workerPool chan chan Job) Worker {
return Worker{
ID: id,
JobQueue: make(chan Job),
WorkerPool: workerPool,
QuitChan: make(chan bool),
}
}
func NewDispatcher(maxWorkers int, jobQueue chan Job) *Dispatcher {
workerPool := make(chan chan Job, maxWorkers)
return &Dispatcher{
WorkerPool: workerPool,
MaxWorkers: maxWorkers,
JobQueue: jobQueue,
}
}
Utilizar constructores tiene ventajas significativas, como asegurarnos de que todas las propiedades estén correctamente inicializadas.
¿Cómo funcionan los workers y el proceso de dispatch?
Los workers son el núcleo del procesamiento paralelo. Implementaremos métodos para que continuamente escuchen trabajos y los procesen adecuadamente.
func (w Worker) Start() {
go func() {
for {
w.WorkerPool <- w.JobQueue
select {
case job := <-w.JobQueue:
result := fibonacci(job.Number)
time.Sleep(job.Delay)
fmt.Printf("Worker %d ha finalizado con resultado %d\n", w.ID, result)
case <-w.QuitChan:
fmt.Printf("Worker %d ha sido detenido\n", w.ID)
return
}
}
}()
}
func (w Worker) Stop() {
go func() {
w.QuitChan <- true
}()
}
Implementamos tanto la ejecución indefinida de los workers como el mecanismo para detenerlos. En cuanto al dispatcher, su objetivo es asignar trabajos a los workers:
func (d *Dispatcher) Dispatch() {
go func() {
for {
select {
case job := <-d.JobQueue:
go func(job Job) {
workerJobQueue := <-d.WorkerPool
workerJobQueue <- job
}(job)
}
}
}()
}
¿Cómo ejecutar la serie Fibonacci de forma eficiente?
Para el cálculo de la serie de Fibonacci, utilizamos recursión, un método que ya fue analizado previamente y es ampliamente conocido por su elegancia y simplicidad.
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
¿Qué sigue después de estructurar y programar workers y dispatcher?
Hasta este punto, hemos construido los cimientos de nuestro servidor web en Go. En futuras implementaciones, la integración de una interfaz web para enviar trabajos al dispatcher ampliará las capacidades de este proyecto, permitiendo que múltiples usuarios interactúen y aprovechen este sistema eficiente. No dudes en explorar más allá de lo aprendido aquí y aprovecha la oportunidad de ampliar tu comprensión sobre goroutines, canales y la programación concurrente en Go. ¡Buena suerte y sigue aprendiendo!