AWS Lambda (RequestStreamLambda) Проблемы с IOStream - PullRequest
0 голосов
/ 09 мая 2020

У меня есть Java 11 Lambda, который реализует RequestStreamLambda:

public class ListArticlesHandler implements RequestStreamHandler {

    @Inject
    private ListArticlesService listArticlesService;

    @Override
    public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
            throws IOException {

        LambdaLogger logger = context.getLogger();
        logger.log("log me something, please, anything...");
        ObjectMapper om = new ObjectMapper();
        logger.log("again, anything...");

        String event = readEvent(inputStream, logger);
        logger.log("I survived the stringing of the stream!");
        logger.log("did I get the event though?: " + event);
        // LOGGER.info(this, event);
        JsonNode node = JsonNodeFactory.instance.nullNode();
        try {
            node = om.readTree(event);
        } catch (JsonProcessingException e) {
            logger.log("unable to parse event into JSON: " + event);
        }

        JsonNode queryParams = node.get("queryStringParameters");

        List<Article> articles = null;
        if (queryParams != null) {
            JsonNode author = queryParams.get("author");
            if (author != null) {
                articles = listArticlesService.listArticlesCreatedBy(author.asText());
                logger.log(articles != null ? articles.stream().map(Article::getTitle).collect(Collectors.joining()) : "null");
                // LOGGER.info(this, "articles", articles != null ? articles.stream().map(Article::getTitle).collect(Collectors.joining()) : "null");
            }

            JsonNode month = queryParams.get("month");
            JsonNode year = queryParams.get("year");
            if (month != null && year != null) {
                LocalDateTime ldts = LocalDate.of(year.asInt(), month.asInt(), 1).atStartOfDay();
                long millisStart = ldts.toEpochSecond(ZoneOffset.UTC);

                LocalDateTime ldte = LocalDate.of(year.asInt(), month.asInt(), 1).with(TemporalAdjusters.lastDayOfMonth()).atTime(23, 59, 59);
                long millisEnd = ldte.toEpochSecond(ZoneOffset.UTC);

                articles = listArticlesService.listArticlesCreatedBetween(millisStart, millisEnd);
                logger.log(articles != null ? articles.stream().map(Article::getTitle).collect(Collectors.joining()) : "null");
                //LOGGER.info(this, "articles", articles != null ? articles.stream().map(Article::getTitle).collect(Collectors.joining()) : "null");
            }

            JsonNode tag = queryParams.get("tag");
            if (tag != null) {
                articles = listArticlesService.listArticlesWithTag(tag.asText());
                logger.log(articles != null ? articles.stream().map(Article::getTitle).collect(Collectors.joining()) : "null");
                //LOGGER.info(this, "articles", articles != null ? articles.stream().map(Article::getTitle).collect(Collectors.joining()) : "null");
            }

        }

        if (articles != null) {
            StreamResponse<List<Article>> res = new StreamResponse<>(200, 0, articles);
            logger.log(articles.stream().map(Article::getTitle).collect(Collectors.joining()));
            //LOGGER.info(this, "returning articles", articles.stream().map(Article::getTitle).collect(Collectors.joining()));
            String responseJson = om.writeValueAsString(res);
            OutputStreamWriter writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
            writer.write(responseJson);
            writer.close();
        }
    }


    protected String readEvent(InputStream inputStream, LambdaLogger logger) {

        logger.log("running readEvent on inputStream");
        StringBuilder textBuilder = new StringBuilder();
        Reader reader = new BufferedReader(new InputStreamReader
                (inputStream, Charset.forName(StandardCharsets.UTF_8.name())));
        int c = 0;
        try {
            while ((c = reader.read()) != -1) {
                textBuilder.append((char) c);
            }
        } catch (IOException e) {
            logger.log(e.getMessage());
        }

        logger.log("returning event: " + textBuilder.toString());
        return textBuilder.toString();
    }

}

Он подключен к AWS APIGateway через развертывание CloudFormation с использованием следующего шаблона (обратите внимание, что это отрывок из этот шаблон):

