简体   繁体   中英

How to call an aws java lambda function from another AWS Java Lambda function when both are in same account, same region

I have a java aws lambda function or handler as AHandler that does some stuff eg It has been subscribed to SNS events, It parses that SNS event and log relevant data to the database.

I have another java aws lambda BHandler, Objective of this BHandler to receive a request from AHandler and provide a response back to AHandler. Because BHandler's objective is to provide a response with some json data. and that would be used by the AHandler.

May I see any clear example which tells how we can do such things ?

I saw this example call lambda function from a java class and Invoke lambda function from java

My question talks about that situation, when one aws java lambda function (or handler) calls to another aws java lambda function when both are in same region, same account,same vpc execution stuff, same rights. In that case aws java lambda function can directly call( or invoke) to another or still it has to provide aws key,region etc stuff (as in above links) ? A clear example/explanation would be very helpful.

EDIT

The AHandler who is calling another Lambda function (BHandler) , exist on same account have given complete AWSLambdaFullAccess with everything eg

“iam:PassRole", "lambda:*",

Here is the code to call :

Note : Below code works when I call the same function with everything same from a normal java main function. But its not working like calling from on lambda function (like ALambdaHandler calling BLambdaHandler as a function call). Even its not returning any exception. Its just showing timeout, its got stuck at the code of: lambdaClient.invoke

String awsAccessKeyId = PropertyManager.getSetting("awsAccessKeyId");
        String awsSecretAccessKey = PropertyManager.getSetting("awsSecretAccessKey");
        String regionName = PropertyManager.getSetting("regionName");
        String geoIPFunctionName = PropertyManager.getSetting("FunctionName");

        Region region;
        AWSCredentials credentials;
        AWSLambdaClient lambdaClient;

        credentials = new BasicAWSCredentials(awsAccessKeyId,
                awsSecretAccessKey);

        lambdaClient = (credentials == null) ? new AWSLambdaClient()
                : new AWSLambdaClient(credentials);
        region = Region.getRegion(Regions.fromName(regionName));
        lambdaClient.setRegion(region);


        String returnGeoIPDetails = null;

        try {


            InvokeRequest invokeRequest = new InvokeRequest();
            invokeRequest.setFunctionName(FunctionName);
            invokeRequest.setPayload(ipInput);


            returnDetails = byteBufferToString(
                    lambdaClient.invoke(invokeRequest).getPayload(),
                    Charset.forName("UTF-8"),logger);
        } catch (Exception e) {

            logger.log(e.getMessage());
        }

EDIT I did everything as suggested by others and followed everything. At the end I reached to AWS support, and the problem was related to some VPC configurations stuff, and that got solved.If you have encountered similar stuff, then may be check security configs, VPC stuff.

We have achieved this by using com.amazonaws.services.lambda.model.InvokeRequest. Here is code sample.

public class LambdaInvokerFromCode {
     public void runWithoutPayload(String functionName) {
            runWithPayload(functionName, null);
        }

        public void runWithPayload(String functionName, String payload) {
            AWSLambdaAsyncClient client = new AWSLambdaAsyncClient();
            client.withRegion(Regions.US_EAST_1);

            InvokeRequest request = new InvokeRequest();
            request.withFunctionName(functionName).withPayload(payload);
            InvokeResult invoke = client.invoke(request);
            System.out.println("Result invoking " + functionName + ": " + invoke);
    }



    public static void main(String[] args) {
            String KeyName ="41159569322017486.json";
            String status = "success";
            String body = "{\"bucketName\":\""+DBUtils.S3BUCKET_BULKORDER+"\",\"keyName\":\""+KeyName+"\", \"status\":\""+status+"\"}";
            System.out.println(body);

            JSONObject inputjson = new JSONObject(body); 
            String bucketName = inputjson.getString("bucketName");
            String keyName = inputjson.getString("keyName");
            String Status = inputjson.getString("status");
            String destinationKeyName = keyName+"_"+status;
            LambdaInvokerFromCode obj = new LambdaInvokerFromCode();
            obj.runWithPayload(DBUtils.FILE_RENAME_HANDLER_NAME,body);
        }
}

