CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-software-amazon-awssdk--http-client-spi

Service Provider Interface (SPI) for HTTP client implementations in the AWS SDK for Java v2

Overview
Eval results
Files

metrics.mddocs/

Metrics and Monitoring

Built-in metrics collection capabilities for HTTP client implementations. Provides standardized metrics for HTTP/1.1 and HTTP/2 operations including connection pooling, performance monitoring, and protocol-specific statistics.

Capabilities

HttpMetric

Defines standard metrics that HTTP client implementations should collect for HTTP/1.1 and HTTP/2 operations. These metrics provide insights into client performance and resource utilization.

/**
 * Metrics collected by HTTP clients for HTTP/1 and HTTP/2 operations.
 * Implementations should collect these metrics to provide visibility
 * into client performance and resource utilization.
 */
public final class HttpMetric {
    /**
     * Name of the HTTP client implementation
     */
    public static final SdkMetric<String> HTTP_CLIENT_NAME;
    
    /**
     * Maximum number of concurrent requests supported by the client
     */
    public static final SdkMetric<Integer> MAX_CONCURRENCY;
    
    /**
     * Current number of available concurrent request slots
     */
    public static final SdkMetric<Integer> AVAILABLE_CONCURRENCY;
    
    /**
     * Current number of requests actively being executed
     */
    public static final SdkMetric<Integer> LEASED_CONCURRENCY;
    
    /**
     * Number of requests currently waiting to acquire concurrency
     */
    public static final SdkMetric<Integer> PENDING_CONCURRENCY_ACQUIRES;
    
    /**
     * HTTP response status code for completed requests
     */
    public static final SdkMetric<Integer> HTTP_STATUS_CODE;
    
    /**
     * Time taken to acquire a connection or channel from the pool
     */
    public static final SdkMetric<Duration> CONCURRENCY_ACQUIRE_DURATION;
}

Usage Example:

// HTTP client implementation collecting metrics
public class MyHttpClient implements SdkHttpClient {
    private final MetricCollector metricCollector;
    private final ConnectionPool connectionPool;
    
    @Override
    public ExecutableHttpRequest prepareRequest(HttpExecuteRequest request) {
        return new MetricsAwareExecutableRequest(request, metricCollector, connectionPool);
    }
    
    // Collect periodic metrics
    private void collectPoolMetrics() {
        metricCollector.reportMetric(HttpMetric.HTTP_CLIENT_NAME, clientName());
        metricCollector.reportMetric(HttpMetric.MAX_CONCURRENCY, connectionPool.maxConnections());
        metricCollector.reportMetric(HttpMetric.AVAILABLE_CONCURRENCY, connectionPool.availableConnections());
        metricCollector.reportMetric(HttpMetric.LEASED_CONCURRENCY, connectionPool.activeConnections());
        metricCollector.reportMetric(HttpMetric.PENDING_CONCURRENCY_ACQUIRES, connectionPool.pendingAcquires());
    }
}

// Metrics-aware request execution
public class MetricsAwareExecutableRequest implements ExecutableHttpRequest {
    private final HttpExecuteRequest request;
    private final MetricCollector metricCollector;
    private final ConnectionPool connectionPool;
    
    @Override
    public HttpExecuteResponse call() throws IOException {
        long acquireStart = System.nanoTime();
        
        try (Connection connection = connectionPool.acquire()) {
            long acquireDuration = System.nanoTime() - acquireStart;
            metricCollector.reportMetric(HttpMetric.CONCURRENCY_ACQUIRE_DURATION, 
                Duration.ofNanos(acquireDuration));
            
            HttpExecuteResponse response = connection.execute(request);
            
            // Report response status
            metricCollector.reportMetric(HttpMetric.HTTP_STATUS_CODE, 
                response.httpResponse().statusCode());
            
            return response;
        }
    }
}

Http2Metric

Defines HTTP/2 specific metrics that provide insights into HTTP/2 protocol behavior, stream management, and flow control.

