简体   繁体   English

Micronaut 与 AWS Lambda 和 SQS

[英]Micronaut with AWS Lambda and SQS

I have created simple application with Micronaut, and Graalvm and want to deploy it to the AWS Lambda and get it triggered from the SQS and process the messages.我已经使用 Micronaut 和 Graalvm 创建了简单的应用程序,并希望将其部署到 AWS Lambda 并从 SQS 触发并处理消息。 But somehow, it is not working as expected.但不知何故,它没有按预期工作。

To build this application, I use command: gradlew buildNativeLambda要构建这个应用程序,我使用命令: gradlew buildNativeLambda

Below is my setup:以下是我的设置:

Micronaut application: Micronaut 应用程序:

Trial 1: (not working)试用1:(不工作)

import com.example.services.TestService;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import jakarta.inject.Inject;

import java.util.List;
import java.util.Map;

@Controller
public class Controller {

    @Inject
    private TestService testService;

    @Post(consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON)
    public Map<String, Object> indexPost(@Body List<Map<String, Object>> requestBody) throws JsonProcessingException {
        return this.testService.handleRequest(requestBody.get(0));
    }
}

Prepare a build and upload it to Lambda and add the following handler: io.micronaut.function.aws.proxy.MicronautLambdaHandler准备构建并将其上传到 Lambda 并添加以下处理程序: io.micronaut.function.aws.proxy.MicronautLambdaHandler

It just works fine when I try to trigger the lambda from "Test" tab on lambda management console .当我尝试从 lambda 管理控制台上的“测试”选项卡中触发 lambda 时,它工作正常 However, when I add the SQS as trigger, and send the message to the SQS, the message does get disappeared from the queue (thus it is being read), but I see no logs in the lambda and the functionality is also not achieved.但是,当我将 SQS 添加为触发器并将消息发送到 SQS 时,消息确实从队列中消失了(因此它正在被读取),但我在 lambda 中没有看到任何日志,并且功能也没有实现。 Thus it does not work with the SQS.因此它不适用于 SQS。

Trial 2: (not working)试验2:(不工作)

import com.amazonaws.services.lambda.runtime.events.SQSEvent;
import com.example.services.TestService;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.micronaut.core.annotation.Introspected;
import io.micronaut.function.aws.MicronautRequestHandler;
import jakarta.inject.Inject;

import java.util.List;

@Introspected
public class SQSEventHandler extends MicronautRequestHandler<SQSEvent, Void> {

    @Inject
    private TestService testService;