Edit: For such a scenario, consider using Step Functions .

We had similar problem and tried to gather various implementations to achieve this. Turns out it had nothing to do with the code.

Few basic rules:

  1. Ensure proper policy and role for your lambda function, at minimum: { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs: : : " }, { "Effect": "Allow", "Action": [ "lambda:InvokeFunction" ], "Resource": [ " " ] } ] }
  2. Have functions in same regions.

  3. No VPC configurations needed. If your applications have VPC, make sure your lambda function has appropriate role policy (refer AWSLambdaVPCAccessExecutionRole)

  4. Most important (primarily why it was failing for us), set right timeouts and heap sizes. Calling Lambda is going to wait until called one is finished. Simple math of 2x the called lambda values works. Also this was only with java lambda function calling another java lambda function. With node js lambda function calling another lambda function did not have this issue.

Following are some implementations that works for us:

  1. Using service interface


    import com.amazonaws.regions.Regions;
    import com.amazonaws.services.lambda.AWSLambdaAsyncClientBuilder;
    import com.amazonaws.services.lambda.invoke.LambdaInvokerFactory;
    import com.amazonaws.services.lambda.runtime.Context;
    import com.amazonaws.services.lambda.runtime.RequestHandler;

    public class LambdaFunctionHandler implements RequestHandler {

        @Override
        public String handleRequest(Object input, Context context) {
            context.getLogger().log("Input: " + input);

            FineGrainedService fg = LambdaInvokerFactory.builder()
                    .lambdaClient(
                            AWSLambdaAsyncClientBuilder.standard()
                            .withRegion(Regions.US_EAST_2)
                            .build()
                    )
                    .build(FineGrainedService.class);

            context.getLogger().log("Response back from FG" + fg.getClass());

            String fgRespone = fg.callFineGrained("Call from Gateway");
            context.getLogger().log("fgRespone: " + fgRespone);

            // TODO: implement your handler
            return "Hello from Gateway Lambda!";
        }

    }


    import com.amazonaws.services.lambda.invoke.LambdaFunction;

    public interface FineGrainedService {

        @LambdaFunction(functionName="SimpleFineGrained")
        String callFineGrained(String input);
    }


  1. Using invoker


    import java.nio.ByteBuffer;

    import com.amazonaws.services.lambda.AWSLambdaClient;
    import com.amazonaws.services.lambda.model.InvokeRequest;
    import com.amazonaws.services.lambda.runtime.Context;
    import com.amazonaws.services.lambda.runtime.RequestHandler;

    public class LambdaFunctionHandler implements RequestHandler {

        @Override
        public String handleRequest(Object input, Context context) {
            context.getLogger().log("Input: " + input);

            AWSLambdaClient lambdaClient = new AWSLambdaClient();
            try {
                InvokeRequest invokeRequest = new InvokeRequest();
                invokeRequest.setFunctionName("SimpleFineGrained");
                invokeRequest.setPayload("From gateway");

                context.getLogger().log("Before Invoke");
                ByteBuffer payload = lambdaClient.invoke(invokeRequest).getPayload();
                context.getLogger().log("After Inoke");

                context.getLogger().log(payload.toString());
                context.getLogger().log("After Payload logger");

            } catch (Exception e) {
                // TODO: handle exception
            }

            // TODO: implement your handler
            return "Hello from Lambda!";
        }

    }

AWSLambdaClient should be created from builder.

You can use LambdaClient to invoke Lambda asynchronously by passing InvocationType.EVENT parameter. Look at an example:

LambdaClient lambdaClient = LambdaClient.builder().build();
InvokeRequest invokeRequest = InvokeRequest.builder()
        .functionName("functionName")
        .invocationType(InvocationType.EVENT)
        .payload(SdkBytes.fromUtf8String("payload"))
        .build();
InvokeResponse response = lambdaClient.invoke(invokeRequest);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM