SpringCloud Function作为SpringCloud家族成员最早在2017年提出,项目主要负责人为Mark Fisher,目前已经来到了3.0版本。SpringCloud Function的出现旨在为快速发展的Serverless市场提供一个Spring的接入路径,使用SpringCloud Function进行无服务(我这里直接称为函数式编程)的项目开发可以大大节约成本,同时对于SpringBoot熟悉的人可以迅速上手。关于无服务和SpringCloud Function的详细资料可以参考网上的其他资料我不再赘述。以下提供一个简单的SpringCloud Function项目工程构建和打包范例。
在开始之前需要知道,目前使用SpringCloud Function需要结合云服务商提供的serverless相关服务,包括:AWS Lambda,Azure Functions,Cloud Functions,国内阿里云,腾讯云,华为云等都提供类似服务,或者开源的自建Serverless平台,包括:IBM OpenWhisk,kubeless,knative等。
创建一个SpringCloud Function应用十分简单,可以直接使用Spring官网提供的Quick start-Spring Initializr快速构建一个Springboot工程,只需在生成Dependencies时勾选SpringCloud Function即可。而后你将得到一个项目工程的压缩包,解压后导入IDE即可运行。
以下完整范例可到我的GitHub仓库获取:https://github.com/jwwam/scfunc.git
推荐使用上述的方式去构建Springboot项目,方便快捷,我这里也给出一个简单的示例,主要是加以说明。
1.先来看下项目工程的目录结构,突出函数式编程我创建一个function包用于管理所有函数。application.properties只有一个配置用于映射函数所在包的位置。pojo包用来管理所有的pojo对象,Java函数是支持接收object的,一般业务中我们需要实体类对象以满足开发需求。
2.使用maven构建,下面是pom配置,注意打包方式为jar,使用Lombok插件精简代码量。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zyq</groupId>
<artifactId>scfunc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>scfunc</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-function-web</artifactId>
<!--<version>1.0.1.RELEASE</version>-->
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.主启动类部分,这里提供了多个Function,使用@Bean注入即可调用,无需多余的配置。先来看看代码
package com.zyq.scfunc;
import com.zyq.scfunc.pojo.Foo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import reactor.core.publisher.Flux;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
@SpringBootApplication
@Slf4j
public class ScfuncApplication {
public static void main(String[] args) {
SpringApplication.run(ScfuncApplication.class, args);
}
@Bean
public Function<String, String> reverseString() {
return value -> new StringBuilder(value).reverse().toString();
}
@Bean
public Function<String, String> uppercase() {
return value -> value.toUpperCase();
}
@Bean
public Supplier<Flux<Foo>> words() {
return () -> Flux.fromArray(new Foo[]{new Foo("foo"), new Foo("bar")}).log();
}
@Bean
public Function<Foo, List> word() {
return ss -> {
log.info("调用word成功,入参: "+ss.toString());
return Arrays.asList(ss.getValue().split(","));
};
}
@Bean
public Function<Flux<String>, Flux<String>> lowerCase() {
return flux -> flux.map(value -> value.toLowerCase());
}
}
这里有几点必须了解:
(1)函数式编程主要使用Java8提供的util包中Function类(接口),关于Function的使用可以自行学习,需要注意的是Function使用lambda表达式编写主要业务代码。
(2)Function<T, R>其中T表示入参类型,R表示返回值类型,主类中提供了一个word()方法接收实体类入参,返回List数组。
(3)Flux可以理解为包装类,内部提供了丰富的方法用于函数计算和数值处理。
(4)函数必须使用@Bean注解,否则无法找到函数入口。
4.同理,除了在主类中标记Function外我们还可以创建独立的函数方法,比如在functions包下创建Greeter类(即Greeter函数)
package com.zyq.scfunc.functions;
/**
* @ClassName: Greeter
* @Auther: zhangyingqi
* @Date: 2019/8/26 17:38
* @Description:
*/
import java.util.function.Function;
public class Greeter implements Function<String, String> {
@Override
public String apply(String s) {
return "Hello " + s + ", and welcome to Spring Cloud Function!!!";
}
}
Greeter必须实现Function接口才可以被标记为函数,这里只需重写apply方法即完成函数主体内容。
5.下面给出POJO类,其中的内部方法有几个是摘抄自他处,可以自行删除
package com.zyq.scfunc.pojo;
import lombok.Data;
/**
* @ClassName: Foo
* @Auther: zhangyingqi
* @Date: 2019/8/27 15:16
* @Description:
*/
@Data
public class Foo {
private String value;
Foo() {
}
public String lowercase() {
return value.toLowerCase();
}
public Foo(String value) {
this.value = value;
}
public String uppercase() {
return value.toUpperCase();
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
6.最后是配置文件
spring.cloud.function.scan.packages=com.zyq.scfunc.functions
需要指出要扫描的function包地址,这样才能映射到functions包下面的函数
7.打包发布
使用maven打包即可
8.运行及接口测试
IDE启动测试或者通过控制台命令行启动jar
我这里使用命令行启动测试,环境为MacOS+jdk8
同样使用命令行测试函数是否接通,word和greeter接口都没有问题。
使用postman测试Post请求还是一样,入参使用Json,返回List自动格式化为Json
9.最后一点即是发布,需要使用云服务商提供的或者开源自建Serverless平台,相关信息需自行了解,本人也在学习中,无服务带来的函数式开发使得我们程序员只需关注业务,这就是进步吧。
本文完,2019-8-29