Tunny è una libreria Golang per la creazione e la gestione di pool di goroutine, che ti consente di limitare il lavoro da qualsiasi numero di goroutine utilizzando API sincrone.
Quando il tuo lavoro proviene da un numero arbitrario di fonti asincrone ma la tua capacità di elaborazione in parallelo è limitata, un pool di goroutine fisso è estremamente utile. Ad esempio, quando si elaborano lavori di richieste HTTP intensivi per la CPU, è possibile creare un pool delle dimensioni del numero di CPU.
## Installazione
```shell
go get github.com/Jeffail/tunny
In alternativa, utilizzando dep:
dep ensure -add github.com/Jeffail/tunny
Utilizzo
Per la maggior parte dei casi, il lavoro pesante può essere rappresentato da un semplice func()
, nel qual caso è possibile utilizzare NewFunc
. Vediamo come utilizzare il nostro esempio di richieste HTTP al conteggio della CPU:
package main
import (
"io/ioutil"
"net/http"
"runtime"
"github.com/Jeffail/tunny"
)
func main() {
numCPUs := runtime.NumCPU()
pool := tunny.NewFunc(numCPUs, func(payload interface{}) interface{} {
var result []byte
// TODO: Eseguire alcune operazioni intensive per la CPU utilizzando il payload
return result
})
defer pool.Close()
http.HandleFunc("/work", func(w http.ResponseWriter, r *http.Request) {
input, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Errore interno", http.StatusInternalServerError)
}
defer r.Body.Close()
// Importare questo lavoro nel nostro pool. Questa chiamata è sincrona e bloccante fino al completamento del lavoro.
result := pool.Process(input)
w.Write(result.([]byte))
})
http.ListenAndServe(":8080", nil)
}
Tunny supporta anche i timeout. Puoi sostituire la chiamata Process
sopra con il seguente codice:
result, err := pool.ProcessTimed(input, time.Second*5)
if err == tunny.ErrJobTimedOut {
http.Error(w, "Richiesta scaduta", http.StatusRequestTimeout)
}
Puoi anche utilizzare il contesto della richiesta (o qualsiasi altro contesto) per gestire i timeout e le scadenze. Sostituisci semplicemente la chiamata Process
con il seguente codice:
result, err := pool.ProcessCtx(r.Context(), input)
if err == context.DeadlineExceeded {
http.Error(w, "Richiesta scaduta", http.StatusRequestTimeout)
}
Modifica delle dimensioni del pool
Puoi utilizzare SetSize(int)
per modificare le dimensioni del pool Tunny in qualsiasi momento.
pool.SetSize(10) // 10 goroutine
pool.SetSize(100) // 100 goroutine
Questo è sicuro anche se altre goroutine stanno ancora elaborando.
Goroutine con stato
A volte, ogni goroutine nel pool Tunny ha bisogno del proprio stato di gestione. In tal caso, è necessario implementare tunny.Worker
, che include chiamate per la terminazione, l'interruzione (se un lavoro scade e non è più necessario) e il blocco dell'allocazione del prossimo lavoro fino al verificarsi di una determinata condizione.
Quando si crea un pool con il tipo Worker
, è necessario fornire un costruttore per generare la propria implementazione personalizzata:
pool := tunny.New(poolSize, func() Worker {
// TODO: Eseguire l'allocazione dello stato per ciascuna goroutine qui.
return newCustomWorker()
})
In questo modo, Tunny può gestire la creazione e la distruzione del tipo Worker
quando cambia la dimensione del pool.
Ordinamento
I lavori in coda non sono garantiti di essere elaborati in ordine. A causa dell'attuale implementazione dei canali e dei blocchi select, le pile di lavori in coda verranno elaborare come una coda FIFO. Tuttavia, questo comportamento non fa parte delle specifiche e non dovrebbe essere affidabile.