一个项目中的多个Spring Cloud Functions可在AWS Lambda上部署 [英] Multiple Spring Cloud Functions in one project for deployment on AWS Lambda

查看:140
本文介绍了一个项目中的多个Spring Cloud Functions可在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);
    }

}

功能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 是上下文中的唯一函数.

日志输出如下:

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节中): 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;
    }
}

在我的 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 Beans的了解有限.

在查看上下文中所有可用bean的列表之后,很明显,我不得不使用类名,但是以小写字母i开头. e. function.name = storeFunction function.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 storeFunction queryFunction (上面@Bean方法的名称).

最后,我必须告诉Spring要调用哪个函数.这可以通过创建环境变量FUNCTION_NAME并将其设置为Bean名称之一来实现.

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

顺便说一句,我创建了一个教程那个.

I could need some help here...

I'm using Spring Cloud Function, and I want to deploy my functions on AWS Lambda, using the adapter for AWS.

My application class looks like this:

package example;

@SpringBootApplication
public class SpringCloudFunctionApiGatewayApplication {

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

}

Function 1 looks like this:

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;
    }
}

Finally, my function handler looks like this:

package example;

public class TestFunctionHandler extends SpringBootApiGatewayRequestHandler {

}

This setup works perfectly. When deploying to Lambda, I provide example.TestFunctionHandler as handler in the AWS console, and Spring Cloud recognizes automatically that example.QueryFunction is the only function in the context.

The log output looks like this:

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 

Now, my problem comes here. I want to have multiple functions in one project. I know, on Lambda there can be only one function per deployment. However, for code maintenance reasons (there is some shared code as well as configurations in the real project), we want to have all functions in one project, deploy the project multiple times, and define in the deployment, which is the relevant function.

Using the native AWS SDK for Lambda, that was easy (like in this example in section 4.2): One implementation of RequestStreamHandler, with multiple methods (even if RequestStreamHandler has only one handleRequest() method). The point was that one could define the relevant function as handler: package.ClassName::methodName

However, that does not work with Spring Cloud Function (as we can have only one handler, which is TestFunctionHandler in this case). The documentations mentions that multiple functions are possible, by specifying function.name in application.properties, or as Lambda environment variable FUNCTION_NAME. Anyhow, I don't get that working.

My function 2 looks like this:

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;
    }
}

In my application.properties, I have this line:

function.name = example.StoreFunction

The same applies if I create an environment variable FUNCTION_NAME: example.StoreFunction

If I now deploy the library and trigger it, I get the following logs:

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 

Every help is highly appreciated!

解决方案

Ok, the problem was obviously my limited knowledge about Spring Beans.

After I viewed a list of all available beans in the context, it was clear that I had to use the class name, but starting with a lower case, i. e. function.name = storeFunction or function.name = queryFunction.

Edit to explain my solution in detail:

In my project, I have several functions like this:

@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;
    }
}

Then, I register them as beans, e. g. like this:

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

Now, I have the two beans storeFunction and queryFunction (the names of the @Bean methods above) available in my Spring context.

Finally, I have to tell Spring which of the functions to call. That can be done by creating an environment variable FUNCTION_NAME and setting it to one of the bean names.

When I now deploy the project to AWS Lambda, I have to tell Lambda which function the invoke (as Lambda only can invoke one function per deployment).

Btw, I created a tutorial for that.

这篇关于一个项目中的多个Spring Cloud Functions可在AWS Lambda上部署的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