一个项目中的多个 Spring Cloud Functions 用于在 AWS Lambda 上部署

Posted

技术标签:

【中文标题】一个项目中的多个 Spring Cloud Functions 用于在 AWS Lambda 上部署【英文标题】:Multiple Spring Cloud Functions in one project for deployment on AWS Lambda 【发布时间】:2019-04-26 10:04:10 【问题描述】:

我可能需要一些帮助...

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

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

package example;

@SpringBootApplication
public class SpringCloudFunctionApiGatewayApplication 

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


函数 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;
    

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

package example;

public class TestFunctionHandler extends SpringBootApiGatewayRequestHandler 


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

日志输出如下所示:

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 

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

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

但是,这不适用于 Spring Cloud Function(因为我们只能有一个处理程序,在这种情况下是 TestFunctionHandler)。 The documentations mentions 可以通过在 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;
    

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

function.name = example.StoreFunction

如果我创建一个环境变量 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 

我们非常感谢每一个帮助!

【问题讨论】:

有什么方法可以在运行时使用 Spring Cloud Function 在同一 AWS Lambda 中同时拥有多个 EndPoints 功能? @pellyadolfo 我认为您在谈论人们所说的“单片 Lambda”(内部具有多个函数的单个 lambda)。在这种情况下,您的 lambda 中应该有一个函数(只有在代理类型 API 网关请求等事件上由 lambda 容器触发),然后这将路由到不同的内部函数。尽管现在从架构的角度来看,使用“Monolithic Lambda”与“Micro Lambda”(具有单个功能的单个 lambda)哪个是有争议的,它们中的每一个都有自己的优点和缺点,并且完全基于应用程序的需求。 @AbhishekChatterjee 最后,我以带有RouterFunction的单片lambda的方式做到了。我同意你的看法......如果你有足够的资源。就我而言,我是我项目的唯一开发人员,并且不想维护一堆 lambda 函数。实际上效果很好。 @pellyadolfo,没错,这完全取决于场景。但是,您似乎正在使用 env 变量在两个函数之间进行路由,但这是在部署时决定的静态绑定。您可以考虑一个推荐选项,使用 spring 内置路由器功能。其实我们都在同一条路上,试图以不同的方式实现相同的目标。在我的spring-routing方式中,我面临着一些与实现相关的路由问题。如果您可以提供帮助,请查看***.com/questions/63389412/… 【参考方案1】:

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

在查看上下文中所有可用 bean 的列表后,很明显我必须使用类名,但以小写字母开头,i。 e. 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;
    

然后,我将它们注册为 bean,例如。 G。像这样:

@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();
    

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

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

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

顺便说一句,我为此创建了a tutorial。

【讨论】:

你好@margul .. 我有同样的问题,但是,你的解决方案还不清楚。如何在一个属性中同时拥有两个函数名?你能详细说明一下吗?谢谢! 我用更多细节编辑了我的答案。如果您需要更多信息,请告诉我。 有什么办法,我可以在项目中定义多个函数,并在AWS中单独部署每个函数吗?就像某种方式为每个函数生成 JAR 并部署每个函数 @markusgulden:嗨,马克,您是否有任何 aws lambda 文档的官方源链接,其中指定我们只能将 ONE FUNCTION 用于 lambda。这对我真的很有帮助.. @markusgulden 感谢您的教程。但就云无关部分而言,我对此有一个评论。我认为,您试图使供应商独立的数据建模和存储库策略,在现实世界的应用程序中并不是那么简单(尽管对于指出与云无关的概念非常有帮助),因为数据模型本身可以根据数据库(如 mysql示例应用程序的数据模型不同于同一应用程序的 Cassandra 数据模型),因此存储库接线。在我看来,很难以与云无关的方式概括实体/存​​储库。【参考方案2】:

我遇到了同样的问题。在我的项目中,我有多个 springs 函数想根据请求动态调用它们。

我的功能:

功能1:

    public class Login implements Function<AuthenticationRequestDTO, JwtTokenDTO>
        public JwtTokenDTO apply(AuthenticationRequestDTO user) 
           // Logic
        
   

功能2:

public class UserInfo implements Function<JwtTokenDTO, WhoAmIDTO>
    public WhoAmIDTO apply(JwtTokenDTO jwt) 
       // Logic 
    

在我的 Spring Boot 应用程序类中,我添加了 customRouter,它实现了接口 MessageRoutingCallback 并从 http 标头返回特定于 http 请求的“func_name”。这样我们就不需要 Static FUNCTION_NAME 或 spring.cloud.function.definition 不支持动态函数路由。

从 UI 发送请求集标头“func_name”到您要调用的 spring 函数。

我的应用程序类:

@SpringBootApplication
public class Application 
    
    public static void main(String[] args) 
        SpringApplication.run(TestApplication.class, args);
    

    @Bean
    public MessageRoutingCallback customRouter() 
        return new MessageRoutingCallback() 
            @Override
            public String functionDefinition(Message<?> message) 
                String fnName = (String) message.getHeaders().get("func_name");
                return fnName;
            
        ;
    

在 AWS Handler 中指向:org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest

【讨论】:

以上是关于一个项目中的多个 Spring Cloud Functions 用于在 AWS Lambda 上部署的主要内容,如果未能解决你的问题,请参考以下文章

spring cloud config:如何使用多个配置

Spring Cloud Alibaba 本地调试方案

Spring Cloud构建微服务架构——服务注册与发现

Spring Cloud构建微服务架构服务注册与发现

Spring Cloud Alibaba 本地调试方案

Spring Cloud Alibaba 本地调试方案