Skip to content

rjozefowicz/rapid-aws

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 

Repository files navigation

rapid-aws

Example lightweight client for AWS services that limits AWS Lambda cold starts for Java. It is pure-Java8 implementation without third-part libraries that increases fat JAR size and cold starts time.

It uses AWS Low-Level API described on https://docs.aws.amazon.com/index.html

Current implementation allows integration with:

  • DynamoDB
  • Lambda function
  • SQS
  • Comprehend

Getting Started

Configuration

The following environment variables can be configured:

  • FORCE_HTTPS: boolean - Some AWS services allow communication via plan HTTP. It can reduce connection time and cold starts a lot. By default this variable is set as false
  • INITIAL_SSL_SOCKETS - Number of initially created SSL sockets during container startup. It can slightly speed up initial connections to AWS services. It is good to match number of sockets with number of AWS services accessed from Lambda function

Example AWS Lambda

This example assumes that there are configured:

  • DynamoDB Table rapid-aws-data created with Primary Key uuid with String type
  • SQS queue rapid-aws-example
  • Another Lambda function detected-entities-processor

It uses org.json as JSON parser for better readability during preparing JSON requests.

In order to increase performance there is a custom SSLSocketFactory implementation that creates pool of sockets to be used by Rapid clients.

JSON request in the following format:

{
  "input": "Text to be persisted in DynamoDB and analyzed by Comprehend"
}

Maven dependencies:

<dependencies>
         <dependency>
            <groupId>pl.r6lab.aws</groupId>
            <artifactId>rapid-aws</artifactId>
            <version>0.1.0</version>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-core</artifactId>
            <version>1.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20180813</version>
        </dependency>
    </dependencies>

Example AWS Lambda handler architecture: Lambda function architecture diagram

Function code:

public class TestFunction implements RequestHandler<Map<String, String>, Void> {

    private static final String RAPID_AWS_DATA_TABLE_NAME = "rapid-aws-data";
    private static final String DETECTED_ENTITIES_PROCESSOR_FUNCTION_NAME = "detected-entities-processor";

    private final RapidClient client = RapidClient.envAware();
    private final RapidLambdaInvocationClient lambdaClient = RapidLambdaInvocationClient.envAware();
    private final RapidSQSClient sqsClient = RapidSQSClient.envAware();

    public Void handleRequest(Map<String, String> data, Context context) {
        String input = data.get("input");
        if (nonNull(input)) {

            // 1. PutItem in DynamoDB
            String putItem = putItem(input);
            Response putItemResponse = client.execute(BasicRequest.of(ServiceName.DYNAMODB, "PutItem", HttpMethod.POST, putItem));

            if (putItemResponse.isSuccess()) {

                // 2. DetectEntities in Comprehend
                String detectEntities = detectEntities(input);
                Response detectEntitiesResponse = client.execute(BasicRequest.of(ServiceName.COMPREHEND, "DetectEntities", HttpMethod.POST, detectEntities));

                if (detectEntitiesResponse.isSuccess()) {

                    // 3. Send message to SQS queue
                    Response sendMessageResponse = sqsClient.execute(SQSRequest.of("SendMessage", detectEntitiesResponse.getPayload(), new HashMap<>(), getQueueUrl(), HttpMethod.POST));
                    if (!sendMessageResponse.isSuccess()) {
                        logError(ServiceName.SQS, detectEntitiesResponse.getPayload());
                    }

                    // 4. Invoke another lambda function
                    Response invokeResponse = lambdaClient.execute(LambdaInvokeRequest.of(DETECTED_ENTITIES_PROCESSOR_FUNCTION_NAME, detectEntitiesResponse.getPayload(), HttpMethod.POST));
                    if (!invokeResponse.isSuccess()) {
                        logError(ServiceName.LAMBDA, invokeResponse.getPayload());
                    }
                } else {
                    logError(ServiceName.COMPREHEND, detectEntitiesResponse.getPayload());
                }

            } else {
                logError(ServiceName.DYNAMODB, putItemResponse.getPayload());
            }
        }
        return null;
    }

    private String putItem(String input) {
        JSONObject request = new JSONObject();
        request.put("TableName", RAPID_AWS_DATA_TABLE_NAME);
        JSONObject item = new JSONObject();
        JSONObject uuid = new JSONObject();
        uuid.put("S", UUID.randomUUID());
        JSONObject value = new JSONObject();
        value.put("S", input);
        item.put("uuid", uuid);
        item.put("value", value);
        request.put("Item", item);
        String jsonValue = request.toString();
        System.out.println(jsonValue);
        return jsonValue;
    }

    private String detectEntities(String input) {
        JSONObject request = new JSONObject();
        request.put("LanguageCode", "en");
        request.put("Text", input);
        String jsonValue = request.toString();
        System.out.println(jsonValue);
        return jsonValue;
    }

    private void logError(ServiceName serviceName, String payload) {
        System.out.println("Something went wrong while accessing " + serviceName.getName());
        System.out.println(payload);
    }

    private String getQueueUrl() {
        return "YOUR_QUEUE_URL";
    }