A high-performance, reliable network connection pool management system for Go applications.
- Thread-safe connection management with mutex protection
- Support for both client and server connection pools
- Dynamic capacity adjustment based on usage patterns
- Automatic connection health monitoring
- Multiple TLS security modes (none, self-signed, verified)
- Connection identification and tracking
- Graceful error handling and recovery
- Configurable connection creation intervals
- Auto-reconnection with exponential backoff
- Connection activity validation
go get github.com/NodePassProject/pool
package main
import (
"net"
"time"
"github.com/NodePassProject/pool"
)
func main() {
// Create a dialer function
dialer := func() (net.Conn, error) {
return net.Dial("tcp", "example.com:8080")
}
// Create a new client pool with:
// - Minimum capacity: 5 connections
// - Maximum capacity: 20 connections
// - Minimum interval: 500ms between connection attempts
// - Maximum interval: 5s between connection attempts
// - TLS mode: "2" (verified certificates)
// - Hostname for certificate verification: "example.com"
clientPool := pool.NewClientPool(
5, 20,
500*time.Millisecond, 5*time.Second,
"2", "example.com",
dialer,
)
// Start the client manager (usually in a goroutine)
go clientPool.ClientManager()
// Get a connection by ID (usually received from the server)
conn := clientPool.ClientGet("connection-id")
// Use the connection...
// When finished with the pool
clientPool.Close()
}
package main
import (
"crypto/tls"
"net"
"github.com/NodePassProject/pool"
)
func main() {
// Create a listener
listener, err := net.Listen("tcp", ":8080")
if err != nil {
panic(err)
}
// Optional: Create a TLS config
tlsConfig := &tls.Config{
// Configure TLS settings
MinVersion: tls.VersionTLS13,
}
// Create a new server pool
// - Restrict to specific client IP (optional, "" for any IP, "192.168.1.10" to only allow that specific IP)
// - Use TLS config (optional, nil for no TLS)
// - Use the created listener
serverPool := pool.NewServerPool("192.168.1.10", tlsConfig, listener)
// Start the server manager (usually in a goroutine)
go serverPool.ServerManager()
// Get a new connection from the pool (blocks until available)
id, conn := serverPool.ServerGet()
// Use the connection...
// When finished with the pool
serverPool.Close()
}
// Check if the pool is ready
if clientPool.Ready() {
// The pool is initialized and ready for use
}
// Get current active connection count
activeConnections := clientPool.Active()
// Get current capacity setting
capacity := clientPool.Capacity()
// Get current connection creation interval
interval := clientPool.Interval()
// Manually flush all connections (rarely needed)
clientPool.Flush()
// Record an error (increases internal error counter)
clientPool.AddError()
The NewServerPool
function allows you to restrict incoming connections to a specific client IP address:
// Create a server pool that only accepts connections from 192.168.1.10
serverPool := pool.NewServerPool("192.168.1.10", tlsConfig, listener)
When the clientIP
parameter is set:
- All connections from other IP addresses will be immediately closed
- This provides an additional layer of security beyond network firewalls
- Particularly useful for internal services or dedicated client-server applications
To allow connections from any IP address, use an empty string:
// Create a server pool that accepts connections from any IP
serverPool := pool.NewServerPool("", tlsConfig, listener)
The pool supports three TLS security modes for client connections:
"0"
: No TLS (plain TCP connections)"1"
: Self-signed certificates (InsecureSkipVerify=true)"2"
: Verified certificates (proper certificate validation)
The pool automatically adjusts:
- Connection creation intervals based on idle connection count
- Connection capacity based on connection creation success rate
package main
import (
"log"
"net"
"time"
"github.com/NodePassProject/pool"
"github.com/NodePassProject/logs"
)
func main() {
logger := logs.NewLogger(logs.Info, true)
clientPool := pool.NewClientPool(
5, 20,
500*time.Millisecond, 5*time.Second,
"2", "example.com",
func() (net.Conn, error) {
conn, err := net.Dial("tcp", "example.com:8080")
if err != nil {
// Log the error
logger.Error("Connection failed: %v", err)
// Record the error in the pool
clientPool.AddError()
}
return conn, err
},
)
go clientPool.ClientManager()
// Your application logic...
}
package main
import (
"context"
"net"
"time"
"github.com/NodePassProject/pool"
)
func main() {
// Create a context that can be cancelled
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
clientPool := pool.NewClientPool(
5, 20,
500*time.Millisecond, 5*time.Second,
"2", "example.com",
func() (net.Conn, error) {
// Use context-aware dialer
dialer := net.Dialer{Timeout: 5 * time.Second}
return dialer.DialContext(ctx, "tcp", "example.com:8080")
},
)
go clientPool.ClientManager()
// When needed to stop the pool:
// cancel()
// clientPool.Close()
}
package main
import (
"net"
"sync/atomic"
"time"
"github.com/NodePassProject/pool"
)
func main() {
// Create pools for different servers
serverAddresses := []string{
"server1.example.com:8080",
"server2.example.com:8080",
"server3.example.com:8080",
}
pools := make([]*pool.Pool, len(serverAddresses))
for i, addr := range serverAddresses {
serverAddr := addr // Create local copy for closure
pools[i] = pool.NewClientPool(
5, 20,
500*time.Millisecond, 5*time.Second,
"2", serverAddr[:len(serverAddr)-5], // Extract hostname
func() (net.Conn, error) {
return net.Dial("tcp", serverAddr)
},
)
go pools[i].ClientManager()
}
// Simple round-robin load balancer
var counter int32 = 0
getNextPool := func() *pool.Pool {
next := atomic.AddInt32(&counter, 1) % int32(len(pools))
return pools[next]
}
// Usage
id, conn := getNextPool().ServerGet()
// Use connection...
// When done with all pools
for _, p := range pools {
p.Close()
}
}
The ideal pool size depends on your application's workload:
- Too small: Can cause connection contention and delays
- Too large: Wastes resources and may overload the server
Start with a minimum capacity that can handle your baseline load and a maximum capacity that can handle peak loads. Monitor connection usage and adjust accordingly.
TLS connections have additional overhead:
- Handshake cost: TLS handshakes are CPU-intensive
- Memory usage: TLS connections require more memory
- Latency: Initial connection setup is slower
If maximum performance is critical and the network is secure, consider using mode "0"
(no TLS).
The isActive
method checks connection health by setting a brief read deadline and attempting to read. This ensures connections in the pool are valid, but adds a small overhead. For extremely high-throughput systems, consider implementing a custom validation strategy.
-
Connection Timeout
- Check network connectivity
- Verify server address and port
- Increase connection timeout in dialer
-
TLS Handshake Failure
- Verify certificate validity
- Check hostname configuration matches certificate
- For testing, try TLS mode
"1"
(InsecureSkipVerify)
-
Pool Exhaustion
- Increase max capacity
- Decrease connection hold time
- Check for connection leaks (connections not being released)
-
High Error Rate
- Monitor with
AddError()
- Implement backoff strategy in dialer
- Consider server-side issues
- Monitor with
Set log points at key locations:
- Before/after dialer calls
- When connections are added/removed from the pool
- When pool capacity is adjusted
- When connections are validated
Copyright (c) 2025, NodePassProject. Licensed under the BSD 3-Clause License. See the LICENSE file for details.