ratelimiter

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 16, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

README

Rate Limiter

Простой и эффективный rate limiter для Go, который позволяет ограничивать количество запросов в единицу времени.

Особенности

  • 🚀 Высокая производительность - использует минимальные блокировки
  • 🔒 Thread-safe - безопасен для использования в многопоточной среде
  • Простой API - всего несколько методов для работы
  • 🎯 Точное ограничение - строго соблюдает установленные лимиты
  • 📊 Мониторинг состояния - возможность получить текущее состояние лимитера

Установка

go get github.com/TRAD3R/ratelimiter

Быстрый старт

package main

import (
    "fmt"
    "time"
    "github.com/TRAD3R/ratelimiter"
)

func main() {
    // Создаем rate limiter: 10 запросов в секунду
    rl := ratelimiter.NewRateLimiter(10, time.Second)
    
    // Проверяем, разрешен ли запрос
    if rl.AllowRequest() {
        fmt.Println("Запрос разрешен")
    } else {
        fmt.Println("Запрос отклонен - превышен лимит")
    }
}

API

NewRateLimiter(rps int, interval time.Duration) *RateLimiter

Создает новый rate limiter с указанным лимитом запросов в секунду и интервалом сброса.

Параметры:

  • rps - максимальное количество запросов за интервал
  • interval - интервал времени для сброса счетчика

Пример:

// 100 запросов в секунду
rl := ratelimiter.NewRateLimiter(100, time.Second)

// 5 запросов в минуту
rl := ratelimiter.NewRateLimiter(5, time.Minute)

// 1000 запросов в 100 миллисекунд
rl := ratelimiter.NewRateLimiter(1000, 100*time.Millisecond)
AllowRequest() bool

Проверяет, разрешен ли запрос согласно установленным лимитам. Если запрос разрешен, увеличивает внутренний счетчик.

Возвращает:

  • true - запрос разрешен
  • false - запрос отклонен (превышен лимит)

Пример:

rl := ratelimiter.NewRateLimiter(2, time.Second)

// Первые два запроса будут разрешены
for i := 0; i < 3; i++ {
    if rl.AllowRequest() {
        fmt.Printf("Запрос %d: разрешен\n", i+1)
    } else {
        fmt.Printf("Запрос %d: отклонен\n", i+1)
    }
}
// Вывод:
// Запрос 1: разрешен
// Запрос 2: разрешен
// Запрос 3: отклонен
CurrentState() (int, time.Duration)

Возвращает текущее состояние rate limiter'а.

Возвращает:

  • int - количество токенов, доступных прямо сейчас
  • time.Duration - время до появления следующего токена (0, если токен уже доступен)

Пример:

rl := ratelimiter.NewRateLimiter(10, time.Second)

// Делаем несколько запросов
rl.AllowRequest()
rl.AllowRequest()

// Проверяем состояние
available, timeUntilNextToken := rl.CurrentState()
fmt.Printf("Доступно токенов: %d, до следующего: %v\n", available, timeUntilNextToken)

Примеры использования

Базовое использование
package main

import (
    "fmt"
    "time"
    "github.com/TRAD3R/ratelimiter"
)

func main() {
    rl := ratelimiter.NewRateLimiter(5, time.Second)
    
    for i := 0; i < 10; i++ {
        if rl.AllowRequest() {
            fmt.Printf("Запрос %d: OK\n", i+1)
        } else {
            fmt.Printf("Запрос %d: RATE LIMITED\n", i+1)
        }
        time.Sleep(100 * time.Millisecond)
    }
}
HTTP middleware
package main

import (
    "net/http"
    "time"
    "github.com/TRAD3R/ratelimiter"
)

var rl = ratelimiter.NewRateLimiter(100, time.Minute) // 100 запросов в минуту

func rateLimitMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if !rl.AllowRequest() {
            http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
            return
        }
        next.ServeHTTP(w, r)
    })
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello, World!"))
    })
    
    http.ListenAndServe(":8080", rateLimitMiddleware(mux))
}
Мониторинг состояния
package main

import (
    "fmt"
    "time"
    "github.com/TRAD3R/ratelimiter"
)

func main() {
    rl := ratelimiter.NewRateLimiter(3, 2*time.Second)
    
    // Мониторим состояние каждые 500мс
    go func() {
        ticker := time.NewTicker(500 * time.Millisecond)
        defer ticker.Stop()
        
        for range ticker.C {
            available, timeLeft := rl.CurrentState()
            fmt.Printf("Состояние: %d/%d токенов доступно, до следующего: %v\n",
                available, 3, timeLeft.Round(time.Millisecond))
        }
    }()
    
    // Делаем запросы
    for i := 0; i < 10; i++ {
        rl.AllowRequest()
        time.Sleep(300 * time.Millisecond)
    }
    
    time.Sleep(3 * time.Second)
}

Производительность

Rate limiter оптимизирован для высокой производительности:

  • Блокировки: Использует минимальные блокировки только при необходимости
  • Память: Низкое потребление памяти (всего несколько полей)
  • CPU: Минимальные вычисления при каждом запросе