/**
 * Metrics specific to HTTP/2 operations.
 * These metrics provide insights into HTTP/2 stream management,
 * flow control, and protocol-specific performance characteristics.
 */
public final class Http2Metric {
    /**
     * Local HTTP/2 stream window size in bytes.
     * Indicates how much data the local endpoint can receive.
     */
    public static final SdkMetric<Integer> LOCAL_STREAM_WINDOW_SIZE_IN_BYTES;
    
    /**
     * Remote HTTP/2 stream window size in bytes.
     * Indicates how much data can be sent to the remote endpoint.
     */
    public static final SdkMetric<Integer> REMOTE_STREAM_WINDOW_SIZE_IN_BYTES;
}

Usage Example:

// HTTP/2 client implementation with flow control metrics
public class Http2Client implements SdkAsyncHttpClient {
    private final MetricCollector metricCollector;
    
    public void reportStreamMetrics(Http2Stream stream) {
        // Report flow control window sizes
        metricCollector.reportMetric(Http2Metric.LOCAL_STREAM_WINDOW_SIZE_IN_BYTES,
            stream.getLocalWindowSize());
        
        metricCollector.reportMetric(Http2Metric.REMOTE_STREAM_WINDOW_SIZE_IN_BYTES,
            stream.getRemoteWindowSize());
    }
    
    @Override
    public CompletableFuture<Void> execute(AsyncExecuteRequest request) {
        return http2Connection.createStream()
            .thenCompose(stream -> {
                // Report initial window sizes
                reportStreamMetrics(stream);
                
                // Execute request and monitor flow control
                return stream.sendRequest(request)
                    .thenRun(() -> {
                        // Report final window sizes
                        reportStreamMetrics(stream);
                    });
            });
    }
}

Metric Collection Integration

SdkMetric Type System

The metric system uses type-safe metric definitions:

/**
 * Type-safe metric definition
 */
public interface SdkMetric<T> {
    /**
     * @return Metric name for identification
     */
    String name();
    
    /**
     * @return Expected value type for this metric
     */
    Class<T> valueType();
}

MetricCollector Integration

HTTP clients should integrate with the AWS SDK metric collection system:

// Metric collection during request execution
public class MetricsIntegratedClient implements SdkHttpClient {
    @Override
    public ExecutableHttpRequest prepareRequest(HttpExecuteRequest request) {
        Optional<MetricCollector> collector = request.metricCollector();
        
        return new ExecutableHttpRequest() {
            @Override
            public HttpExecuteResponse call() throws IOException {
                long startTime = System.nanoTime();
                
                try {
                    // Report client name
                    collector.ifPresent(c -> 
                        c.reportMetric(HttpMetric.HTTP_CLIENT_NAME, clientName()));
                    
                    // Execute request
                    HttpExecuteResponse response = executeHttpRequest(request);
                    
                    // Report success metrics
                    collector.ifPresent(c -> {
                        c.reportMetric(HttpMetric.HTTP_STATUS_CODE, 
                            response.httpResponse().statusCode());
                        
                        long duration = System.nanoTime() - startTime;
                        c.reportMetric(HttpMetric.CONCURRENCY_ACQUIRE_DURATION,
                            Duration.ofNanos(duration));
                    });
                    
                    return response;
                } catch (IOException e) {
                    // Report error metrics
                    collector.ifPresent(c -> {
                        // Could report error-specific metrics here
                    });
                    throw e;
                }
            }
        };
    }
}

Connection Pool Metrics

Real-time Pool Monitoring

HTTP clients should provide real-time visibility into connection pool state:

public class MonitoredConnectionPool implements ConnectionPool {
    private final AtomicInteger maxConnections;
    private final AtomicInteger activeConnections;
    private final AtomicInteger availableConnections;
    private final AtomicInteger pendingAcquires;
    private final MetricCollector metricCollector;
    
    public MonitoredConnectionPool(int maxConnections, MetricCollector metricCollector) {
        this.maxConnections = new AtomicInteger(maxConnections);
        this.activeConnections = new AtomicInteger(0);
        this.availableConnections = new AtomicInteger(maxConnections);
        this.pendingAcquires = new AtomicInteger(0);
        this.metricCollector = metricCollector;
        
        // Start periodic metric reporting
        startMetricReporting();
    }
    