    @Override
    public Void execute(SQSEvent input) {
        System.out.println("EVENT PROCESSING STARTS ===>");
        List<SQSEvent.SQSMessage> messages = input.getRecords();
        System.out.println("Number of messages:::" + messages.size());
        for (SQSEvent.SQSMessage single: messages) {
            try {
                System.out.println("Message body::: " + single.getBody());
                this.testService.handleRequest(single.getBody());
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        System.out.println("<=== EVENT PROCESSING ENDS");
        return null;
    }
}

Prepare a build and upload it to Lambda and add the following handler: com.example.eventHandlers.SQSEventHandler准备构建并将其上传到 Lambda 并添加以下处理程序: com.example.eventHandlers.SQSEventHandler

Now it does the same behavior with the SQS message as previous one, the message gets disappeared from the queue, but no effect in lambda.现在它对 SQS 消息执行与前一个相同的行为,消息从队列中消失,但在 lambda 中无效。

Edit - 31-08-2022:编辑 - 2022 年 8 月 31 日:

I use below body as an event to test from "Test" tab in lambda console, and this is to check the AWS Gateway event and it does works fine as we have @Controller class.我使用下面的正文作为事件从 lambda 控制台中的“测试”选项卡进行测试,这是为了检查 AWS 网关事件,它工作正常,因为我们有@Controller class。 Without @Controller it fails.没有@Controller ,它会失败。

Event data:事件数据:

{
  "path": "/",
  "httpMethod": "POST",
  "headers": {
    "Accept": "application/json"
  },
  "body": "<my req body>"
}

But when I try to test it with the SQS event, it fails.但是当我尝试使用 SQS 事件对其进行测试时,它失败了。 I have configured com.example.eventHandlers.SQSEventHandler as event handler in lambda for this test.我已将 com.example.eventHandlers.SQSEventHandler 配置为com.example.eventHandlers.SQSEventHandler中的事件处理程序用于此测试。 Below is the event data which I used to test and the results:以下是我用来测试的事件数据和结果:

Event data :事件数据

{
    "Records": [
        {
            "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d",
            "receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...",
            "body": "{\"action\": \"READ_ALL\"}",
            "attributes": {
                "ApproximateReceiveCount": "1",
                "SentTimestamp": "1545082649183",
                "SenderId": "AIDAIENQZJOLO23YVJ4VO",
                "ApproximateFirstReceiveTimestamp": "1545082649185"
            },
            "messageAttributes": {},
            "md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3",
            "eventSource": "aws:sqs",
            "eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:my-queue",
            "awsRegion": "us-east-2"
        }
    ]
}

Results:结果:

{
  "statusCode": 405,
  "multiValueHeaders": {
    "Allow": [
      "POST"
    ],
    "Content-Type": [
      "application/json"
    ]
  },
  "body": "{\"message\":\"Method Not Allowed\",\"_links\":{\"self\":{\"href\":\"https://nullnull\",\"templated\":false}},\"_embedded\":{\"errors\":[{\"message\":\"Method [GET] not allowed for URI [https://nullnull]. Allowed methods: [POST]\"}]}}",
  "isBase64Encoded": false
}

If your AWS Lambda function is not generating entries in Amazon CloudWatch Logs, then it normally indicates that the Lambda function has insufficient permissions to use CloudWatch Logs. If your AWS Lambda function is not generating entries in Amazon CloudWatch Logs, then it normally indicates that the Lambda function has insufficient permissions to use CloudWatch Logs.

This can be fixed by adding permissions to the IAM Role associated with the Lambda function.这可以通过向与 Lambda function 关联的 IAM 角色添加权限来解决。 Assign it the AWSLambdaBasicExecutionRole managed policy, which grants permission to write to CloudWatch Logs.为其分配AWSLambdaBasicExecutionRole托管策略,该策略授予写入 CloudWatch Logs 的权限。

See: AWS managed policies for Lambda features请参阅: 针对 Lambda 功能的 AWS 托管策略

Finally made it working, if anyone is looking for the solution:最后让它工作,如果有人正在寻找解决方案:

build.gradle: build.gradle:

.
.
dependencies {
    annotationProcessor 'io.micronaut:micronaut-inject-java'
    annotationProcessor("io.micronaut.data:micronaut-data-processor")
    implementation("io.micronaut:micronaut-jackson-databind")
    implementation("io.micronaut.data:micronaut-data-hibernate-jpa")
    implementation("io.micronaut.sql:micronaut-jdbc-hikari")
    implementation("jakarta.annotation:jakarta.annotation-api")
    runtimeOnly("ch.qos.logback:logback-classic")
    runtimeOnly("mysql:mysql-connector-java")
    compileOnly("org.graalvm.nativeimage:svm")
    implementation("io.micronaut.aws:micronaut-function-aws") <--- IMP!!!
    implementation("io.micronaut.aws:micronaut-function-aws-custom-runtime") <--- IMP!!!
    implementation("io.micronaut:micronaut-validation")
    testImplementation("io.micronaut:micronaut-http-client")
}
.
.

Application.java: (The starting point of the application) Application.java:(申请的起点)

public class Application extends AbstractMicronautLambdaRuntime<CustomSQSEvent, String, CustomSQSEvent, String> {
    public static void main(String[] args) throws MalformedURLException {
//        Micronaut.run(Application.class, args); // We don't need this now
        new Application().run(args);
    }

    @Override
    @Nullable
    protected RequestHandler<CustomSQSEvent, String> createRequestHandler(String... args) {
        try {
            return new SQSEventHandler(createApplicationContextBuilderWithArgs(args));
        } catch (ContainerInitializationException e) {
            throw new ConfigurationException("Exception thrown instantiating SQSEventHandler", e);
        }
    }
}

CustomSQSEvent.java: (This is needed to handle the serialization/deserialization issues with the message. This is the exact copy of class com.amazonaws.services.lambda.runtime.events.SQSEvent with few annnotations.) CustomSQSEvent.java: (This is needed to handle the serialization/deserialization issues with the message. This is the exact copy of class com.amazonaws.services.lambda.runtime.events.SQSEvent with few annnotations.)

@Introspected <--- Very IMP!!!
public class CustomSQSEvent implements Serializable {
    
    @JsonProperty("Records") <--- Very IMP!!!
    private List<CustomSQSEvent.SQSMessage> records;
    
    @Introspected <--- Very IMP!!!
    public static class SQSMessage implements Serializable, Cloneable {
        ...
    }

     /**
     * Default constructor
     */
     public CustomSQSEvent() {
     }

     .
     .
     .

}

SQSEventHandler.java: (The actual magic happens here) SQSEventHandler.java:(真正的魔法发生在这里)

@Introspected
public class SQSEventHandler implements RequestHandler<CustomSQSEvent, String>, ApplicationContextProvider, Closeable {


    protected final MicronautLambdaContainerHandler handler;

    public SQSEventHandler() throws ContainerInitializationException {
        this.handler = new MicronautLambdaContainerHandler();
    }

    public SQSEventHandler(ApplicationContextBuilder applicationContextBuilder) throws ContainerInitializationException {
        this.handler = new MicronautLambdaContainerHandler(applicationContextBuilder);
    }

    public SQSEventHandler(ApplicationContext applicationContext) throws ContainerInitializationException {
        this.handler = new MicronautLambdaContainerHandler(applicationContext);
    }

    @Override
    public String handleRequest(CustomSQSEvent input, Context context) {
        System.out.println("EVENT PROCESSING STARTS ===>");
        TestService testService = this.getApplicationContext().getBean(TestService.class);
        
        List<CustomSQSEvent.SQSMessage> messages = input.getRecords();

        for (CustomSQSEvent.SQSMessage single: messages) {
            try {
                System.out.println("Message body::: " + single.getBody());
                testService.handleRequest(single.getBody());
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        System.out.println("<=== EVENT PROCESSING ENDS");
        return "";
    }

    @Override
    public ApplicationContext getApplicationContext() {
        return this.handler.getApplicationContext();
    }

    @Override
    public void close() {
        this.getApplicationContext().close();
    }
}

Removed HomeController.java (the class which has @Controller ).删除了HomeController.java (具有@Controller的 class )。

And finally build the project using following command: gradlew buildNativeLambda -Pmicronaut.runtime=lambda最后使用以下命令构建项目: gradlew buildNativeLambda -Pmicronaut.runtime=lambda

Upload the created zip in the lambda and add the following as handler: com.example.eventHandlers.SQSEventHandler .将创建的zip上传到 lambda 中,并添加以下内容作为处理程序: com.example.eventHandlers.SQSEventHandler This is the path to our custom event handler.这是我们自定义事件处理程序的路径。

Compiled solution from below references:从以下参考中编译的解决方案:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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