做者 | 方剑(洛夜) Spring Cloud Alibaba 开源项目负责人/创始人之一 来源|阿里巴巴云原生公众号html
导读:本文摘自 Spring Cloud Alibaba 开源项目创始团队成员方剑撰写的《深刻理解 Spring Cloud 与实战》一书,主要讲述了 Java 微服务框架 Spring Boot/Cloud 这个事实标准下如何应对 FaaS 场景。java
Serverless & FaaS
2019 年,O'Reilly 对 1500 名 IT 专业人员的调查中,有 40% 的受访者在采用 Serverless 架构的组织中工做。2020 年 DataDog 调查显示,如今有超过 50% 的 AWS 用户正在使用 Serverless 架构的 AWS Lambda。spring
Serverless 正在成为主流,因而就诞生了下面这幅图,从单体应用的管理到微服务应用的管理再到函数的管理。编程
Serverless 到目前为止尚未一个精准定义。Martin Fowler 在我的博客上有一篇《Serverless Architectures》文章,其对 Serverless 的的定义分红了 BaaS 或 FaaS。后端
Baas 是全称是 Backend-as-a-Service,后端即服务,FaaS 的全称是 Function-as-a-Service,函数即服务。数组
今天咱们来聊聊 FaaS。这是维基百科对 FaaS 的定义:架构
函数即服务(FaaS)是一类云计算服务,它提供了一个平台,使客户能够开发,运行和管理应用程序功能,而无需构建和维护一般与开发和启动应用程序相关的基础架构。遵循此模型构建应用程序是实现 Serverless 架构的一种方法,一般在构建微服务应用程序时使用。app
对于 Python、JavaScript 这种天生支持 Lambda 的开发语言,和 FaaS 简直是完美结合。Serverless Framework 的调研报告也很好地说明了这一点。NodeJS、Python 是 FaaS 使用率前二的语言。负载均衡
咱们知道,由于 JVM 占用的内存比较大,因此 Java 应用的启动会有点慢,不太适合 FaaS 这个场景,这也是 Java 在使用率上偏低的缘由。框架
另外,对 Java 开发者来讲 Spring Boot/Cloud 已经成为了事实标准,依赖注入是 Spring Framework 的核心,Spring Boot/Cloud 这个事实标准应对 FaaS 这个场景,会碰撞出怎么样的火花呢?这就是今天咱们要聊的 Spring Cloud Function。
Java Function
在对 Spring Cloud Function 介绍以前,咱们先来看 Java 里的核心函数定义。
JDK 1.8 推出了新特性 Lambda 表达式,java.util.function 包下面提供了不少的函数。这 3 个函数尤其重要:
1. java.util.function.Function: 须要一个参数,获得另外一个结果.
@FunctionalInterface public interface Function<T, R> { R apply(T t); }
好比经过 Stream API 里的 map 方法能够经过 Function 把字符串从小写变成大写:
Stream.of("a", "b", "c").map(String::toUpperCase);
这里的 map 方法须要一个 Function 参数:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
2. java.util.function.Consumer: 须要一个参数进行操做,无返回值。
@FunctionalInterface public interface Consumer<T> { void accept(T t); }
好比经过 Stream API 里的 forEach 方法遍历每一个元素,作对应的业务逻辑处理:
RestTemplate restTemplate = new RestTemplate(); Stream.of("200", "201", "202").forEach(code -> { ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://httpbin.org/status/" + code, String.class); System.out.println(responseEntity.getStatusCode()); });
3. java.util.function.Supplier: 获得一个结果,无输入参数。
@FunctionalInterface public interface Supplier<T> { T get(); }
好比自定义 Supplier 能够返回随机数:
Random random = new Random(); Supplier supplier100 = () -> random.nextInt(100); Supplier supplier1000 = () -> random.nextInt(1000); System.out.println(supplier100.get()); System.out.println(supplier1000.get());
Spring Cloud Function
Java Function 的编程模型很是简单,本质上就是这 3 个核心函数:
- Supplier<O>
- Function<I, O>
- Consumer<I>
Spring Cloud Function 是 Spring 生态跟 Serverless(FaaS) 相关的一个项目。它出现的目的是加强 Java Function,主要体如今这几点:
-
统一云厂商的 FaaS 编程模型: Spring Cloud Function 的口号是 "Write Once, Run Anywhere"。咱们写的 Spring Cloud Function 代码能够运行在本地、各个云厂商(AWS Lambda, GCP Cloud Functions, Azure Functions)。
-
自动类型转换: 理解过 Spring MVC 或者 Spring Cloud Stream 的同窗确定对 HttpMessageConverter 或者 MessageConverter 模型,这个转换器的做用是将 HTTP BODY(或者 Message Payload)、HTTP Query Parameter、HTTP HEADER(或者 Message Header)自动转换成对应的 POJO。有了这个特性后,咱们就无需关注函数的入参和返回值,用 String 参数就能够获取原始的入参信息,用 User 这个 POJO 参数就能够将原始的入参参数自动转换成 User 对象。
-
函数组合: 可让多个函数之间进行组合操做。
-
函数管理: 新增 FunctionCatalog、FunctionRegistry 接口用于 Function 的管理。管理 ApplicationContext 内的 Function,动态注册 Function 等操做。
-
Reactive 支持: Spring Cloud Function 新增好比 FluxFunction、FluxSupplier、FunctionConsumer 这种 Reactive 函数。
-
自动跟 Spring 生态内部原有的组件进行深度集成:
- Spring Web/Spring WebFlux: 一次 HTTP 请求是一次函数调用。
- Spring Cloud Task: 一次任务执行是一次函数调用。
- Spring Cloud Stream: 一次消息消费/生产/转换是一次函数调用。
这里再多介绍统一云厂商的 FaaS 编程模型,让你们对 Spring Cloud Function 更有体感。
AWS Lambda 是第一个是提供 FaaS 服务的云厂商,RequestStreamHandler 是 AWS 提供的针对 Java 开发者的接口,须要实现这个接口:
public class HandlerStream implements RequestStreamHandler { @Override public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { ...
Azure Functions 针对 Java 开发者提供了 @HttpTrigger 注解:
public class Function { public String echo(@HttpTrigger(name = "req", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) String req, ExecutionContext context) { ... } }
从这两段代码能够看出,不一样的云厂商要编写不一样的代码。若是要变换云厂商,这个过程会很痛苦。
另外,不管是 AWS、Azure 或者 GCP 提供的接口或注解,他们没有任何 Spring 上下文相关的初始化逻辑。若是咱们是一个 Spring Boot/Cloud 应用迁移到 FaaS 平台,须要添加 Spring 上下文初始化逻辑等改动量。
Spring Cloud Function 的出现就是为了解决这些问题。
Spring Cloud Function 的使用
Spring Cloud Function & Spring Web:
@SpringBootApplication public class SpringCloudFunctionWebApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudFunctionWebApplication.class, args); } @Bean public Function<String, String> upperCase() { return s -> s.toUpperCase(); } @Bean public Function<User, String> user() { return user -> user.toString(); } }
访问对应的 Endpoint:
$ curl -XPOST -H "Content-Type: text/plain" localhost:8080/upperCase -d hello HELLO $ curl -XPOST -H "Content-Type: text/plain" localhost:8080/user -d '{"name":"hello SCF"}' User{name\u003d\u0027hello SCF\u0027}
Spring Cloud Function & Spring Cloud Stream:
@SpringBootApplication public class SpringCloudFunctionStreamApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudFunctionStreamApplication.class, args); } @Bean public Function<String, String> uppercase() { return x -> x.toUpperCase(); } @Bean public Function<String, String> prefix() { return x -> "prefix-" + x; } }
加上 function 相关的配置(针对 input-topic 上的每一个消息,payload 转换大写后再加上 prefix- 前缀,再写到 output-topic 上):
spring.cloud.stream.bindings.input.destination=input-topic spring.cloud.stream.bindings.input.group=scf-group spring.cloud.stream.bindings.output.destination=output-topic spring.cloud.stream.function.definition=uppercase|prefix
Spring Cloud Function & Spring Cloud Task:
@SpringBootApplication public class SpringCloudFunctionTaskApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudFunctionTaskApplication.class, args); } @Bean public Supplier<List<String>> supplier() { return () -> Arrays.asList("200", "201", "202"); } @Bean public Function<List<String>, List<String>> function() { return (list) -> list.stream().map( item -> "prefix-" + item).collect(Collectors.toList()); } @Bean public Consumer<List<String>> consumer() { return (list) -> { list.stream().forEach(System.out::println); }; } }
加上 function 相关的配置(Supplier 模拟任务的输入源,Function 模拟对任务输入源的处理,Consumer 模拟处理对 Function 处理输入源后的数据):
spring.cloud.function.task.function=function spring.cloud.function.task.supplier=supplier spring.cloud.function.task.consumer=consumer
《深刻理解 Spring Cloud 与实战》一书正式开始预售啦,这是一本深刻剖析 Spring Cloud 全家桶的书籍,涉及如下内容:
- Spring Boot 核心特性
- Spring Cloud 服务注册/服务发现原理剖析
- 双注册双订阅模型完成 Eureka 迁移至 Nacos 的案例
- 负载均衡:Spring Cloud LoadBalancer 和 Netflix Ribbon
- Dubbo Spring Cloud:Spring Cloud 与 Apache Dubbo 的融合
- Spring Cloud 灰度发布案例
- Spring 体系配置,动态刷新加载原理剖析
- Spring Cloud Circuit Breaker 抽象以及 Sentinel、Hystrix、Resilience4j 熔断器对比
- Spring 体系消息编程模型剖析
- Spring Cloud Data Flow 完成批处理和流处理任务
- Spring Cloud Gateway 网关剖析
- Spring 与 Serverless 的融合
点击了解详情,更有机会赢取免费图书!
做者简介
方剑 Spring Cloud Alibaba 开源项目负责人/创始人之一。《深刻理解 Spring Cloud 与实战》做者,Apache RocketMQ Committer,Alibaba Nacos Committer。曾在我的博客上编写过《SpringMVC 源码分析系列》、《SpringBoot 源码分析系列》文章,目前,关注微服务、云原生、Kubernetes。
《深刻理解 Spring Cloud 与实战》做者方剑将出席 1 月 9 日 Spring Cloud Alibaba 上海站,现场活动也有互动赠书活动,欢迎来现场与做者面基。