9.4 KiB
9.4 KiB
AWS SDK for Java 2.x Best Practices
Client Configuration
Timeout Configuration
Always configure both API call and attempt timeouts to prevent hanging requests.
ClientOverrideConfiguration config = ClientOverrideConfiguration.builder()
.apiCallTimeout(Duration.ofSeconds(30)) // Total for all retries
.apiCallAttemptTimeout(Duration.ofSeconds(10)) // Per-attempt timeout
.build();
Best Practices:
- Set
apiCallTimeouthigher thanapiCallAttemptTimeout - Use appropriate timeouts based on your service's characteristics
- Consider network latency and service response times
- Monitor timeout metrics to adjust as needed
HTTP Client Selection
Choose the appropriate HTTP client for your use case.
For Synchronous Applications (Apache HttpClient)
ApacheHttpClient httpClient = ApacheHttpClient.builder()
.maxConnections(100)
.connectionTimeout(Duration.ofSeconds(5))
.socketTimeout(Duration.ofSeconds(30))
.build();
Best Use Cases:
- Traditional synchronous applications
- Medium-throughput operations
- When blocking behavior is acceptable
For Asynchronous Applications (Netty NIO Client)
NettyNioAsyncHttpClient httpClient = NettyNioAsyncHttpClient.builder()
.maxConcurrency(100)
.connectionTimeout(Duration.ofSeconds(5))
.readTimeout(Duration.ofSeconds(30))
.writeTimeout(Duration.ofSeconds(30))
.sslProvider(SslProvider.OPENSSL)
.build();
Best Use Cases:
- High-throughput applications
- I/O-bound operations
- When non-blocking behavior is required
- For improved SSL performance
For Lightweight Applications (URL Connection Client)
UrlConnectionHttpClient httpClient = UrlConnectionHttpClient.builder()
.socketTimeout(Duration.ofSeconds(30))
.build();
Best Use Cases:
- Simple applications with low requirements
- When minimizing dependencies
- For basic operations
Authentication and Security
Credential Management
Default Provider Chain
// Use default chain (recommended)
S3Client s3Client = S3Client.builder().build();
Default Chain Order:
- Java system properties (
aws.accessKeyId,aws.secretAccessKey) - Environment variables (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY) - Web identity token from
AWS_WEB_IDENTITY_TOKEN_FILE - Shared credentials file (
~/.aws/credentials) - Config file (
~/.aws/config) - Amazon ECS container credentials
- Amazon EC2 instance profile credentials
Explicit Credential Provider
// Use specific credential provider
CredentialsProvider credentials = ProfileCredentialsProvider.create("my-profile");
S3Client s3Client = S3Client.builder()
.credentialsProvider(credentials)
.build();
IAM Roles (Preferred for Production)
// Use IAM role credentials
CredentialsProvider instanceProfile = InstanceProfileCredentialsProvider.create();
S3Client s3Client = S3Client.builder()
.credentialsProvider(instanceProfile)
.build();
Security Best Practices
- Never hardcode credentials - Use credential providers or environment variables
- Use IAM roles - Prefer over access keys when possible
- Implement credential rotation - For long-lived access keys
- Apply least privilege - Grant minimum required permissions
- Enable SSL - Always use HTTPS (default behavior)
- Monitor access - Enable AWS CloudTrail for auditing
- Use SSO for human users - Instead of long-term credentials
Resource Management
Client Lifecycle
// Option 1: Try-with-resources (recommended)
try (S3Client s3 = S3Client.builder().build()) {
// Use client
} // Auto-closed
// Option 2: Explicit close
S3Client s3 = S3Client.builder().build();
try {
// Use client
} finally {
s3.close();
}
Stream Handling
Close streams immediately to prevent connection pool exhaustion.
try (ResponseInputStream<GetObjectResponse> response =
s3Client.getObject(GetObjectRequest.builder()
.bucket(bucketName)
.key(objectKey)
.build())) {
// Read and process data immediately
byte[] data = response.readAllBytes();
} // Stream auto-closed, connection returned to pool
Performance Optimization
Connection Pooling
// Configure connection pooling
ApacheHttpClient httpClient = ApacheHttpClient.builder()
.maxConnections(100) // Adjust based on your needs
.connectionTimeout(Duration.ofSeconds(5))
.socketTimeout(Duration.ofSeconds(30))
.connectionTimeToLive(Duration.ofMinutes(5))
.build();
Best Practices:
- Set appropriate
maxConnectionsbased on expected load - Consider connection time to live (TTL)
- Monitor connection pool metrics
- Use appropriate timeouts
SSL Optimization
Use OpenSSL with Netty for better SSL performance.
<!-- Maven dependency -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>2.0.61.Final</version>
<scope>runtime</scope>
</dependency>
// Use OpenSSL for async clients
NettyNioAsyncHttpClient httpClient = NettyNioAsyncHttpClient.builder()
.sslProvider(SslProvider.OPENSSL)
.build();
Async for I/O-Bound Operations
// Use async clients for I/O-bound operations
S3AsyncClient s3AsyncClient = S3AsyncClient.builder()
.httpClient(httpClient)
.build();
// Use CompletableFuture for non-blocking operations
CompletableFuture<PutObjectResponse> future = s3AsyncClient.putObject(request);
future.thenAccept(response -> {
// Handle success
}).exceptionally(error -> {
// Handle error
return null;
});
Monitoring and Observability
Enable SDK Metrics
CloudWatchMetricPublisher publisher = CloudWatchMetricPublisher.create();
S3Client s3Client = S3Client.builder()
.overrideConfiguration(b -> b
.addMetricPublisher(publisher))
.build();
CloudWatch Integration
Configure CloudWatch metrics publisher to collect SDK metrics.
CloudWatchMetricPublisher cloudWatchPublisher = CloudWatchMetricPublisher.builder()
.namespace("MyApplication")
.credentialProvider(credentials)
.build();
Custom Metrics
Implement custom metrics for application-specific monitoring.
public class CustomMetricPublisher implements MetricPublisher {
@Override
public void publish(MetricCollection metrics) {
// Implement custom metrics logic
metrics.forEach(metric -> {
System.out.println("Metric: " + metric.name() + " = " + metric.value());
});
}
}
Error Handling
Comprehensive Error Handling
try {
awsOperation();
} catch (SdkServiceException e) {
// Service-specific error
System.err.println("AWS Service Error: " + e.awsErrorDetails().errorMessage());
System.err.println("Error Code: " + e.awsErrorDetails().errorCode());
System.err.println("Status Code: " + e.statusCode());
System.err.println("Request ID: " + e.requestId());
} catch (SdkClientException e) {
// Client-side error (network, timeout, etc.)
System.err.println("Client Error: " + e.getMessage());
} catch (Exception e) {
// Other errors
System.err.println("Unexpected Error: " + e.getMessage());
}
Retry Configuration
RetryPolicy retryPolicy = RetryPolicy.builder()
.numRetries(3)
.retryCondition(RetryCondition.defaultRetryCondition())
.backoffStrategy(BackoffStrategy.defaultStrategy())
.build();
Testing Strategies
Local Testing with LocalStack
@TestConfiguration
public class LocalStackConfig {
@Bean
public S3Client s3Client() {
return S3Client.builder()
.endpointOverride(URI.create("http://localhost:4566"))
.credentialsProvider(StaticCredentialsProvider.create(
AwsBasicCredentials.create("test", "test")))
.build();
}
}
Testcontainers Integration
@Testcontainers
@SpringBootTest
public class AwsIntegrationTest {
@Container
static LocalStackContainer localstack = new LocalStackContainer(DockerImageName.parse("localstack/localstack:3.0"))
.withServices(LocalStackContainer.Service.S3);
@DynamicPropertySource
static void configProperties(DynamicPropertyRegistry registry) {
registry.add("aws.endpoint", () -> localstack.getEndpointOverride(LocalStackContainer.Service.S3));
}
}
Configuration Templates
High-Throughput Configuration
ApacheHttpClient highThroughputClient = ApacheHttpClient.builder()
.maxConnections(200)
.connectionTimeout(Duration.ofSeconds(3))
.socketTimeout(Duration.ofSeconds(30))
.connectionTimeToLive(Duration.ofMinutes(10))
.build();
S3Client s3Client = S3Client.builder()
.region(Region.US_EAST_1)
.httpClient(highThroughputClient)
.overrideConfiguration(b -> b
.apiCallTimeout(Duration.ofSeconds(45))
.apiCallAttemptTimeout(Duration.ofSeconds(15)))
.build();
Low-Latency Configuration
ApacheHttpClient lowLatencyClient = ApacheHttpClient.builder()
.maxConnections(50)
.connectionTimeout(Duration.ofSeconds(2))
.socketTimeout(Duration.ofSeconds(10))
.build();
S3Client s3Client = S3Client.builder()
.region(Region.US_EAST_1)
.httpClient(lowLatencyClient)
.overrideConfiguration(b -> b
.apiCallTimeout(Duration.ofSeconds(15))
.apiCallAttemptTimeout(Duration.ofSeconds(3)))
.build();