HTTP Server Performance Guide¶
Comprehensive guide for optimizing Blueprint's HTTP server performance, scaling strategies, and production deployment best practices.
Performance Fundamentals¶
Server Configuration Optimization¶
Connection Timeouts¶
func optimizeServerTimeouts(config *httpserver.ServerConfig) {
// Production timeouts
config.ReadTimeout = 30 // 30 seconds read timeout
config.WriteTimeout = 30 // 30 seconds write timeout
// For API servers with long-running operations
config.ReadTimeout = 60 // 1 minute
config.WriteTimeout = 300 // 5 minutes for large responses
// For microservices with quick responses
config.ReadTimeout = 10 // 10 seconds
config.WriteTimeout = 10 // 10 seconds
}
HTTP Server Tuning¶
func createOptimizedServer(config *httpserver.ServerConfig, logger *log.Logger) (*httpserver.Server, error) {
server, err := httpserver.NewServer(config, logger)
if err != nil {
return nil, err
}
// Optimize underlying HTTP server
server.Server.MaxHeaderBytes = 1 << 20 // 1MB max header size
server.Server.IdleTimeout = 120 * time.Second
server.Server.ReadHeaderTimeout = 5 * time.Second
return server, nil
}
Gin Router Optimization¶
Release Mode¶
func optimizeGinRouter() {
// Always use release mode in production
gin.SetMode(gin.ReleaseMode)
// Or set via environment
os.Setenv("GIN_MODE", "release")
}
Router Configuration¶
func createOptimizedRouter(logger *log.Logger) *gin.Engine {
// Disable debug features
gin.SetMode(gin.ReleaseMode)
router := gin.New()
// Use only necessary middleware
if logger != nil {
router.Use(httplog.HTTPLogMiddleware(logger))
}
router.Use(gin.Recovery())
// Avoid unnecessary middleware in production
// router.Use(gin.Logger()) // Skip default logger
return router
}
Middleware Performance¶
Middleware Ordering¶
Optimize middleware order for best performance:
func optimizeMiddlewareOrder(server *httpserver.Server, logger *log.Logger) {
// 1. Fast security headers (minimal overhead)
server.UseDefaultSecurityHeaders()
// 2. Rate limiting (early rejection of excess traffic)
server.UseRateLimiting(1000)
// 3. Request ID (lightweight)
server.AddMiddleware(requestIDMiddleware())
// 4. Authentication (reject unauthorized early)
tokenAuth := auth.NewAuthToken("X-API-Key", "secret")
server.UseAuth(tokenAuth)
// 5. Expensive middleware last
server.UseSession(sessionConfig, backend, logger)
server.UseCSRFProtection()
}
Efficient Rate Limiting¶
func efficientRateLimiting(server *httpserver.Server) {
// Use efficient rate limiting
r := rate.Every(time.Second / 100) // 100 requests per second
burst := 50 // Allow bursts
server.AddMiddleware(security.RateLimitMiddleware(r, burst))
// For high-traffic scenarios, consider Redis-based rate limiting
// with connection pooling and distributed counters
}
Lightweight Middleware¶
func lightweightMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// Minimal processing
c.Header("X-Request-ID", generateFastID())
c.Next()
}
}
// Avoid expensive operations in middleware
func avoidSlowMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// Avoid:
// - Database queries
// - External API calls
// - Heavy computations
// - Large memory allocations
c.Next()
}
}
Memory Optimization¶
Connection Management¶
func optimizeConnections(server *httpserver.Server) {
// Configure connection pooling
server.Server.SetKeepAlivesEnabled(true)
server.Server.IdleTimeout = 60 * time.Second
// For high-concurrency scenarios
server.Server.MaxHeaderBytes = 32 << 10 // 32KB max headers
}
Memory Usage Monitoring¶
func memoryMonitoringMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// Monitor memory usage periodically, not on every request
if rand.Intn(1000) == 0 { // 0.1% sampling
var m runtime.MemStats
runtime.ReadMemStats(&m)
if m.Alloc > 500<<20 { // 500MB threshold
log.Warn("High memory usage", "alloc", m.Alloc)
}
}
c.Next()
}
}
Garbage Collection Optimization¶
func optimizeGC() {
// Tune GC for server workloads
debug.SetGCPercent(100) // Default is usually good
// For memory-constrained environments
debug.SetGCPercent(50)
// For high-throughput scenarios
debug.SetGCPercent(200)
// Set memory limit (Go 1.19+)
debug.SetMemoryLimit(1 << 30) // 1GB limit
}
Session Performance¶
Efficient Session Storage¶
func optimizeSessionStorage(logger *log.Logger) kv.KV {
// For single instance - memory is fastest
if isSingleInstance() {
return kv.NewMemoryKV()
}
// For distributed - Redis with connection pooling
redisConfig := redis.NewConfig()
redisConfig.Address = "redis:6379"
redisConfig.MaxConnections = 100
redisConfig.MaxIdle = 20
redisConfig.IdleTimeout = 300 * time.Second
backend, err := redis.NewClient(redisConfig)
if err != nil {
logger.Error(err, "failed to connect to Redis, falling back to memory")
return kv.NewMemoryKV()
}
return backend
}
Session Configuration¶
func optimizeSessionConfig() *session.Config {
config := session.NewConfig()
// Optimize timeouts
config.ExpirationSeconds = 3600 // 1 hour
config.IdleTimeoutSeconds = 1800 // 30 minutes
config.CleanupIntervalSeconds = 300 // 5 minutes
// Optimize for performance
config.HttpOnly = true // Prevent XSS
config.Secure = true // HTTPS only in production
config.SameSite = http.SameSiteStrictMode
return config
}
Database and External Services¶
Connection Pooling¶
type OptimizedService struct {
db *sql.DB
cache *redis.Client
logger *log.Logger
}
func NewOptimizedService() *OptimizedService {
// Database connection pool
db, _ := sql.Open("postgres", dsn)
db.SetMaxOpenConns(100) // Max concurrent connections
db.SetMaxIdleConns(10) // Idle connections to keep
db.SetConnMaxLifetime(time.Hour) // Connection lifetime
// Redis connection pool
redisClient := redis.NewClient(&redis.Options{
Addr: "redis:6379",
PoolSize: 100,
MinIdleConns: 10,
PoolTimeout: 4 * time.Second,
})
return &OptimizedService{
db: db,
cache: redisClient,
}
}
Caching Strategies¶
func cacheMiddleware(cache *redis.Client) gin.HandlerFunc {
return func(c *gin.Context) {
// Only cache GET requests
if c.Request.Method != "GET" {
c.Next()
return
}
cacheKey := generateCacheKey(c.Request.URL.Path, c.Request.URL.RawQuery)
// Try cache first
if cached, err := cache.Get(ctx, cacheKey).Result(); err == nil {
c.Header("X-Cache", "HIT")
c.Data(200, "application/json", []byte(cached))
return
}
// Capture response
w := &responseWriter{ResponseWriter: c.Writer}
c.Writer = w
c.Next()
// Cache successful responses
if w.status == 200 && len(w.body) > 0 {
cache.Set(ctx, cacheKey, w.body, 5*time.Minute)
}
}
}
type responseWriter struct {
gin.ResponseWriter
body []byte
status int
}
func (w *responseWriter) Write(data []byte) (int, error) {
w.body = append(w.body, data...)
return w.ResponseWriter.Write(data)
}
func (w *responseWriter) WriteHeader(status int) {
w.status = status
w.ResponseWriter.WriteHeader(status)
}
Load Balancing and Scaling¶
Horizontal Scaling¶
func createLoadBalancedServer(instanceID string) *httpserver.Server {
config := httpserver.NewServerConfig()
// Each instance gets a unique port for development
basePort := 8080
port := basePort + instanceID
config.Port = port
// Shared configuration
config.ReadTimeout = 30
config.WriteTimeout = 30
logger := log.New(fmt.Sprintf("instance-%d", instanceID))
server, _ := httpserver.NewServer(config, logger)
return server
}
Health Checks for Load Balancers¶
func setupHealthChecks(server *httpserver.Server) {
router := server.Route()
// Simple health check
router.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "healthy"})
})
// Detailed health check
router.GET("/health/detailed", func(c *gin.Context) {
checks := performHealthChecks()
allHealthy := true
for _, status := range checks {
if status != "healthy" {
allHealthy = false
break
}
}
statusCode := 200
if !allHealthy {
statusCode = 503
}
c.JSON(statusCode, gin.H{
"status": allHealthy,
"checks": checks,
"instance": os.Getenv("INSTANCE_ID"),
"timestamp": time.Now(),
})
})
// Readiness check (for Kubernetes)
router.GET("/ready", func(c *gin.Context) {
if isReady() {
c.JSON(200, gin.H{"ready": true})
} else {
c.JSON(503, gin.H{"ready": false})
}
})
// Liveness check (for Kubernetes)
router.GET("/live", func(c *gin.Context) {
c.JSON(200, gin.H{"alive": true})
})
}
func performHealthChecks() map[string]string {
checks := make(map[string]string)
// Database check
if pingDatabase() {
checks["database"] = "healthy"
} else {
checks["database"] = "unhealthy"
}
// Redis check
if pingRedis() {
checks["redis"] = "healthy"
} else {
checks["redis"] = "unhealthy"
}
// Memory check
if checkMemoryUsage() {
checks["memory"] = "healthy"
} else {
checks["memory"] = "warning"
}
return checks
}
Graceful Shutdown¶
func gracefulShutdownServer(server *httpserver.Server, logger *log.Logger) {
// Channel to receive OS signals
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
// Start server in goroutine
go func() {
logger.Info("server starting", "port", server.Config.Port)
if err := server.Start(); err != nil {
logger.Error(err, "server failed to start")
}
}()
// Block until signal received
<-quit
logger.Info("shutting down server...")
// Create context with timeout for graceful shutdown
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// Attempt graceful shutdown
if err := server.Shutdown(ctx); err != nil {
logger.Error(err, "forced shutdown")
os.Exit(1)
}
logger.Info("server stopped gracefully")
}
Monitoring and Metrics¶
Performance Metrics¶
func performanceMetricsMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Next()
duration := time.Since(start)
// Record metrics (use your preferred metrics library)
recordMetrics(c.Request.Method, c.FullPath(), c.Writer.Status(), duration)
// Log slow requests
if duration > 1*time.Second {
log.Warn("slow request",
"path", c.Request.URL.Path,
"method", c.Request.Method,
"duration", duration,
"status", c.Writer.Status())
}
}
}
func recordMetrics(method, path string, status int, duration time.Duration) {
// Implementation depends on your metrics system
// Examples: Prometheus, StatsD, CloudWatch, etc.
}
Prometheus Metrics¶
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
httpDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "Duration of HTTP requests in seconds",
},
[]string{"method", "path", "status"},
)
httpRequests = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests",
},
[]string{"method", "path", "status"},
)
)
func init() {
prometheus.MustRegister(httpDuration)
prometheus.MustRegister(httpRequests)
}
func prometheusMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Next()
duration := time.Since(start)
status := strconv.Itoa(c.Writer.Status())
httpDuration.WithLabelValues(c.Request.Method, c.FullPath(), status).Observe(duration.Seconds())
httpRequests.WithLabelValues(c.Request.Method, c.FullPath(), status).Inc()
}
}
func setupMetricsEndpoint(server *httpserver.Server) {
server.Route().GET("/metrics", gin.WrapH(promhttp.Handler()))
}
Production Deployment¶
Container Optimization¶
# Dockerfile optimized for performance
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY ../../httpserver .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
# Performance-optimized runtime
ENV GIN_MODE=release
ENV GOGC=100
ENV GOMAXPROCS=0
EXPOSE 8080
CMD ["./main"]
Environment Configuration¶
func productionConfig() *httpserver.ServerConfig {
config := httpserver.NewServerConfig()
// Read from environment
config.Port = getEnvInt("PORT", 8080)
config.Host = getEnv("HOST", "0.0.0.0")
config.ReadTimeout = getEnvInt("READ_TIMEOUT", 30)
config.WriteTimeout = getEnvInt("WRITE_TIMEOUT", 30)
// Production settings
config.Debug = false
// TLS configuration
if certFile := os.Getenv("TLS_CERT_FILE"); certFile != "" {
config.TLSCert = certFile
config.TLSKey = os.Getenv("TLS_KEY_FILE")
config.TLSEnable = true
}
return config
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
func getEnvInt(key string, defaultValue int) int {
if value := os.Getenv(key); value != "" {
if i, err := strconv.Atoi(value); err == nil {
return i
}
}
return defaultValue
}
Kubernetes Deployment¶
apiVersion: apps/v1
kind: Deployment
metadata:
name: http-server
spec:
replicas: 3
selector:
matchLabels:
app: http-server
template:
metadata:
labels:
app: http-server
spec:
containers:
- name: http-server
image: your-app:latest
ports:
- containerPort: 8080
env:
- name: PORT
value: "8080"
- name: GIN_MODE
value: "release"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /live
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: http-server-service
spec:
selector:
app: http-server
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
Performance Testing¶
Load Testing¶
func loadTestEndpoint() {
// Example using built-in testing
func TestServerPerformance(t *testing.T) {
server := setupTestServer()
// Concurrent requests
concurrency := 100
requests := 1000
var wg sync.WaitGroup
results := make(chan time.Duration, requests)
for i := 0; i < concurrency; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < requests/concurrency; j++ {
start := time.Now()
req := httptest.NewRequest("GET", "/api/test", nil)
w := httptest.NewRecorder()
server.Route().ServeHTTP(w, req)
results <- time.Since(start)
}
}()
}
wg.Wait()
close(results)
// Analyze results
var durations []time.Duration
for duration := range results {
durations = append(durations, duration)
}
sort.Slice(durations, func(i, j int) bool {
return durations[i] < durations[j]
})
p50 := durations[len(durations)/2]
p95 := durations[int(float64(len(durations))*0.95)]
p99 := durations[int(float64(len(durations))*0.99)]
t.Logf("Performance Results: P50=%v, P95=%v, P99=%v", p50, p95, p99)
// Assert performance requirements
assert.True(t, p95 < 100*time.Millisecond, "95th percentile should be under 100ms")
}
}
Best Practices Summary¶
Configuration¶
- Use release mode in production
- Set appropriate timeouts
- Configure connection pooling
- Enable keep-alive connections
Middleware¶
- Order middleware by execution cost
- Minimize middleware overhead
- Use efficient rate limiting
- Implement proper caching
Memory Management¶
- Monitor memory usage
- Tune garbage collection
- Limit request sizes
- Use connection pooling
Monitoring¶
- Implement health checks
- Add performance metrics
- Monitor error rates
- Set up alerting
Scaling¶
- Design for horizontal scaling
- Implement graceful shutdown
- Use load balancers
- Cache frequently accessed data
Security vs Performance¶
- Balance security and performance
- Use efficient authentication
- Implement reasonable rate limits
- Cache security validations when possible
This performance guide provides a comprehensive foundation for building high-performance HTTP servers with Blueprint while maintaining security and reliability.