تُعد مكتبة Tunny مكتبة برمجية بلغة Golang تُستخدم لإنشاء وإدارة حمامات الجملة وتسمح لك بتقييد العمل من أي عدد من الجملة باستخدام واجهات برمجية متزامنة.

عندما يكون العمل الخاص بك مشتقًا من أعداد لامتناهية من المصادر الغير متزامنة ولكن قدرتك على المعالجة المتوازية محدودة، يكون حمام الجملة الثابت بعدد الجملة مهمًا للغاية. على سبيل المثال، عند معالجة وظائف طلبات HTTP مكثفة للمعالجة المركزية، يمكنك إنشاء حمام بحجم عدد وحدات المعالجة المركزية.

التثبيت

go get github.com/Jeffail/tunny

بديلًا، باستخدام dep:

dep ensure -add github.com/Jeffail/tunny

الاستخدام

في معظم الحالات، يمكن تمثيل عملك الشاق بواسطة func() بسيطة، وفي هذه الحالة يمكنك استخدام NewFunc. دعنا نرى كيفية استخدام مثالنا لطلبات HTTP لعمليات العد للوحدات المعالجة المركزية:

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: Perform some CPU-intensive operations using 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, "Internal error", http.StatusInternalServerError)
		}
		defer r.Body.Close()

		// استيراد هذا العمل إلى حمامنا. هذا الاستدعاء متزامن وسيحجب حتى اكتمال الوظيفة.
		result := pool.Process(input)

		w.Write(result.([]byte))
	})

	http.ListenAndServe(":8080", nil)
}

تدعم Tunny أيضًا المهلات الزمنية. يمكنك استبدال الاستدعاء أعلاه Process بالشفرة التالية:

result, err := pool.ProcessTimed(input, time.Second*5)
if err == tunny.ErrJobTimedOut {
	http.Error(w, "Request timed out", http.StatusRequestTimeout)
}

يمكنك أيضًا استخدام سياق الطلب (أو أي سياق آخر) للتعامل مع المهلات الزمنية والمواعيد النهائية. ببساطة استبدل استدعاء Process بالشفرة التالية:

result, err := pool.ProcessCtx(r.Context(), input)
if err == context.DeadlineExceeded {
	http.Error(w, "Request timed out", http.StatusRequestTimeout)
}

تعديل حجم الحمام

يمكنك استخدام SetSize(int) لتغيير حجم حمام Tunny في أي وقت.

pool.SetSize(10) // 10 جمل
pool.SetSize(100) // 100 جمل

هذا آمن حتى إذا كانت جمل أخرى لا تزال في المعالجة.

جملة الجملة

في بعض الأحيان، تحتاج كل جملة في حمام Tunny إلى حالتها الخاصة بالإدارة. في هذه الحالة، يجب عليك تنفيذ tunny.Worker، والذي يتضمن استدعاءات للإنهاء والانقطاع (إذا كانت الوظيفة تستغرق وقتًا طويلاً ولم تعد مطلوبة) وحظر تخصيص الوظيفة التالية حتى يتم تلبية شرط معين.

عند إنشاء حمام بنوع Worker، يجب توفير منشئ لإنشاء تنفيذك المخصص:

pool := tunny.New(poolSize, func() Worker {
	// TODO: Perform state allocation for each goroutine here.
	return newCustomWorker()
})

بهذه الطريقة، يمكن لـ Tunny تنظيف إنشاء وتدمير نوع Worker عند تغيير حجم الحمام.

الترتيب

لا يضمن معالجة الوظائف المعلقة في الترتيب. نظرًا لتنفيذ القنوات وكتل الاختيار الحالي، سيتم معالجة مكدسات الوظائف المعلقة كطابور انتظار FIFO. ومع ذلك، فإن هذا السلوك ليس جزءًا من المواصفات ولا يجب الاعتماد عليه.