es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Cómo hacer más rápido una llamada de API en Golang?

Estoy tratando de subir un montón de archivos utilizando la api de la empresa al servicio de almacenamiento que proporcionan (básicamente a mi cuenta). Tengo muchos archivos, como 40-50 o algo así.
Tengo la ruta completa de los archivos y utilizo os.Open, para que pueda pasar el io.Reader. Intenté usar client.Files.Upload() sin goroutines, pero tardó mucho tiempo en subirlos y decidí usar goroutines. Aquí está la implementación que intenté. Cuando ejecuto el programa, solo sube un archivo que es el que tiene el tamaño más bajo o algo así que espera mucho tiempo. ¿Qué está mal con esto? ¿No es así que cada vez que se ejecuta el bucle for crea una goroutine, continúa su ciclo y crea una para cada file? ¿Cómo hacerlo lo más rápido posible con goroutines?

var filePaths []string
var wg sync.WaitGroup

// Llena el slice de strings con la ruta completa de los archivos.
func fill() {
    filepath.Walk(rootpath, func(path string, info os.FileInfo, err error) error {
        if !info.IsDir() {
            filePaths = append(filePaths, path)
        }
        if err != nil {
            fmt.Println("ERROR:", err)
        }
        return nil
    })
}

func main() {
    fill()

    tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
    oauthClient := oauth2.NewClient(context.TODO(), tokenSource)
    client := putio.NewClient(oauthClient)

    for _, path := range filePaths {
        wg.Add(1)

        go func() {
            defer wg.Done()

            f, err := os.Open(path)
            if err != nil {
                log.Println("err:OPEN", err)
            }

            upload, err := client.Files.Upload(context.TODO(), f, path, 0)
            if err != nil {
                log.Println("error uploading file:", err)
            }
            fmt.Println(upload)
        }()
    }
    wg.Wait()
}
Tags:  , , ,

Answer

  1. Avatar for davy.ai

    El problema con la implementación actual es que todas las goroutines están intentando leer del mismo recurso, lo cual está causando una condición de carrera. Dado que las tareas están limitadas por la E/S, crear demasiadas goroutines puede ralentizar el proceso. En lugar de crear una goroutine para cada archivo, podemos limitar el número de cargas de archivos concurrentes utilizando un canal y un enfoque de grupo de trabajadores. Aquí tenemos una implementación modificada:

    var filePaths []string
    
    // llena la lista de strings con la ruta completa de los archivos
    func fill() {
        filepath.Walk(rootpath, func(path string, info os.FileInfo, err error) error {
            if !info.IsDir() {
                filePaths = append(filePaths, path)
            }
            if err != nil {
                fmt.Println("ERROR:", err)
            }
            return nil
        })
    }
    
    func uploadFile(client *putio.Client, path string, ch chan struct{}) {
        defer func() {<-ch }() // libera una ranura después de cargar el archivo
    
        f, err := os.Open(path)
        if err != nil {
            log.Println("err:OPEN", err)
        }
    
        upload, err := client.Files.Upload(context.TODO(), f, path, 0)
        if err != nil {
            log.Println("error al cargar el archivo:", err)
        }
        fmt.Println(upload)
    }
    
    func main() {
        fill()
    
        tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
        oauthClient := oauth2.NewClient(context.TODO(), tokenSource)
        client := putio.NewClient(oauthClient)
    
        // utiliza un canal para limitar el número de goroutines que pueden ejecutarse simultáneamente
        numWorkers := 10
        ch := make(chan struct{}, numWorkers)
        for _, path := range filePaths {
            ch <- struct{}{} // bloquea hasta que haya una ranura disponible
            go uploadFile(client, path, ch)
        }
        // espera a que todas las cargas de archivos se completen
        for i := 0; i < numWorkers; i++ {
            ch <- struct{}{}
        }
    }
    

    En esta implementación, limitamos el número de goroutines que pueden ejecutarse simultáneamente a numWorkers, que se establece en 10. Utilizamos un chan struct{} para crear un mecanismo similar a un semáforo y limitar el número de goroutines que pueden acceder al recurso compartido (la API de red en este caso). La función uploadFile envuelve la lógica de carga de archivos y libera una ranura en el canal después de que se complete cada carga de archivo.

    Ten en cuenta que aún necesitamos esperar a que todas las cargas de archivos se completen al final de la función main. Logramos esto bloqueando en el canal numWorkers veces.

Comments are closed.