热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

使用SpringBoot将RestClient异步到DynamoDB

1.Overview从Spring框架5.0和SpringBoot2.0开始,该框架提供对异步编程的支持,从2.0版本开始的AWS开发工具包也是如此。在

1. Overview

从Spring框架5.0和Spring Boot 2.0开始,该框架提供对异步编程的支持,从2.0版本开始的AWS开发工具包也是如此。

在本文中,我将通过构建简单的反应式REST应用程序来探索使用异步DynamoDB API和Spring Webflux。 假设我们需要处理HTTP请求以检索或存储一些Event(id:string,body:string)。 事件将存储在DynamoDB中。

It might be easier to simply look at the code on Github and follow it there.

2. Dependencies

让我们从WebFlux和DynamoDB SDK的Maven依赖关系开始

org.springframework.bootspring-boot-starter-webflux2.2.4.RELEASEsoftware.amazon.awssdkdynamodb2.10.65

3. DynamoDB
3.1 Spring Configuration

一个简单的配置就是我们建立了与DynamoDB的连接。 为了测试目的,我们需要指定dynamoEndpoint在Forreal应用程序中,我们需要指定aws区域。

@Configuration
public class AppConfig {@Value("${aws.accessKey}")String accessKey;@Value("${aws.secretKey}")String secretKey;@Value("${dynamodb.endpoint:}")String dynamoEndpoint;@BeanAwsBasicCredentials awsBasicCredentials(){return AwsBasicCredentials.create(accessKey, secretKey);}@BeanDynamoDbAsyncClient dynamoDbAsyncClient(AwsBasicCredentials awsBasicCredentials){DynamoDbAsyncClientBuilder clientBuilder = DynamoDbAsyncClient.builder();clientBuilder.credentialsProvider(StaticCredentialsProvider.create(awsBasicCredentials));if(!dynamoEndpoint.isEmpty()){clientBuilder.endpointOverride(URI.create(dynamoEndpoint));}return clientBuilder.build();}
}

application.yaml带有连接细节

aws:accessKey: anysecretKey: any
dynamodb:endpoint: http://localhost:8000/

3.2 Reactive DynamoDB Service

Unfortunately, second version of AWS SDK doesn’t have support for DynamoDBMapper yet(you can track mapper’s readiness here), so table creation, sending requests and parsing responses need to be done by “low level” API.

在DynamoDbService我们会:

  • 如果不存在则创建表Implement methods for saving and retrieving event

@Service
public class DynamoDbService {public static final String TABLE_NAME &#61; "events";public static final String ID_COLUMN &#61; "id";public static final String BODY_COLUMN &#61; "body";final DynamoDbAsyncClient client;&#64;Autowiredpublic DynamoDbService(DynamoDbAsyncClient client) {this.client &#61; client;}//Creating table on startup if not exists&#64;PostConstructpublic void createTableIfNeeded() throws ExecutionException, InterruptedException {ListTablesRequest request &#61; ListTablesRequest.builder().exclusiveStartTableName(TABLE_NAME).build();CompletableFuture listTableResponse &#61; client.listTables(request);CompletableFuture createTableRequest &#61; listTableResponse.thenCompose(response -> {boolean tableExist &#61; response.tableNames().contains(TABLE_NAME);if (!tableExist) {return createTable();} else {return CompletableFuture.completedFuture(null);}});//Wait in synchronous manner for table creationcreateTableRequest.get();}public CompletableFuture saveEvent(Event event) {Map item &#61; new HashMap<>();item.put(ID_COLUMN, AttributeValue.builder().s(event.getUuid()).build());item.put(BODY_COLUMN, AttributeValue.builder().s(event.getBody()).build());PutItemRequest putItemRequest &#61; PutItemRequest.builder().tableName(TABLE_NAME).item(item).build();return client.putItem(putItemRequest);}public CompletableFuture getEvent(String id) {Map key &#61; new HashMap<>();key.put(ID_COLUMN, AttributeValue.builder().s(id).build());GetItemRequest getRequest &#61; GetItemRequest.builder().tableName(TABLE_NAME).key(key).attributesToGet(BODY_COLUMN).build();return client.getItem(getRequest).thenApply(item -> {if (!item.hasItem()) {return null;} else {Map itemAttr &#61; item.item();String body &#61; itemAttr.get(BODY_COLUMN).s();return new Event(id, body);}});}private CompletableFuture createTable() {CreateTableRequest request &#61; CreateTableRequest.builder().tableName(TABLE_NAME).keySchema(KeySchemaElement.builder().attributeName(ID_COLUMN).keyType(KeyType.HASH).build()).attributeDefinitions(AttributeDefinition.builder().attributeName(ID_COLUMN).attributeType(ScalarAttributeType.S).build()).billingMode(BillingMode.PAY_PER_REQUEST).build();return client.createTable(request);}
}

