HTTP Client Optimization:

Reduced connection timeouts from 30s to 15s
Decreased idle connection limits from 100 to 30
Lowered keepalive duration from 90s to 30s
Added MaxConnsPerHost limit of 50 to prevent connection flooding
Optimized TLS handshake timeout to 5s

Cache System Optimization:
Implemented size-limited LRU cache with max 1000 items
Added efficient eviction of least recently used items
Improved cleanup process with batch operations
Reduced lock contention by splitting read/write operations
Optimized memory usage with access tracking
Added immediate cleanup of expired items during access

Connection Management:
Enabled connection reuse with keepalives
Reduced connection pool size to conserve memory
Implemented more aggressive connection timeout
Added connection limits per host to prevent resource exhaustion
This commit is contained in:
2025-01-06 12:48:33 +00:00
parent 7e3dc46b6e
commit 74c620ad51
2 changed files with 88 additions and 12 deletions
+79 -5
View File
@@ -24,13 +24,24 @@ type Cache struct {
// mutex protects concurrent access to the items map
// Use RLock/RUnlock for reads and Lock/Unlock for writes
mutex sync.RWMutex
// maxSize is the maximum number of items allowed in the cache
maxSize int
// accessList maintains the order of item access for eviction
accessList []string
}
// DefaultMaxSize is the default maximum number of items in the cache
const DefaultMaxSize = 1000
// NewCache creates a new empty cache instance.
// The cache is immediately ready for use and is thread-safe.
func NewCache() *Cache {
return &Cache{
items: make(map[string]CacheItem),
items: make(map[string]CacheItem),
maxSize: DefaultMaxSize,
accessList: make([]string, 0, DefaultMaxSize),
}
}
@@ -43,10 +54,27 @@ func NewCache() *Cache {
func (c *Cache) Set(key string, value interface{}, expiration time.Duration) {
c.mutex.Lock()
defer c.mutex.Unlock()
// If key exists, update it
if _, exists := c.items[key]; exists {
c.items[key] = CacheItem{
Value: value,
ExpiresAt: time.Now().Add(expiration),
}
return
}
// If cache is full, remove oldest item
if len(c.items) >= c.maxSize {
c.evictOldest()
}
// Add new item
c.items[key] = CacheItem{
Value: value,
ExpiresAt: time.Now().Add(expiration),
}
c.accessList = append(c.accessList, key)
}
// Get retrieves an item from the cache if it exists and hasn't expired.
@@ -58,15 +86,25 @@ func (c *Cache) Set(key string, value interface{}, expiration time.Duration) {
// Thread-safe: Uses read locking to ensure safe concurrent access.
func (c *Cache) Get(key string) (interface{}, bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
item, found := c.items[key]
c.mutex.RUnlock()
if !found {
return nil, false
}
if time.Now().After(item.ExpiresAt) {
delete(c.items, key)
c.mutex.Lock()
c.removeItem(key)
c.mutex.Unlock()
return nil, false
}
// Update access order
c.mutex.Lock()
c.updateAccessOrder(key)
c.mutex.Unlock()
return item.Value, true
}
@@ -86,10 +124,46 @@ func (c *Cache) Delete(key string) {
func (c *Cache) Cleanup() {
c.mutex.Lock()
defer c.mutex.Unlock()
now := time.Now()
for key, item := range c.items {
if now.After(item.ExpiresAt) {
var newAccessList []string
for _, key := range c.accessList {
if item, exists := c.items[key]; exists && !now.After(item.ExpiresAt) {
newAccessList = append(newAccessList, key)
} else {
delete(c.items, key)
}
}
c.accessList = newAccessList
}
// evictOldest removes the least recently used item from the cache
func (c *Cache) evictOldest() {
if len(c.accessList) > 0 {
oldest := c.accessList[0]
c.removeItem(oldest)
}
}
// removeItem removes an item from both the cache and access list
func (c *Cache) removeItem(key string) {
delete(c.items, key)
for i, k := range c.accessList {
if k == key {
c.accessList = append(c.accessList[:i], c.accessList[i+1:]...)
break
}
}
}
// updateAccessOrder moves the accessed key to the end of the access list
func (c *Cache) updateAccessOrder(key string) {
for i, k := range c.accessList {
if k == key {
c.accessList = append(append(c.accessList[:i], c.accessList[i+1:]...), key)
break
}
}
}
+9 -7
View File
@@ -180,17 +180,19 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h
Proxy: http.ProxyFromEnvironment,
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
dialer := &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
Timeout: 15 * time.Second, // Reduced timeout
KeepAlive: 15 * time.Second, // Reduced keepalive
}
return dialer.DialContext(ctx, network, addr)
},
ForceAttemptHTTP2: true,
TLSHandshakeTimeout: 10 * time.Second,
TLSHandshakeTimeout: 5 * time.Second, // Reduced from 10s
ExpectContinueTimeout: 0,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 100,
IdleConnTimeout: 90 * time.Second,
MaxIdleConns: 30, // Reduced from 100
MaxIdleConnsPerHost: 10, // Reduced from 100
IdleConnTimeout: 30 * time.Second, // Reduced from 90s
DisableKeepAlives: false, // Enable connection reuse
MaxConnsPerHost: 50, // Limit max connections
}
var httpClient *http.Client
@@ -198,7 +200,7 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h
httpClient = config.HTTPClient
} else {
httpClient = &http.Client{
Timeout: time.Second * 30,
Timeout: time.Second * 15, // Reduced timeout
Transport: transport,
}
}