Бенчмарки
BenchmarkAllowRequest-22              29049027    38.81 ns/op
BenchmarkConcurrentAllowRequest-22     9769224   121.4 ns/op
BenchmarkCurrentState-22              27427677    40.28 ns/op

Тестирование

Проект имеет 100% покрытие тестами:

# Запуск всех тестов
go test -v

# Запуск с покрытием
go test -cover

# Запуск бенчмарков
go test -bench=.

# Запуск примеров
go test -run Example
Типы тестов
  • Unit тесты - тестирование отдельных функций
  • Интеграционные тесты - тестирование взаимодействия компонентов
  • Concurrency тесты - проверка thread-safety
  • Edge case тесты - граничные случаи
  • Performance тесты - бенчмарки

Лицензия

Apache License 2.0

Вклад в проект

Приветствуются pull request'ы и issue'ы! Пожалуйста, убедитесь, что:

  1. Код соответствует стилю проекта
  2. Все тесты проходят
  3. Добавлены тесты для новой функциональности
  4. Обновлена документация при необходимости

Changelog

v1.0.0
  • Первоначальный релиз
  • Базовая функциональность rate limiting
  • Thread-safe реализация
  • Полное покрытие тестами

Documentation

Overview

Package ratelimiter provides a simple and efficient rate limiter for Go applications. It allows limiting the number of requests per time interval with thread-safe operations.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type RateLimiter

type RateLimiter struct {
	// contains filtered or unexported fields
}

RateLimiter implements a token bucket rate limiter that allows a maximum number of requests per time interval. Tokens are refilled continuously (not in bursts at fixed boundaries), so it never allows more than 'rps' requests within any sliding window of length 'interval'. It is thread-safe and can be used concurrently from multiple goroutines.

func NewRateLimiter

func NewRateLimiter(rps int, interval time.Duration) *RateLimiter

NewRateLimiter creates a new rate limiter with the specified requests per second limit and reset interval. The rate limiter will allow up to 'rps' requests within any sliding window of length 'interval'. The bucket starts full, so an initial burst of up to 'rps' requests is allowed immediately.

Example
package main

import (
	"fmt"
	"time"

	"github.com/TRAD3R/ratelimiter"
)

func main() {
	// Создаем rate limiter с лимитом 10 запросов в секунду
	rl := ratelimiter.NewRateLimiter(10, time.Second)

	// Проверяем, разрешен ли запрос
	if rl.AllowRequest() {
		fmt.Println("Request allowed")
	} else {
		fmt.Println("Request denied")
	}
}
Output:
Request allowed

func (*RateLimiter) AllowRequest

func (rl *RateLimiter) AllowRequest() bool

AllowRequest checks if a request is allowed according to the rate limit. It returns true if the request is allowed and consumes one token, or false if the rate limit has been exceeded.

Example
package main

import (
	"fmt"
	"time"

	"github.com/TRAD3R/ratelimiter"
)

func main() {
	// Создаем rate limiter с лимитом 2 запроса в 100 миллисекунд
	rl := ratelimiter.NewRateLimiter(2, 100*time.Millisecond)

	// Первые два запроса должны быть разрешены
	for i := 0; i < 2; i++ {
		if rl.AllowRequest() {
			fmt.Printf("Request %d: allowed\n", i+1)
		}
	}

	// Третий запрос должен быть отклонен
	if !rl.AllowRequest() {
		fmt.Println("Request 3: denied (rate limit exceeded)")
	}

	// Ждем, пока накопится токен
	time.Sleep(110 * time.Millisecond)

	// После пополнения запрос снова разрешен
	if rl.AllowRequest() {
		fmt.Println("Request after refill: allowed")
	}
}
Output:
Request 1: allowed
Request 2: allowed
Request 3: denied (rate limit exceeded)
Request after refill: allowed

func (*RateLimiter) CurrentState

func (rl *RateLimiter) CurrentState() (int, time.Duration)

CurrentState returns the current state of the rate limiter. It returns the number of tokens currently available (rounded down) and the time remaining until the next token becomes available (0 if a token is already available).

Example
package main

import (
	"fmt"
	"time"

	"github.com/TRAD3R/ratelimiter"
)

func main() {
	rl := ratelimiter.NewRateLimiter(5, time.Second)

	// Делаем несколько запросов
	rl.AllowRequest()
	rl.AllowRequest()

	// Проверяем текущее состояние
	available, wait := rl.CurrentState()
	fmt.Printf("Available tokens: %d\n", available)
	if wait == 0 {
		fmt.Println("Rate limiter is not blocking")
	}
}
Output:
Available tokens: 3
Rate limiter is not blocking

func (*RateLimiter) GetInterval

func (rl *RateLimiter) GetInterval() time.Duration

GetInterval returns the configured reset interval. This method is primarily for testing purposes.

func (*RateLimiter) GetRPS

func (rl *RateLimiter) GetRPS() int

GetRPS returns the configured requests per second limit. This method is primarily for testing purposes.

func (*RateLimiter) GetRetryAfter

func (rl *RateLimiter) GetRetryAfter() time.Time

GetRetryAfter returns the time when the next token will become available. It returns the current time if a token is already available. This method is primarily for testing purposes.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL