在 AWS Lambda 上部署的一个项目中的多个 Spring Cloud 函数

mar*_*gul 5 spring-boot spring-cloud aws-lambda

我在这里需要一些帮助...

我正在使用 Spring Cloud Function,我想使用适用于 AWS 的适配器在 AWS Lambda 上部署我的函数。

我的应用程序类如下所示:

package example;

@SpringBootApplication
public class SpringCloudFunctionApiGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringCloudFunctionApiGatewayApplication.class, args);
    }

}
Run Code Online (Sandbox Code Playgroud)

函数 1 如下所示:

package example;

@Component
public class StoreFunction implements Consumer<Message<DemoEntity>>{

    @Override
    public void accept(Message<DemoEntity> t) {

        System.out.println("Stored entity " + ((DemoEntity)t.getPayload()).getName());
        return;
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,我的函数处理程序如下所示:

package example;

public class TestFunctionHandler extends SpringBootApiGatewayRequestHandler {

}
Run Code Online (Sandbox Code Playgroud)

此设置完美运行。部署到 Lambda 时,我在 AWS 控制台中提供example.TestFunctionHandler作为处理程序,Spring Cloud 自动识别example.QueryFunction是上下文中的唯一函数。

日志输出如下所示:

START RequestId: 3bd996e7-ef5e-11e8-9829-1f50e2b93b6c Version: $LATEST
20:27:45.821 [main] INFO org.springframework.cloud.function.adapter.aws.SpringFunctionInitializer - Initializing: class de.margul.awstutorials.springcloudfunction.apigateway.SpringCloudFunctionApiGatewayApplication

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                        

2018-11-23 20:27:48.221  INFO 1 --- [           main] lambdainternal.LambdaRTEntry             : Starting LambdaRTEntry on ip-10-153-127-174.ec2.internal with PID 1 (/var/runtime/lib/LambdaJavaRTEntry-1.0.jar started by sbx_user1060 in /)
2018-11-23 20:27:48.242  INFO 1 --- [           main] lambdainternal.LambdaRTEntry             : No active profile set, falling back to default profiles: default
2018-11-23 20:27:52.081  INFO 1 --- [           main] lambdainternal.LambdaRTEntry             : Started LambdaRTEntry in 5.941 seconds (JVM running for 7.429)
Stored entity John Doe
END RequestId: 3bd996e7-ef5e-11e8-9829-1f50e2b93b6c
REPORT RequestId: 3bd996e7-ef5e-11e8-9829-1f50e2b93b6c  Duration: 7113.98 ms    Billed Duration: 7200 ms    Memory Size: 1088 MB    Max Memory Used: 113 MB 
Run Code Online (Sandbox Code Playgroud)

现在,我的问题来了。我想在一个项目中拥有多个功能。我知道,在 Lambda 上,每个部署只能有一个函数。但是,出于代码维护的原因(在实际项目中有一些共享代码以及配置),我们希望将所有功能都在一个项目中,多次部署该项目,并在部署中定义相关功能。

使用适用于 Lambda 的本机 AWS 开发工具包,这很容易(如第 4.2 节中的此示例中所示):RequestStreamHandler 的一种实现,具有多种方法(即使RequestStreamHandler只有一种handleRequest()方法)。关键是可以将相关函数定义为处理程序:package.ClassName::methodName

但是,这不适用于 Spring Cloud Function(因为我们只能有一个处理程序,在本例中为TestFunctionHandler)。 文档提到可以通过在application.properties 中指定function.name或作为 Lambda 环境变量FUNCTION_NAME来实现多个功能。无论如何,我不明白这一点。

我的函数 2 如下所示:

package example;

@Component
public class QueryFunction implements Function<Message<String>, Message<DemoEntity>>{

    @Override
    public Message<DemoEntity> apply(Message<String> m) {

        String name = m.getPayload();

        DemoEntity response = new DemoEntity();
        response.setName(name);
        Message<DemoEntity> message = MessageBuilder
                .withPayload(response)
                .setHeader("contentType", "application/json")
                .build();
        return message;
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的application.properties,我有这一行:

function.name = example.StoreFunction
Run Code Online (Sandbox Code Playgroud)

如果我创建一个环境变量FUNCTION_NAME,同样适用:example.StoreFunction

如果我现在部署库并触发它,我会得到以下日志:

START RequestId: 67e64098-ef5d-11e8-bdbf-9ddadadef0ce Version: $LATEST
20:21:50.802 [main] INFO org.springframework.cloud.function.adapter.aws.SpringFunctionInitializer - Initializing: class example.SpringCloudFunctionApiGatewayApplication

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                        

2018-11-23 20:21:53.684  INFO 1 --- [           main] lambdainternal.LambdaRTEntry             : Starting LambdaRTEntry on ip-10-153-127-174.ec2.internal with PID 1 (/var/runtime/lib/LambdaJavaRTEntry-1.0.jar started by sbx_user1059 in /)
2018-11-23 20:21:53.687  INFO 1 --- [           main] lambdainternal.LambdaRTEntry             : No active profile set, falling back to default profiles: default
2018-11-23 20:21:57.488  INFO 1 --- [           main] lambdainternal.LambdaRTEntry             : Started LambdaRTEntry in 6.353 seconds (JVM running for 8.326)
No function defined: java.lang.IllegalStateException
java.lang.IllegalStateException: No function defined
    at org.springframework.cloud.function.adapter.aws.SpringFunctionInitializer.apply(SpringFunctionInitializer.java:134)
    at org.springframework.cloud.function.adapter.aws.SpringBootRequestHandler.handleRequest(SpringBootRequestHandler.java:48)

END RequestId: 67e64098-ef5d-11e8-bdbf-9ddadadef0ce
REPORT RequestId: 67e64098-ef5d-11e8-bdbf-9ddadadef0ce  Duration: 7454.73 ms    Billed Duration: 7500 ms    Memory Size: 1088 MB    Max Memory Used: 130 MB 
Run Code Online (Sandbox Code Playgroud)

非常感谢每一个帮助!

mar*_*gul 6

好吧,问题显然是我对 Spring Beans 的了解有限。

在查看上下文中所有可用 bean 的列表后,很明显我必须使用类名,但以小写开头,即function.name = storeFunctionfunction.name = queryFunction

编辑以详细解释我的解决方案:

在我的项目中,我有几个这样的功能:

@Component
public class StoreFunction implements Consumer<String>{

    @Override
    public void accept(String s) {

        // Logic comes here
    }
}

@Component
public class QueryFunction implements Function<String, String>{

    @Override
    public void apply(String s) {

        return s;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,我将它们注册为 bean,例如:

@SpringBootApplication
public class SpringCloudFunctionApiGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringCloudFunctionApiGatewayApplication.class, args);
    }

    @Bean
    StoreFunction storeFunction(){
        return new StoreFunction();
    }

    @Bean
    QueryFunction queryFunction(){
        return new QueryFunction();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,我的 Spring 上下文中有两个 bean storeFunctionqueryFunction(上面@Bean 方法的名称)。

最后,我必须告诉 Spring 要调用哪些函数。这可以通过创建环境变量 FUNCTION_NAME 并将其设置为 bean 名称之一来完成。

当我现在将项目部署到 AWS Lambda 时,我必须告诉 Lambda 调用哪个函数(因为 Lambda 每次部署只能调用一个函数)。

顺便说一句,我为此创建了一个教程

  • 我用更多细节编辑了我的答案。如果您需要更多信息,请与我们联系。 (2认同)