Ooms را با semahores متوقف کنید – جامعه dev

GO نوشتن کد همزمان را آسان می کند – فقط به Dosomething () اضافه کنید و خاموش هستید. اما اگر مراقب نیستید ، می توانید خدمات خود را با بسیاری از گوروس ها تحت الشعاع قرار دهید. در اینجا نحوه جلوگیری از بروز تصادفی خود با استفاده از یک semaphore ساده و مؤثر آورده شده است.
Semaphore یک الگوی همزمانی است که مدتها قبل از آن وجود داشته است ، اما اجرای آن با کانال های GO بسیار آسان است.
در علوم کامپیوتر موارد استفاده زیادی برای semaphores وجود دارد ، اما یکی از کاربردی ترین آنها در GO محدود کردن تعداد Goroutines برنامه های برنامه شما است.
گوروتین ها ارزان هستند اما رایگان نیستند. گوروهای بی حد و حصر می توانند به دلیل مشاجره CPU ، استفاده از حافظه فراری (پشته و پشته) و حتی نشت گوروتین منجر به عملکرد تخریب شده شوند.
مثال:
قطعه کد زیر از Go-Chi اقتباس شده است: https://github.com/go-chi/chi. این یک سرور وب ساده با یک نقطه پایانی HTTP ایجاد می کند تا پرونده های بزرگی را که به عنوان درخواست های چند منظوره ارائه شده است ، پردازش کند و از یک semaphore برای محدود کردن همزمانی جهانی استفاده می کند ، اطمینان حاصل می کند که این سرویس هرگز بیش از تعداد ثابت پرونده ها به طور همزمان پردازش نمی کند – صرف نظر از اینکه تعداد کاربران به نقطه پایانی ضربه می زنند.
func main() {
maxGoroutinesEnv := os.Getenv("MAX_GOROUTINES")
maxGoroutines, err := strconv.Atoi(maxGoroutinesEnv)
if err != nil {
log.Fatalf("failed to load MAX_GOROUTINES env var %w", err)
}
// create semaphore
sem := NewSemaphore(maxGoroutines)
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Get("https://dev.to/", func(w http.ResponseWriter, r *http.Request) {
processListOfLargeCustomerProvidedConfigs(w, r, sem)
})
http.ListenAndServe(":3000", r)
}
func processListOfLargeCustomerProvidedConfigs(
w http.ResponseWriter,
r *http.Request,
sem *Semaphore,
) {
err := r.ParseMultipartForm(50 << 20)
if err != nil {
errMsg := fmt.Sprintf("could not parse multipart form: %w", err)
http.Error(w, errMsg, http.StatusBadRequest)
return
}
files := r.MultipartForm.File["configs"]
if len(files) == 0 {
http.Error(w, "no files in request", http.StatusBadRequest)
return
}
for _, f := range files {
// blocks if the semaphore is "full"
sem.Acquire(1)
go func(f *multipart.FileHeader) {
defer sem.Release(1)
// memory and cpu intensive task
processFile(f)
}(f)
}
}
همانطور که در بالا مشاهده می کنید ، هر بار که سعی در ایجاد یک goroutine برای پردازش یک فایل داریم ، اگر سمیفور “پر” باشد و به طور خودکار پس از انتشار یک قطعه کار ، به طور خودکار ادامه می دهیم.
اجرای حداقل semaphore:
در اینجا اجرای کامل مورد استفاده در مثال آورده شده است:
type Semaphore struct {
c chan struct{}
}
func NewSemaphore(w int) *Semaphore {
return &Semaphore{
// create a buffered channel with capacity
// equal to the weight of the semaphore
c: make(chan struct{}, w),
}
}
func (s *Semaphore) Acquire(w int) {
for range w {
// Send an empty struct to the channel.
// Blocks if the channel is full — meaning we've reached our concurrency limit.
// We use `struct{}` to avoid extra allocations.
s.c <- struct{}{}
}
}
func (s *Semaphore) Release(w int) {
// pull the desired amount of work
// out of the semaphore channel
for range w {
<-s.c
}
}
📦 کتابخانه را ترجیح می دهید؟
و اگر می خواهید یک semaphore را حتی سریعتر اجرا کنید ، می توانید یکی را در اینجا پیدا کنید: https://pkg.go.dev/golang.org/x/sync/semaphore