    @Override
    public Connection acquire() throws IOException {
        pendingAcquires.incrementAndGet();
        long acquireStart = System.nanoTime();
        
        try {
            Connection connection = doAcquire();
            
            // Update counters
            activeConnections.incrementAndGet();
            availableConnections.decrementAndGet();
            pendingAcquires.decrementAndGet();
            
            // Report acquire duration
            long acquireDuration = System.nanoTime() - acquireStart;
            metricCollector.reportMetric(HttpMetric.CONCURRENCY_ACQUIRE_DURATION,
                Duration.ofNanos(acquireDuration));
            
            return new MetricsAwareConnection(connection, this);
        } catch (IOException e) {
            pendingAcquires.decrementAndGet();
            throw e;
        }
    }
    
    public void releaseConnection(Connection connection) {
        activeConnections.decrementAndGet();
        availableConnections.incrementAndGet();
        doRelease(connection);
    }
    
    private void startMetricReporting() {
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(() -> {
            metricCollector.reportMetric(HttpMetric.MAX_CONCURRENCY, maxConnections.get());
            metricCollector.reportMetric(HttpMetric.AVAILABLE_CONCURRENCY, availableConnections.get());
            metricCollector.reportMetric(HttpMetric.LEASED_CONCURRENCY, activeConnections.get());
            metricCollector.reportMetric(HttpMetric.PENDING_CONCURRENCY_ACQUIRES, pendingAcquires.get());
        }, 0, 10, TimeUnit.SECONDS);
    }
}

// Connection wrapper that reports metrics on release
public class MetricsAwareConnection implements Connection {
    private final Connection delegate;
    private final MonitoredConnectionPool pool;
    
    @Override
    public void close() {
        delegate.close();
        pool.releaseConnection(delegate);
    }
    
    // Delegate all other methods to the wrapped connection
}

HTTP/2 Stream Metrics

For HTTP/2 implementations, track stream-specific metrics:

public class Http2StreamManager {
    private final MetricCollector metricCollector;
    private final Map<Integer, Http2Stream> activeStreams = new ConcurrentHashMap<>();
    
    public Http2Stream createStream() {
        Http2Stream stream = new Http2Stream();
        activeStreams.put(stream.getId(), stream);
        
        // Report initial flow control state
        reportStreamFlowControl(stream);
        
        return stream;
    }
    
    public void reportStreamFlowControl(Http2Stream stream) {
        metricCollector.reportMetric(Http2Metric.LOCAL_STREAM_WINDOW_SIZE_IN_BYTES,
            stream.getLocalFlowControlWindow());
        
        metricCollector.reportMetric(Http2Metric.REMOTE_STREAM_WINDOW_SIZE_IN_BYTES,
            stream.getRemoteFlowControlWindow());
    }
    
    public void onWindowUpdate(int streamId, int windowSizeIncrement) {
        Http2Stream stream = activeStreams.get(streamId);
        if (stream != null) {
            stream.updateRemoteWindow(windowSizeIncrement);
            reportStreamFlowControl(stream);
        }
    }
    
    public void onStreamData(int streamId, int dataSize) {
        Http2Stream stream = activeStreams.get(streamId);
        if (stream != null) {
            stream.consumeLocalWindow(dataSize);
            reportStreamFlowControl(stream);
        }
    }
}

Metrics Best Practices

