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.