Tunny là một thư viện Golang được sử dụng để tạo và quản lý các pool goroutine, cho phép bạn giới hạn công việc từ bất kỳ số lượng goroutine nào bằng cách sử dụng các API đồng bộ.
Khi công việc của bạn đến từ một số nguồn không đồng bộ nhưng khả năng xử lý song song của bạn bị hạn chế, một pool goroutine cố định rất hữu ích. Ví dụ, khi xử lý các công việc yêu cầu CPU nặng, bạn có thể tạo một pool có kích thước bằng với số lượng CPU.
Cài đặt
go get github.com/Jeffail/tunny
Hoặc sử dụng dep:
dep ensure -add github.com/Jeffail/tunny
Sử dụng
Trong hầu hết các trường hợp, công việc nặng của bạn có thể được biểu diễn bằng một func()
đơn giản, trong trường hợp này bạn có thể sử dụng NewFunc
. Hãy xem cách sử dụng ví dụ của chúng ta về yêu cầu HTTP đến việc đếm 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: Thực hiện một số hoạt động tốn CPU sử dụng 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, "Lỗi nội bộ", http.StatusInternalServerError)
}
defer r.Body.Close()
// Nhập công việc này vào pool của chúng ta. Cuộc gọi này là đồng bộ và sẽ chặn cho đến khi công việc hoàn thành.
result := pool.Process(input)
w.Write(result.([]byte))
})
http.ListenAndServe(":8080", nil)
}
Tunny cũng hỗ trợ timeouts. Bạn có thể thay thế cuộc gọi Process
ở trên bằng đoạn mã sau:
result, err := pool.ProcessTimed(input, time.Second*5)
if err == tunny.ErrJobTimedOut {
http.Error(w, "Yêu cầu đã hết thời gian", http.StatusRequestTimeout)
}
Bạn cũng có thể sử dụng ngữ cảnh yêu cầu (hoặc bất kỳ ngữ cảnh nào khác) để xử lý timeouts và thời hạn. Đơn giản thay thế cuộc gọi Process
bằng đoạn mã sau:
result, err := pool.ProcessCtx(r.Context(), input)
if err == context.DeadlineExceeded {
http.Error(w, "Yêu cầu đã hết thời gian", http.StatusRequestTimeout)
}
Thay đổi Kích thước Pool
Bạn có thể sử dụng SetSize(int)
để thay đổi kích thước của pool Tunny bất kỳ lúc nào.
pool.SetSize(10) // 10 goroutines
pool.SetSize(100) // 100 goroutines
Điều này là an toàn ngay cả khi goroutines khác vẫn đang xử lý.
Goroutine mang theo trạng thái
Đôi khi, mỗi goroutine trong pool Tunny cần có trạng thái quản lý riêng. Trong trường hợp đó, bạn nên triển khai tunny.Worker
, bao gồm các cuộc gọi cho kết thúc, gián đoạn (nếu công việc vượt quá thời gian và không còn cần thiết nữa) và chặn việc phân bổ công việc tiếp theo cho đến khi thỏa mãn điều kiện cụ thể.
Khi tạo ra một pool với kiểu Worker
, bạn cần cung cấp một hàm tạo để tạo ra triển khai tùy chỉnh của bạn:
pool := tunny.New(poolSize, func() Worker {
// TODO: Thực hiện phân bổ trạng thái cho mỗi goroutine ở đây.
return newCustomWorker()
})
Như vậy, Tunny có thể dễ dàng dọn dẹp quá trình tạo ra và hủy triển khai kiểu Worker
khi kích thước pool thay đổi.
Thứ tự
Công việc bị chồng đợi không đảm bảo sẽ được xử lý theo thứ tự. Do hiện tại thực hiện của các kênh và khối lựa chọn, các ngăn xếp công việc bị chồng đợi sẽ được xử lý như một hàng đợi FIFO. Tuy nhiên, hành vi này không phải là một phần của quy định và không nên tin cậy vào nó.