Статьи

Реактивный Spring Webflux с AWS DynamoDB

AWS выпустила AWS SDK для Java версии 2 , теперь SDK поддерживает неблокирующий ввод-вывод для вызовов API различных сервисов AWS. В этой статье я расскажу об использовании API-интерфейса DynamoDB в AWS SDK 2.x и использовании стека Spring Webflux для демонстрации реактивной конечной точки — таким образом, приложение является реактивным сквозным и предположительно должно использовать ресурсы очень эффективно (у меня есть планы сделайте несколько тестов на этой установке в качестве продолжения).

Детали заявки

Может быть проще просто посмотреть на код и следовать ему там — он доступен в моем репозитории GitHub .

Приложение простое — для выполнения операции CRUD на объекте Hotel, представленном с использованием следующего кода Kotlin:

1
2
3
4
5
6
7
data class Hotel(
        val id: String = UUID.randomUUID().toString(),
        val name: String? = null,
        val address: String? = null,
        val state: String? = null,
        val zip: String? = null
)

Я хочу выставить конечные точки для сохранения и извлечения объекта отеля, а также для получения списка отелей по штатам.

Подробная информация о AWS SDK 2

Имена пакетов API-интерфейса AWS SDK 2 теперь начинаются с префикса «software.amazon.awssdk», клиент для взаимодействия с DynamoDB создается с использованием следующего кода:

1
2
3
4
5
6
7
8
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider
import software.amazon.awssdk.regions.Region
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient
 
val client: DynamoDbAsyncClient = DynamoDbAsyncClient.builder()
        .region(Region.of(dynamoProperties.region))
        .credentialsProvider(DefaultCredentialsProvider.builder().build())
        .build()

После создания экземпляра DynamoDbAsyncClient любая операция, использующая этот клиент, возвращает тип Java 8 CompletableFuture . Например, в спасении гостиничного предприятия:

1
2
3
4
5
6
7
val putItemRequest = PutItemRequest.builder()
        .tableName("hotels")
        .item(HotelMapper.toMap(hotel))
        .build()
         
val result: CompletableFuture<PutItemResponse> =
        dynamoClient.putItem(putItemRequest)

и при получении записи по id:

1
2
3
4
5
6
val getItemRequest: GetItemRequest = GetItemRequest.builder()
        .key(mapOf(Constants.ID to AttributeValue.builder().s(id).build()))
        .tableName(Constants.TABLE_NAME)
        .build()
 
val response: CompletableFuture<GetItemResponse> = dynamoClient.getItem(getItemRequest)

CompletableFuture предоставляет полный набор функций для преобразования результатов при их наличии.

Интеграция с Spring Webflux

Spring Webflux — это реактивный веб-фреймворк. Поддержка неблокирующего ввода-вывода в AWS SDK 2 теперь позволяет писать сквозные динамические и неблокирующие приложения с DynamoDB. Spring Webflux использует реакторное ядро для обеспечения поддержки реактивных потоков, а хитрость интеграции с AWS SDK 2 заключается в преобразовании Java 8 CompletableFuture в тип реакторного ядра следующим образом при получении элемента из DynamoDB по id:

1
2
3
4
5
6
7
8
9
val getItemRequest: GetItemRequest = GetItemRequest.builder()
        .key(mapOf(Constants.ID to AttributeValue.builder().s(id).build()))
        .tableName(Constants.TABLE_NAME)
        .build()
 
return Mono.fromCompletionStage(dynamoClient.getItem(getItemRequest))
        .map { resp ->
            HotelMapper.fromMap(id, resp.item())
        }

Spring Webflux ожидает, что типы возврата различных сигнатур метода конечной точки сети будут иметь реактивные типы, поэтому типичной конечной точкой для получения списка отелей является следующее:

1
2
3
4
@RequestMapping(value = ["/hotels"], method = [RequestMethod.GET])
fun getHotelsByState(@RequestParam("state") state: String): Flux<Hotel> {
    return hotelRepo.findHotelsByState(state)
}

Spring Webflux также поддерживает функциональный способ описания API приложения, поэтому эквивалентный API для извлечения отеля по его идентификатору, но в виде функционального DSL, выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
@Configuration
class HotelAdditionalRoutes {
 
    @Bean
    fun routes(hotelRepo: HotelRepo) = router {
        GET("/hotels/{id}") { req ->
            val id = req.pathVariable("id")
            val response: Mono<ServerResponse> = hotelRepo.getHotel(id)
                    .flatMap { hotel ->
                        ServerResponse.ok().body(BodyInserters.fromObject(hotel))
                    }
            response.switchIfEmpty(ServerResponse.notFound().build())
        }
    }
}

Вывод

AWS SDK 2 упрощает написание сквозных реактивных и неблокирующих приложений. Я использовал Spring Webflux и AWS SDK 2 динамо-клиент, чтобы написать такое приложение здесь. Весь рабочий образец доступен в моем репозитории GitHub — https://github.com/bijukunjummen/boot-with-dynamodb, и в нем содержатся инструкции о том, как запустить локальную версию DynamoDB и использовать ее для тестирования приложения.

Смотреть оригинальную статью здесь: Reactive Spring Webflux с AWS DynamoDB

Мнения, высказанные участниками Java Code Geeks, являются их собственными.