4. Reactive REST Controller

一个简单的控制器&#xff0c;具有用于通过id检索事件的GET方法和用于在DynamoDB中保存事件的POST方法。 我们可以通过两种方式来做到这一点-用批注实现或摆脱批注并以功能性方式实现。这不会对性能产生影响&#xff0c;在大多数情况下&#xff0c;这完全取决于个人喜好。

4.1 Annotated Controllers

&#64;RestController
&#64;RequestMapping("/event")
public class AnnotatedController {final DynamoDbService dynamoDbService;public AnnotatedController(DynamoDbService dynamoDbService) {this.dynamoDbService &#61; dynamoDbService;}&#64;GetMapping(value &#61; "/{eventId}", produces &#61; MediaType.APPLICATION_JSON_VALUE)public Mono getEvent(&#64;PathVariable String eventId) {CompletableFuture eventFuture &#61; dynamoDbService.getEvent(eventId);return Mono.fromCompletionStage(eventFuture);}&#64;PostMapping(consumes &#61; MediaType.APPLICATION_JSON_VALUE)public void saveEvent(&#64;RequestBody Event event) {dynamoDbService.saveEvent(event);}
}

4.2 Functional Endpoints

这是一个轻量级的函数编程模型&#xff0c;其中的函数用于路由和处理请求。

&#64;Configuration
public class HttpRouter {&#64;Beanpublic RouterFunction eventRouter(DynamoDbService dynamoDbService) {EventHandler eventHandler &#61; new EventHandler(dynamoDbService);return RouterFunctions.route(GET("/eventfn/{id}").and(accept(APPLICATION_JSON)), eventHandler::getEvent).andRoute(POST("/eventfn").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), eventHandler::saveEvent);}static class EventHandler {private final DynamoDbService dynamoDbService;public EventHandler(DynamoDbService dynamoDbService) {this.dynamoDbService &#61; dynamoDbService;}Mono getEvent(ServerRequest request) {String eventId &#61; request.pathVariable("id");CompletableFuture eventGetFuture &#61; dynamoDbService.getEvent(eventId);Mono eventMono &#61; Mono.fromFuture(eventGetFuture);return ServerResponse.ok().body(eventMono, Event.class);}Mono saveEvent(ServerRequest request) {Mono eventMono &#61; request.bodyToMono(Event.class);eventMono.map(dynamoDbService::saveEvent);return ServerResponse.ok().build();}}
}

5. Spring DynamoDB Integration Test
5.1 Maven dependencies

为了与DynamoDB运行集成测试&#xff0c;我们需要DynamoDBLocal&#xff0c;它不是真正的DynamoDB&#xff0c;而是在其之上具有实现的DynamoDB接口的SQLite。

com.amazonawsDynamoDBLocal1.12.0test
org.apache.maven.pluginsmaven-dependency-plugin2.10copytest-compilecopy-dependenciestestso,dll,dylib${project.basedir}/target/native-libs
dynamodb-local-oregonDynamoDB Local Release Repositoryhttps://s3-us-west-2.amazonaws.com/dynamodb-local/release

5.2 DynamoDB server

现在我们需要在测试运行之前启动DynamoDB。 我更喜欢将其作为JUnit类规则来执行&#xff0c;但我们也可以将其作为spring bean来执行。

public class LocalDynamoDbRule extends ExternalResource {protected DynamoDBProxyServer server;public LocalDynamoDbRule() {//here we set the path from "outputDirectory" of maven-dependency-pluginSystem.setProperty("sqlite4java.library.path", "target/native-libs");}&#64;Overrideprotected void before() throws Exception {this.server &#61; ServerRunner.createServerFromCommandLineArgs(new String[]{"-inMemory", "-port", "8000"});server.start();}&#64;Overrideprotected void after() {this.stopUnchecked(server);}protected void stopUnchecked(DynamoDBProxyServer dynamoDbServer) {try {dynamoDbServer.stop();} catch (Exception e) {throw new RuntimeException(e);}}
}

5.3 Running test

现在&#xff0c;我们可以创建一个集成测试&#xff0c;并通过id和save事件测试get事件。

&#64;RunWith(SpringRunner.class)
&#64;SpringBootTest(webEnvironment &#61; SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IntegrationTest {&#64;ClassRulepublic static LocalDynamoDbRule dynamoDbRule &#61; new LocalDynamoDbRule();&#64;Autowiredprivate WebTestClient webTestClient;&#64;Testpublic void getEvent() {// Create a GET request to test an endpointwebTestClient.get().uri("/event/1").accept(MediaType.APPLICATION_JSON).exchange()// and use the dedicated DSL to test assertions against the response.expectStatus().isOk().expectBody(String.class).isEqualTo(null);}&#64;Testpublic void saveEvent() throws InterruptedException {Event event &#61; new Event("10", "event");webTestClient.post().uri("/event/").body(BodyInserters.fromValue(event)).exchange().expectStatus().isOk();Thread.sleep(1500);webTestClient.get().uri("/event/10").accept(MediaType.APPLICATION_JSON).exchange().expectStatus().isOk().expectBody(Event.class).isEqualTo(event);}
}

6. Docker

在这里&#xff0c;我们将准备在docker中运行的应用程序&#xff0c;以便可以将其部署到AWS。

小提示&#xff1a;从Java 10开始&#xff0c;您可以指定JVM将使用多少内存&#xff0c;具体取决于容器内存。 -XX&#xff1a;MaxRAMPercentage &#61; 75.0意味着JVM不会使用超过75&#xff05;的容器内存。

Dockerfile

# Use our standard java12 baseimage
FROM openjdk:12-alpine# Copy the artifact into the container
COPY target/dynamodb-spring-*-exec.jar /srv/service.jar# Run the artifact and expose the default port
WORKDIR /srvENTRYPOINT [ "java", \"-XX:&#43;UnlockExperimentalVMOptions", \"-XX:&#43;ExitOnOutOfMemoryError", \"-XX:MaxRAMPercentage&#61;75.0", \"-Djava.security.egd&#61;file:/dev/./urandom", \"-jar", "service.jar", \"--spring.profiles.active&#61;prod" ]EXPOSE 8080

构建Docker容器本身dockerbuild-tspring-dynamo。 Alsolet&#39;sseewhatwasgeneratedbysudodockerimagels

REPOSITORY TAG IMAGE ID CREATED SIZE
spring-dynamo latest a974d880400e About a minute ago 364MB
openjdk 12-alpine 0c68e7c5b7a0 12 months ago 339MB


终于&#xff0c;我们的poc准备好了&#xff01;

快乐的编码:)

from: https://dev.to//byegor/spring-webflux-with-async-dynamodb-4ig0



推荐阅读
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • iOS超签签名服务器搭建及其优劣势
    本文介绍了搭建iOS超签签名服务器的原因和优势,包括不掉签、用户可以直接安装不需要信任、体验好等。同时也提到了超签的劣势,即一个证书只能安装100个,成本较高。文章还详细介绍了超签的实现原理,包括用户请求服务器安装mobileconfig文件、服务器调用苹果接口添加udid等步骤。最后,还提到了生成mobileconfig文件和导出AppleWorldwideDeveloperRelationsCertificationAuthority证书的方法。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
author-avatar
吕兵涛_836
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有