← назад к разделу

Код, говорящий с AWS, выглядит одинаково независимо от того, где он запущен — EC2, EKS или ноутбук разработчика. Достигается это тремя вещами: SDK v2 с его единообразными клиентами, credentials chain (код не знает, откуда берутся права) и LocalStack в тестах. Разберём все три плюс Spring Cloud AWS — обвязку, избавляющую от шаблонного кода.

SDK v2: клиенты как бины

Официальный SDK — software.amazon.awssdk:* (v2; v1 на com.amazonaws — устаревшее поколение, в новом коде ему делать нечего). Один клиент на сервис AWS, потокобезопасный, переиспользуемый — то есть бин:

@Configuration
public class AwsConfig {

    @Bean
    SqsClient sqsClient() {
        return SqsClient.builder()
            .region(Region.EU_CENTRAL_1)
            .build();
    }

    @Bean
    S3Client s3Client() {
        return S3Client.builder()
            .region(Region.EU_CENTRAL_1)
            .build();
    }
}

Знакомые мотивы: builder-конфигурация, явные таймауты через overrideConfiguration (дефолты SDK разумны, но resilience-дисциплина требует их знать), встроенные ретраи с backoff — которые надо учитывать, проектируя свои: ретрай поверх ретрая SDK — та же лавина, что и ретрай поверх mesh.

Credentials chain: откуда берутся права

Ни в одном из примеров выше нет ключей — и это не упрощение, а суть. SDK ищет креды по цепочке: переменные окружения → профиль из ~/.aws/ → web identity token (IRSA в EKS) → роль контейнера/инстанса. Эффект: один и тот же код на ноутбуке работает под вашим SSO-профилем, в EKS — под ролью ServiceAccount-а, в ECS — под task role. Конфигурируется не код, а окружение.

Антипаттерн, закрывающий это преимущество: accessKeyId/secretKey в application.yml. Ключи утекают через git и логи, не ротируются и превращают перенос сервиса в квест. Если в репозитории есть строка aws.secret — это инцидент, который ещё не случился. (Gitleaks из security-гайда ловит ровно это.)

Spring Cloud AWS: меньше обвязки

Поверх SDK существует Spring Cloud AWS (проект awspring) — автоконфигурация клиентов из application.yml и высокоуровневые удобства. Самое полезное — SQS-листенер в стиле Kafka:

@Component
public class OrderEventsSqsConsumer {

    @SqsListener("order-events")
    public void handle(OrderEventPayload payload) {
        dispatcher.dispatch(new RegisterOrderEventUseCase(payload));
    }
}

Висибилити-таймаут, удаление после успеха, поллинг — под капотом; ваша забота — идемпотентность обработчика (SQS — at-least-once, как и всё в этом мире). Вторая полезная интеграция — конфигурация и секреты из Parameter Store/Secrets Manager как property source: spring.config.import: aws-secretsmanager:/prod/order-service/ — и секреты не существуют нигде, кроме Secrets Manager (подробнее).

S3-паттерны из Spring — загрузка, presigned URLs, multipart — разобраны отдельной статьёй в object-storage разделе; здесь не дублируем.

Тестирование: LocalStack

LocalStack — локальная имитация AWS API; с Testcontainers интеграционный тест против «AWS» выглядит как обычный тест против PG:

@Testcontainers
class OrderEventsSqsConsumerTest {

    @Container
    static LocalStackContainer localstack =
        new LocalStackContainer(DockerImageName.parse("localstack/localstack:3"))
            .withServices(SQS);

    @DynamicPropertySource
    static void aws(DynamicPropertyRegistry registry) {
        registry.add("spring.cloud.aws.endpoint", () -> localstack.getEndpoint().toString());
        registry.add("spring.cloud.aws.region.static", localstack::getRegion);
    }

    @Test
    void processesOrderEvent() {
        sendToQueue("order-events", paidOrderPayload());
        await().untilAsserted(() -> assertThat(repository.findEvents()).hasSize(1));
    }
}

Границы честности: LocalStack отлично имитирует API-контракты (очереди, бакеты, события) и не имитирует IAM-политики и лимиты — права проверяются только на стейдже. Тестовая стратегия та же, что в общем гайде: бизнес-логика — без AWS вовсе (порт + мок), интеграция адаптера — LocalStack, права и сеть — стейдж.

Цена вызовов: о чём помнить в коде

Облако превращает некоторые строчки кода в строчки счёта. Минимальный список для ревью: поллинг SQS — только long polling (waitTimeSeconds: 20, короткий поллинг платит за пустые ответы); S3-запросы в цикле по одному объекту — батчами или префиксным листингом; межсервисный обмен — внутри AZ/региона, NAT-трафик к AWS-сервисам — через VPC endpoints; CloudWatch-метрики со взрывной кардинальностью — те же правила, но с прайс-листом. Не оптимизация, а гигиена: цена за запрос видна заранее, в отличие от инцидента.

Антипаттерны

АнтипаттернЧем кончаетсяЧто взамен
Ключи в application.yml / gitУтечка, ротация руками, инцидентCredentials chain: IRSA, task role, SSO
SDK v1 в новом кодеДва SDK в одном сервисе, старые багиТолько software.amazon.awssdk
Свой retry поверх retry SDKЛавина повторов при деградацииНастроить retry SDK, свой — выключить
Юнит-тесты с реальным AWSМедленно, дорого, флаки, нужен интернетПорт+мок; LocalStack для адаптера
Короткий поллинг SQSОплата пустых ответов 24/7Long polling 20s
Клиент на каждый вызовСотни TLS-рукопожатий, утечкиКлиент-бин, один на сервис

Что почитать дальше

  • Fundamentals — IAM и credentials chain со стороны платформы.
  • Spring + AWS SDK для S3 — файловые паттерны подробно.
  • Безопасность и наблюдаемость — Secrets Manager и CloudWatch из кода.
  • Test Strategy Style Guide — куда LocalStack встраивается в пирамиду.