Efficient Metric Collection

  1. Avoid High-Frequency Metrics:

    // Don't report metrics for every byte transferred
    // Instead, report periodically or on significant events
    
    public class EfficientMetricReporting {
        private volatile long lastMetricReport = 0;
        private static final long METRIC_REPORT_INTERVAL_MS = 10_000; // 10 seconds
        
        public void maybeReportMetrics() {
            long now = System.currentTimeMillis();
            if (now - lastMetricReport > METRIC_REPORT_INTERVAL_MS) {
                reportCurrentMetrics();
                lastMetricReport = now;
            }
        }
    }
  2. Batch Metric Updates:

    // Collect multiple metrics in a single call
    public void reportBatchMetrics(MetricCollector collector) {
        collector.reportMetrics(Map.of(
            HttpMetric.AVAILABLE_CONCURRENCY, connectionPool.available(),
            HttpMetric.LEASED_CONCURRENCY, connectionPool.active(),
            HttpMetric.PENDING_CONCURRENCY_ACQUIRES, connectionPool.pending()
        ));
    }
  3. Conditional Metric Collection:

    public HttpExecuteResponse call() throws IOException {
        Optional<MetricCollector> collector = request.metricCollector();
        
        // Only collect metrics if a collector is provided
        if (collector.isPresent()) {
            return executeWithMetrics(collector.get());
        } else {
            return executeWithoutMetrics();
        }
    }

Error and Performance Metrics

Track both success and failure scenarios:

public class ComprehensiveMetricsClient implements SdkHttpClient {
    @Override
    public ExecutableHttpRequest prepareRequest(HttpExecuteRequest request) {
        return new ExecutableHttpRequest() {
            @Override
            public HttpExecuteResponse call() throws IOException {
                Optional<MetricCollector> collector = request.metricCollector();
                long startTime = System.nanoTime();
                
                try {
                    HttpExecuteResponse response = executeRequest(request);
                    
                    // Report success metrics
                    collector.ifPresent(c -> {
                        c.reportMetric(HttpMetric.HTTP_STATUS_CODE, 
                            response.httpResponse().statusCode());
                        
                        // Report timing
                        long duration = System.nanoTime() - startTime;
                        c.reportMetric(HttpMetric.CONCURRENCY_ACQUIRE_DURATION,
                            Duration.ofNanos(duration));
                    });
                    
                    return response;
                } catch (IOException e) {
                    // Report error metrics
                    collector.ifPresent(c -> {
                        // Report error count or type
                        c.reportMetric(createErrorMetric(e.getClass().getSimpleName()), 1);
                        
                        // Still report timing for failed requests
                        long duration = System.nanoTime() - startTime;
                        c.reportMetric(HttpMetric.CONCURRENCY_ACQUIRE_DURATION,
                            Duration.ofNanos(duration));
                    });
                    
                    throw e;
                }
            }
        };
    }
    
    private SdkMetric<Integer> createErrorMetric(String errorType) {
        // Create custom metrics for different error types
        return new SdkMetric<Integer>() {
            @Override
            public String name() {
                return "http.client.error." + errorType.toLowerCase();
            }
            
            @Override
            public Class<Integer> valueType() {
                return Integer.class;
            }
        };
    }
}

Monitoring and Observability

Integration with Monitoring Systems

HTTP client metrics can be integrated with various monitoring systems:

// Example integration with Micrometer/Prometheus
public class MicrometerIntegratedClient implements SdkHttpClient {
    private final MeterRegistry meterRegistry;
    private final Counter requestCounter;
    private final Timer requestTimer;
    private final Gauge connectionGauge;
    
    public MicrometerIntegratedClient(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        this.requestCounter = Counter.builder("http.client.requests")
            .description("Total HTTP requests")
            .register(meterRegistry);
        this.requestTimer = Timer.builder("http.client.request.duration")
            .description("HTTP request duration")
            .register(meterRegistry);
        this.connectionGauge = Gauge.builder("http.client.connections.active")
            .description("Active HTTP connections")
            .register(meterRegistry, this, client -> getActiveConnectionCount());
    }
    
    // Implementation integrates AWS SDK metrics with Micrometer
}

The metrics system provides comprehensive visibility into HTTP client behavior, enabling effective monitoring, troubleshooting, and performance optimization of AWS SDK HTTP operations.

Install with Tessl CLI

npx tessl i tessl/maven-software-amazon-awssdk--http-client-spi

docs

content-streaming.md

http-clients.md

http-messages.md

index.md

metrics.md

service-discovery.md

tls-configuration.md

tile.json