v0.1.8 rate limiter added, improved http router, config templates added, docs updated

This commit is contained in:
2025-07-08 00:57:21 -04:00
parent acee9cb7f3
commit d7f2c0d54d
15 changed files with 1373 additions and 251 deletions

View File

@ -5,6 +5,8 @@ import (
"fmt"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/valyala/fasthttp"
)
@ -13,12 +15,19 @@ type HTTPRouter struct {
service *Service
servers map[int]*routerServer // port -> server
mu sync.RWMutex
// Statistics
startTime time.Time
totalRequests atomic.Uint64
routedRequests atomic.Uint64
failedRequests atomic.Uint64
}
func NewHTTPRouter(service *Service) *HTTPRouter {
return &HTTPRouter{
service: service,
servers: make(map[int]*routerServer),
service: service,
servers: make(map[int]*routerServer),
startTime: time.Now(),
}
}
@ -34,24 +43,31 @@ func (r *HTTPRouter) RegisterStream(stream *LogStream) error {
if !exists {
// Create new server for this port
rs = &routerServer{
port: port,
routes: make(map[string]*LogStream),
port: port,
routes: make(map[string]*LogStream),
router: r,
startTime: time.Now(),
}
rs.server = &fasthttp.Server{
Handler: rs.requestHandler,
DisableKeepalive: false,
StreamRequestBody: true,
CloseOnShutdown: true, // Ensure connections close on shutdown
}
r.servers[port] = rs
// Start server in background
go func() {
addr := fmt.Sprintf(":%d", port)
fmt.Printf("[ROUTER] Starting server on port %d\n", port)
if err := rs.server.ListenAndServe(addr); err != nil {
// Log error but don't crash
fmt.Printf("Router server on port %d failed: %v\n", port, err)
fmt.Printf("[ROUTER] Server on port %d failed: %v\n", port, err)
}
}()
// Wait briefly to ensure server starts
time.Sleep(100 * time.Millisecond)
}
r.mu.Unlock()
@ -71,6 +87,7 @@ func (r *HTTPRouter) RegisterStream(stream *LogStream) error {
}
rs.routes[pathPrefix] = stream
fmt.Printf("[ROUTER] Registered stream '%s' at path '%s' on port %d\n", stream.Name, pathPrefix, port)
return nil
}
@ -78,18 +95,27 @@ func (r *HTTPRouter) UnregisterStream(streamName string) {
r.mu.RLock()
defer r.mu.RUnlock()
for _, rs := range r.servers {
for port, rs := range r.servers {
rs.routeMu.Lock()
for path, stream := range rs.routes {
if stream.Name == streamName {
delete(rs.routes, path)
fmt.Printf("[ROUTER] Unregistered stream '%s' from path '%s' on port %d\n",
streamName, path, port)
}
}
// Check if server has no more routes
if len(rs.routes) == 0 {
fmt.Printf("[ROUTER] No routes left on port %d, considering shutdown\n", port)
}
rs.routeMu.Unlock()
}
}
func (r *HTTPRouter) Shutdown() {
fmt.Println("[ROUTER] Starting router shutdown...")
r.mu.Lock()
defer r.mu.Unlock()
@ -98,10 +124,46 @@ func (r *HTTPRouter) Shutdown() {
wg.Add(1)
go func(p int, s *routerServer) {
defer wg.Done()
fmt.Printf("[ROUTER] Shutting down server on port %d\n", p)
if err := s.server.Shutdown(); err != nil {
fmt.Printf("Error shutting down router server on port %d: %v\n", p, err)
fmt.Printf("[ROUTER] Error shutting down server on port %d: %v\n", p, err)
}
}(port, rs)
}
wg.Wait()
fmt.Println("[ROUTER] Router shutdown complete")
}
func (r *HTTPRouter) GetStats() map[string]interface{} {
r.mu.RLock()
defer r.mu.RUnlock()
serverStats := make(map[int]interface{})
totalRoutes := 0
for port, rs := range r.servers {
rs.routeMu.RLock()
routes := make([]string, 0, len(rs.routes))
for path := range rs.routes {
routes = append(routes, path)
totalRoutes++
}
rs.routeMu.RUnlock()
serverStats[port] = map[string]interface{}{
"routes": routes,
"requests": rs.requests.Load(),
"uptime": int(time.Since(rs.startTime).Seconds()),
}
}
return map[string]interface{}{
"uptime_seconds": int(time.Since(r.startTime).Seconds()),
"total_requests": r.totalRequests.Load(),
"routed_requests": r.routedRequests.Load(),
"failed_requests": r.failedRequests.Load(),
"servers": serverStats,
"total_routes": totalRoutes,
}
}