¿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 Jobtype Job struct{ Name string Delay time.Duration
Number int}// Definición del struct Workertype Worker struct{ ID int JobQueue chan Job
WorkerPool chanchan Job
QuitChan chanbool}// Definición del struct Dispatchertype Dispatcher struct{ WorkerPool chanchan 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.
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(){gofunc(){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(){gofunc(){ 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:
¿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.
funcfibonacci(n int)int{if n <=1{return n
}returnfibonacci(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!
package main
import("fmt""time")// Job represents a job to be executed, with a name and a number and a delaytype Job struct {Name string // name of the jobDelay time.Duration// delay between each jobNumber int // number to calculate on the fibonacci sequence}// Worker will be our concurrency-friendly workertype Worker struct {Id int // id of the workerJobQueue chan Job// Jobs to be processedWorkerPool chan chan Job// Pool of workersQuit chan bool // Quit worker}// Dispatcher is a dispatcher that will dispatch jobs to workerstype Dispatcher struct {WorkerPool chan chan Job// Pool of workersMaxWorkers int // Maximum number of workersJobQueue chan Job// Jobs to be processed}// NewWorker returns a new Worker with the provided id and workerpoolfunc NewWorker(id int, workerPool chan chan Job)*Worker{return&Worker{Id: id,WorkerPool: workerPool,JobQueue:make(chan Job),// create a job queueQuit:make(chan bool),// Channel to end jobs}}// Start method starts all workersfunc(w Worker)Start(){ go func(){for{ w.WorkerPool<- w.JobQueue// add job to pool// Multiplexing select {casejob:=<-w.JobQueue:// get job from queue fmt.Printf("worker%d: started %s, %d\n", w.Id, job.Name, job.Number)fib:=Fibonacci(job.Number) time.Sleep(job.Delay) fmt.Printf("worker%d: finished %s, %d with result %d\n", w.Id, job.Name, job.Number, fib)case<-w.Quit:// quit if worker is told to do so fmt.Printf("Worker with id %d Stopped\n", w.Id)return}}}()}// Stop method stop the workerfunc(w Worker)Stop(){ go func(){ w.Quit<-true}()}// Fibonacci calculates the fibonacci sequencefunc Fibonacci(n int) int {if n <=1{return n
}returnFibonacci(n-1)+Fibonacci(n-2)}// NewDispatcher returns a new Dispatcher with the provided maxWorkersfunc NewDispatcher(jobQueue chan Job, maxWorkers int)*Dispatcher{pool:=make(chan chan Job, maxWorkers)return&Dispatcher{WorkerPool: pool,MaxWorkers: maxWorkers,JobQueue: jobQueue,}}// Dispatch will dispatch jobs to workersfunc(d *Dispatcher)dispatch(){for{ select {casejob:=<-d.JobQueue:// get job from queue// Asign the job to a worker go func(){jobChannel:=<-d.WorkerPool// get worker from pool jobChannel <- job // Workers will read from this channel}()}}}func(d *Dispatcher)Run(){fori:=0; i < d.MaxWorkers; i++{worker:=NewWorker(i+1, d.WorkerPool) worker.Start()} go d.dispatch()}
thanks bro!
Un pequeño resumen de lo que pude entender, espero los ayude
El dispatcher recibe todos los jobs, se puede decir que es como el componente global
Cada worker tiene su canal de jobs, y saben cual es el canal del disptacher, es decir el workerpool es el mismo canal para todos los workers.
Cada worker esta enviando su canal al canal del dispatcher
En la medida que el dispatcher recibe jobs este los va repartiendo entre los workers a través de sus canales
package project
import("fmt""time")// Estructura de la tareas de procesartype Job struct {Name string //Nombre de la tareaDelay time.Duration//Tiempo de esperaNumber int // Numero a procesar}type Worker struct {Id int // id del WorkerJobQueue chan Job// Canal de tareas del workerWorkerPool chan chan Job//Canal de canales de tareas, este canal se comparte entre todos los workersQuitChan chan bool //Canal para parar al worker}type Dispatcher struct {WorkerPool chan chan Job//Canal de canales de tareas, este se les pasa a cada worker nuevoMaxWorkers int //cantidad maxima de workersJobQueue chan Job//Canal de tareas, se puede ver como un canal global de tareas que despues se reparten entre workers}func NewWorker(id int, workerPool chan chan Job)*Worker{return&Worker{Id: id,//Se asigna un idWorkerPool: workerPool,//Se le indica el canal donde tiene quie agregar su canal de tareasJobQueue:make(chan Job),//Canal de tareas del workerQuitChan:make(chan bool),//Canal para parar al worker}}func(w Worker)Start(){//Se inicia de manera concurrente un ciclo sin fin go func(){for{//Al worker pool se manda el canal de worker, este se manda cada vez iteracion, es decir cuando el worker termino de hacer un jobs w.WorkerPool<- w.JobQueue//Se multiplexean los canales del worker select {casejob:=<-w.JobQueue://Si se recibe un job en el canal de tareas del worker se ejecuta fmt.Printf("Worker with id %d Started\n", w.Id)fib:=Fibonacci(job.Number) time.Sleep(job.Delay) fmt.Printf("Worker with id %d Finishes with result %d\n", w.Id, fib)case<-w.QuitChan://Si se recibe un job en el canal de salida se para el worker (lo sca del ciclo) fmt.Printf("Worker with id %d Stopped\n", w.Id)return}}}()}//La funcion stop manda un true al canl de salida del workerfunc(w Worker)Stop(){ go func(){ w.QuitChan<-true}()}//El dispatcher cuenta con el el canal global de jobs y un canal de todos los canales de los workersfunc NewDispatcher(jobQueue chan Job, maxWorkers int)*Dispatcher{worker:=make(chan chan Job, maxWorkers)return&Dispatcher{JobQueue: jobQueue,MaxWorkers: maxWorkers,WorkerPool: worker,}}func(d *Dispatcher)Dispatch(){//Inicia de manera indefinidad a mandar jobs a los canales que se van recibiendo en el canal de caneles de jobsfor{ select {casejob:=<-d.JobQueue: go func(){workerJobQueue:=<-d.WorkerPool workerJobQueue <- job
}()}}}func Fibonacci(n int) int {if n <=1{return n
}returnFibonacci(n-1)+Fibonacci(n-2)}
Gracias totales 🙌🏼
Gracias
La verdad es que hasta ahora venía bien el curso pero acá faltó un trabajo previo de explicación de qué es un Worker Pool y conceptualmente qué vamos a querer implementar antes de empezar a picar código.
De acuerdo contigo, me han quedado muchas dudas, estoy revisando otras fuentes para entender el proceso.
Tal cual, la implementation del canal de canales tuve que ir a otras fuentes para informarme mejor.
lo unico que no me gusta es que va explicando a la velicidad de la luz
Y yo colocando el video *2 JAJAJAJ
La mayoría de los vídeos los veo mínimo a 1.25 y a este le tengo que poner pausa :'(
Explico lo que pasó en esta clase con ejemplo más criollo:
Tenemos una empresa de buses. En esta empresa tenemos:
Conductores (workers)
Rutas (Job)
Vendedores de tiquetes de bus (Dispacher)
Cada conductor (Conductor) tiene una lista de rutas (Job) que tiene que hacer.
El vendedor de tiquetes (Dispatcher), tiene una lista de todas las rutas que se ofrecen.
Cuando un cliente (Nosotros/Usuario) se acerca por un tiquete (Job), le compra al vendedor de tiquetes (Dispatcher), un tiquete para ir a otra ciudad.
Este vendedor, ya tiene una lista de tiquetes por realizar (Job), y le envía la ruta a realizar al primer conductor (Worker) que este disponible.
Exelente aporte, creo que el profe debió haber aplicado esta técnica, un ejemplo más cotidiano, heladeria, empresa de buses, restaurante con meseros etc.
Nestor, creo que debes explicar paso a paso con detenimiento este video. Ya lo vi 3 veces y no entiendo.
En mi caso, el tema de "canales dentro canales" no entiendo nada.
Ademas que en todos lados dice WorkerPool... mas engorroso
se debe dividir en al menos 2 partes éste video
Sinceramente, me parece que no explica muy bien.
Deberia explicar el porque lo que esta haciendo se hace de sa manera
Amigos,
¿Qué realiza las siguientes líneas de código?
go func(){workerJobQueue:=<-d.WorkerPool workerJobQueue <- job
}()
La funcion Stop, en que momento se esta implementando? veo que fue declarada pero no invocada.
no entendi, muy bien lo del canal de canales, el porque se debe implementar asi?
El siguiente recurso fue muy útil para mi, da un mejor panorama al chan chan job... ¡Ánimos!
(Como el recurso no esta en https no lo dejo como enlace)
h ttp://tleyden.github.io/blog/2013/11/23/understanding-chan-chans-in-go/
Esta clase se entendería mucho mejor si uno supiera el objetivo de todo ese código que el instructor está digitando. Así al menos uno le vería sentidos.
Explica las cosas, pero no el porque de eso.
esta explicando linea por linea el codigo que esta haciendo, pero no explica porque se hace de esa manera. :( :(
parece un código hecho por el chatGPT
Aqui inicia el verdadero curso la vdd me dormi entre clases aqui esta lo chido
con todo respeto pero no entiendo absolutamente nada, no es que no entienda el tema de lo chan of chan, o las routines, pero si es un "Proyecto" no deberiamos hacer mas cercano a lo real? como es la arquitectura de los archivos, donde debe ir cada cosa, por que se debe escribir cada cosa y en donde.. yo dudo que en un Proyecto real tengamos que hacer este desorden. pense que ibamos a hacer algo mas "Real" que podamos entender. Fibonnacci y un contador no funciona para un "Proyecto" esto es un ejercicio mas elaborado, por favor deberiamos repasar esta clase por que en realidad no explica nada que no halla explicado antes.
w.WorkerPool<- w.JobQueue// add job to pool8
Esto significa que estamos escribiendo en el WorkerPool un Job para que lo procese?
muy poco didáctico, debería de explicar un ejemplo más practico y no una serie fibonacci
Esta muy interesante esta clase lo unico es que siento que fue muy rapido y no estafa enfocado tanto para explicar con calma todo el proceso que estaba haciendo es como explicar el codigo y no el objetivo que quiere hacer
muy poco didáctico, debería de explicar un ejemplo más practico y no con una serie fibonacci
Este video le hizo CHAN CHAN a mi cabeza, litera chan channnnn y boom!!!!