Resources:
  UTableArticle:
    Type: AWS::DynamoDB::Table
    Properties:
      KeySchema:
        - AttributeName: id
          KeyType: HASH
      AttributeDefinitions:
        - AttributeName: id
          AttributeType: S
      ProvisionedThroughput:
        ReadCapacityUnits: 1
        WriteCapacityUnits: 1
      TableName: !Sub ${AWS::StackName}-Article
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain

  FunctionAssumeRoleRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
        Version: "2012-10-17"
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'

  DynamoActionsPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Statement:
          - Action:
              - dynamodb:BatchGetItem
              - dynamodb:GetRecords
              - dynamodb:GetShardIterator
              - dynamodb:Query
              - dynamodb:GetItem
              - dynamodb:Scan
              - dynamodb:BatchWriteItem
              - dynamodb:PutItem
              - dynamodb:UpdateItem
              - dynamodb:DeleteItem
            Effect: Allow
            Resource:
              - !GetAtt [ UTableArticle, Arn ]
              - !Ref AWS::NoValue
        Version: "2012-10-17"
      PolicyName: DynamoActionsPolicy
      Roles:
        - !Ref FunctionAssumeRoleRole
  BFunctionListArticles:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket: !Ref ArtefactRepositoryBucket
        S3Key: !Join [ '', [!Ref ArtefactRepositoryKeyPrefix, '.zip' ] ]
      Handler: !Ref 'ListArticlesHandler'
      Role: !GetAtt [ FunctionAssumeRoleRole, Arn ]
      Runtime: java11
      Environment:
        Variables:
          TABLE_NAME: !Ref UTableArticle
          PRIMARY_KEY: id
    DependsOn:
      - DynamoActionsPolicy
      - FunctionAssumeRoleRole

  BFunctionGWPermissionGetArticle:
    Type: AWS::Lambda::Permission
    DependsOn:
      - BlogRestApi
      - BFunctionListArticles
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !GetAtt [ BFunctionListArticles, Arn ]
      Principal: apigateway.amazonaws.com
      SourceArn: !Join ['', ['arn:', !Ref 'AWS::Partition', ':execute-api:', !Ref 'AWS::Region', ':', !Ref 'AWS::AccountId', ':', !Ref BlogRestApi, '/*/GET/article'] ]
  BlogRestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: Article
  AGWDeploymentArticle:
    Type: AWS::ApiGateway::Deployment
    Properties:
      RestApiId: !Ref BlogRestApi
      Description: Automatically created by the RestApi construct
    DependsOn:
      - MethodArticleIdGet
      - MethodArticleIdPatch
      - ResourceArticleId
      - MethodArticleGet
      - MethodArticlePost
      - ResourceArticle
  BAGDeploymentStageProdArticle:
    Type: AWS::ApiGateway::Stage
    Properties:
      RestApiId: !Ref BlogRestApi
      DeploymentId: !Ref AGWDeploymentArticle
      StageName: prod
  ResourceArticle:
    Type: AWS::ApiGateway::Resource
    Properties:
      ParentId: !GetAtt [ BlogRestApi, RootResourceId ]
      PathPart: article
      RestApiId: !Ref BlogRestApi
  MethodArticleGet:
    Type: AWS::ApiGateway::Method
    Properties:
      HttpMethod: GET
      ResourceId: !Ref ResourceArticle
      RestApiId: !Ref BlogRestApi
      AuthorizationType: NONE
      Integration:
        IntegrationHttpMethod: POST
        Type: AWS_PROXY
        Uri: !Join [ "", ['arn:', !Ref 'AWS::Partition', ':apigateway:', !Ref 'AWS::Region', ':lambda:path/2015-03-31/functions/', !GetAtt [ BFunctionListArticles, Arn ], '/invocations' ] ]

CloudFromation развертывается правильно, и я могу выполнять вызовы через cURL для развертывания в целом или go на ресурс шлюза API и проводить там тест. В любом случае вызов Lambda, кажется, останавливается с таймаутом. Что странно в этом, так это то, что первое сообщение журнала:

logger.log("log me something, please, anything...");

действительно выходит, а второе:

logger.log("again, anything...");

- нет. Если я удалю все журналы, у меня будет та же проблема, а именно:

Fri May 08 23:58:44 UTC 2020 : Received response. Status: 200, Integration latency: 3573 ms
Fri May 08 23:58:44 UTC 2020 : Endpoint response headers: {Date=Fri, 08 May 2020 23:58:44 GMT, Content-Type=application/json, Content-Length=114, Connection=keep-alive, x-amzn-RequestId=1bac3448-38d6-4545-9169-665dc555c47b, X-Amz-Function-Error=Unhandled, x-amzn-Remapped-Content-Length=0, X-Amz-Executed-Version=$LATEST, X-Amzn-Trace-Id=root=1-5eb5f230-4ecec6172243f15370738cce;sampled=0}
Fri May 08 23:58:44 UTC 2020 : Endpoint response body before transformations: {"errorMessage":"2020-05-08T23:58:44.545Z 1bac3448-38d6-4545-9169-665dc555c47b Task timed out after 3.00 seconds"}
Fri May 08 23:58:44 UTC 2020 : Lambda execution failed with status 200 due to customer function error: 2020-05-08T23:58:44.545Z 1bac3448-38d6-4545-9169-665dc555c47b Task timed out after 3.00 seconds. Lambda request id: 1bac3448-38d6-4545-9169-665dc555c47b
Fri May 08 23:58:44 UTC 2020 : Method completed with status: 502

Такое ощущение, что я не обработал InputStream должным образом, или я как-то отрубил его, или что-то в этом роде. Документация AWS, которую я прочитал, не дает никаких подсказок относительно того, что здесь происходит. Есть предложения (кроме serveless.com)?

1 Ответ

2 голосов
/ 09 мая 2020

Ваша лямбда: тайм-аут через 3 секунды. Из вашего сообщения об ошибке:

Тайм-аут задачи истек через 3,00 секунды

Я думаю, что увеличение времени ожидания должно помочь. В CloudFormation у вас есть параметр тайм-аута для этого:

Время, в течение которого Lambda позволяет функции запускаться перед ее остановкой. По умолчанию 3 секунды . Максимально допустимое значение - 900 секунд.

...