Вопрос проверяет понимание паттерна cleanup в rate limiter и передачи контекста для корректного управления ресурсами.
Функция cleanup в rate limiter отвечает за освобождение ресурсов, таких как таймеры, каналы или счетчики запросов. Ее следует запускать в отдельной горутине или с помощью defer, чтобы гарантировать выполнение даже при ошибках. Например, в Go можно использовать defer для вызова cleanup после завершения работы лимитера.
Контекст передается в rate limiter через аргумент функции или через замыкание. Это позволяет лимитеру знать о времени жизни запроса и отменять операции при превышении лимита. В Go контекст обычно передается первым аргументом в функции, такие как Allow или Wait.
type RateLimiter struct {
mu sync.Mutex
limit int
counter int
ctx context.Context
cancel context.CancelFunc
}
func NewRateLimiter(limit int) *RateLimiter {
ctx, cancel := context.WithCancel(context.Background())
rl := &RateLimiter{
limit: limit,
ctx: ctx,
cancel: cancel,
}
go rl.cleanup()
return rl
}
func (rl *RateLimiter) cleanup() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
rl.mu.Lock()
rl.counter = 0
rl.mu.Unlock()
case <-rl.ctx.Done():
return
}
}
}
func (rl *RateLimiter) Allow(ctx context.Context) bool {
select {
case <-ctx.Done():
return false
default:
}
rl.mu.Lock()
defer rl.mu.Unlock()
if rl.counter >= rl.limit {
return false
}
rl.counter++
return true
}Cleanup следует запускать в отдельной горутине для фонового сброса счетчика, а контекст передавать через аргумент для управления отменой. Это обеспечивает корректное освобождение ресурсов и адаптацию к внешним условиям.