Tunny adalah sebuah pustaka Golang untuk membuat dan mengelola goroutine pools, memungkinkan Anda untuk membatasi pekerjaan dari sejumlah goroutine menggunakan API sinkron.
Ketika pekerjaan Anda berasal dari sejumlah sumber asinkron namun kemampuan pemrosesan paralel terbatas, sebuah goroutine pool tetap sangat berguna. Sebagai contoh, ketika memproses pekerjaan permintaan HTTP yang intensif CPU, Anda dapat membuat sebuah pool dengan ukuran sejumlah CPU.
Instalasi
go get github.com/Jeffail/tunny
Atau, menggunakan dep:
dep ensure -add github.com/Jeffail/tunny
Penggunaan
Untuk kebanyakan kasus, pekerjaan berat Anda dapat direpresentasikan dengan sebuah func()
, dalam hal ini Anda dapat menggunakan NewFunc
. Mari kita lihat bagaimana menggunakan contoh kami dari permintaan HTTP untuk menghitung 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: Melakukan beberapa operasi yang intensif CPU menggunakan 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, "Kesalahan internal", http.StatusInternalServerError)
}
defer r.Body.Close()
// Memasukkan pekerjaan ini ke dalam pool kita. Panggilan ini bersifat sinkron dan akan memblokir hingga pekerjaan selesai.
result := pool.Process(input)
w.Write(result.([]byte))
})
http.ListenAndServe(":8080", nil)
}
Tunny juga mendukung timeout. Anda dapat menggantikan panggilan Process
di atas dengan kode berikut:
result, err := pool.ProcessTimed(input, time.Second*5)
if err == tunny.ErrJobTimedOut {
http.Error(w, "Permintaan kedaluwarsa", http.StatusRequestTimeout)
}
Anda juga dapat menggunakan konteks permintaan (atau konteks lainnya) untuk menangani timeout dan deadline. Cukup gantikan panggilan Process
dengan kode berikut:
result, err := pool.ProcessCtx(r.Context(), input)
if err == context.DeadlineExceeded {
http.Error(w, "Permintaan kedaluwarsa", http.StatusRequestTimeout)
}
Mengubah Ukuran Pool
Anda dapat menggunakan SetSize(int)
untuk mengubah ukuran pool Tunny kapan saja.
pool.SetSize(10) // 10 goroutine
pool.SetSize(100) // 100 goroutine
Ini aman bahkan jika goroutine lain masih dalam proses.
Goroutine yang Memiliki Kepala
Terkadang, setiap goroutine dalam pool Tunny memerlukan keadaan manajemen sendiri. Dalam hal tersebut, Anda harus mengimplementasikan tunny.Worker
, yang mencakup panggilan untuk terminasi, interupsi (jika pekerjaan kedaluwarsa dan tidak lagi diperlukan), dan pemadaman alokasi pekerjaan berikutnya sampai kondisi tertentu terpenuhi.
Ketika membuat sebuah pool dengan tipe Worker
, Anda perlu menyediakan sebuah konstruktor untuk menghasilkan implementasi kustom Anda:
pool := tunny.New(poolSize, func() Worker {
// TODO: Melakukan alokasi keadaan untuk setiap goroutine di sini.
return newCustomWorker()
})
Dengan cara ini, Tunny dapat membersihkan pembuatan dan penghancuran tipe Worker
ketika ukuran pool berubah.
Penyusunan
Pekerjaan yang tertunda tidak dijamin akan diproses secara berurutan. Karena implementasi saat ini dari kanal dan blok pilihan, tumpukan pekerjaan yang tertunda akan diproses sebagai antrian FIFO. Namun, perilaku ini bukan bagian dari spesifikasi dan sebaiknya tidak diandalkan.