从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依赖关系开始
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 = "events";public static final String ID_COLUMN = "id";public static final String BODY_COLUMN = "body";final DynamoDbAsyncClient client;@Autowiredpublic DynamoDbService(DynamoDbAsyncClient client) {this.client = client;}//Creating table on startup if not exists@PostConstructpublic void createTableIfNeeded() throws ExecutionException, InterruptedException {ListTablesRequest request = ListTablesRequest.builder().exclusiveStartTableName(TABLE_NAME).build();CompletableFuture
}
一个简单的控制器,具有用于通过id检索事件的GET方法和用于在DynamoDB中保存事件的POST方法。 我们可以通过两种方式来做到这一点-用批注实现或摆脱批注并以功能性方式实现。这不会对性能产生影响,在大多数情况下,这完全取决于个人喜好。
4.1 Annotated Controllers
@RestController
@RequestMapping("/event")
public class AnnotatedController {final DynamoDbService dynamoDbService;public AnnotatedController(DynamoDbService dynamoDbService) {this.dynamoDbService = dynamoDbService;}@GetMapping(value = "/{eventId}", produces = MediaType.APPLICATION_JSON_VALUE)public Mono
}
4.2 Functional Endpoints
这是一个轻量级的函数编程模型,其中的函数用于路由和处理请求。
@Configuration
public class HttpRouter {@Beanpublic RouterFunction
}
5.1 Maven dependencies
为了与DynamoDB运行集成测试,我们需要DynamoDBLocal,它不是真正的DynamoDB,而是在其之上具有实现的DynamoDB接口的SQLite。
5.2 DynamoDB server
现在我们需要在测试运行之前启动DynamoDB。 我更喜欢将其作为JUnit类规则来执行,但我们也可以将其作为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");}@Overrideprotected void before() throws Exception {this.server = ServerRunner.createServerFromCommandLineArgs(new String[]{"-inMemory", "-port", "8000"});server.start();}@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
现在,我们可以创建一个集成测试,并通过id和save事件测试get事件。
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IntegrationTest {@ClassRulepublic static LocalDynamoDbRule dynamoDbRule = new LocalDynamoDbRule();@Autowiredprivate WebTestClient webTestClient;@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);}@Testpublic void saveEvent() throws InterruptedException {Event event = 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);}
}
在这里,我们将准备在docker中运行的应用程序,以便可以将其部署到AWS。
小提示:从Java 10开始,您可以指定JVM将使用多少内存,具体取决于容器内存。 -XX:MaxRAMPercentage = 75.0意味着JVM不会使用超过75%的容器内存。
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:+UnlockExperimentalVMOptions", \"-XX:+ExitOnOutOfMemoryError", \"-XX:MaxRAMPercentage=75.0", \"-Djava.security.egd=file:/dev/./urandom", \"-jar", "service.jar", \"--spring.profiles.active=prod" ]EXPOSE 8080
构建Docker容器本身dockerbuild-tspring-dynamo。 Alsolet'sseewhatwasgeneratedbysudodockerimagels
REPOSITORY TAG IMAGE ID CREATED SIZE
spring-dynamo latest a974d880400e About a minute ago 364MB
openjdk 12-alpine 0c68e7c5b7a0 12 months ago 339MB
终于,我们的poc准备好了!
快乐的编码:)