ratelimiter

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2025 License: Apache-2.0 Imports: 2 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 - время до сброса счетчика

Пример:

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

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

// Проверяем состояние
requests, timeUntilReset := rl.CurrentState()
fmt.Printf("Использовано запросов: %d, до сброса: %v\n", requests, timeUntilReset)

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

Базовое использование
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 {
            requests, timeLeft := rl.CurrentState()
            fmt.Printf("Состояние: %d/%d запросов, до сброса: %v\n", 
                requests, 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              26956904    43.04 ns/op
BenchmarkConcurrentAllowRequest-22     9799383   111.5 ns/op
BenchmarkCurrentState-22              44034135    27.94 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. 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 each 'interval' time window.

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 increments the internal counter, 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 reset: allowed")
	}
}
Output:
Request 1: allowed
Request 2: allowed
Request 3: denied (rate limit exceeded)
Request after reset: 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 requests made in the current interval and the time remaining until the next reset.

Example
package main

import (
	"fmt"
	"time"

	"github.com/TRAD3R/ratelimiter"
)

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

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

	// Проверяем текущее состояние
	requests, timeUntilReset := rl.CurrentState()
	fmt.Printf("Current requests: %d\n", requests)
	if timeUntilReset > 0 {
		fmt.Println("Rate limiter is active")
	}
}
Output:
Current requests: 2
Rate limiter is active

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 rate limiter will reset. 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