By Fang Jian (luoye), Founder and Head of the Spring Cloud Alibaba Open-Source Project
This article is taken from the book, In-depth Understanding and Practices of Spring Cloud, written by Fang Jian. This article describes how to deal with FaaS scenarios under the Java microservice framework Spring Boot/Cloud.
In 2019, O'Reilly researched 1,500 IT professionals. The result showed that 40% of the respondents work in organizations that adopt the Serverless architecture. The DataDog Survey from 2020 shows that more than 50% of AWS users are using AWS Lambda based on Serverless architecture.
Serverless is becoming a mainstream trend. The following figure shows the change from the management of monolithic applications to microservice applications to functions.
So far, Serverless does not have a precise definition. Martin Fowler wrote an article on his blog called Serverless Architectures, which defines Serverless as the combination of BaaS and FaaS.
BaaS is short for Backend as a Service, and FaaS is short for Function as a Service.
Now, let's talk about FaaS. This is the definition of FaaS from Wikipedia, "Function as a Service (FaaS) is a type of cloud computing service. It provides a platform that allows customers to develop, run, and manage application functions without building and maintaining the infrastructure typically associated with application development and launch. Building applications based on this model is a way to implement Serverless architecture. FaaS is usually used when building microservice applications."
Python and JavaScript are natively supported in Lambda and can be combined perfectly with FaaS. The Serverless Framework Research Report also supports this combination. Node.js and Python are the top two languages used in FaaS.
Java-based applications start up slowly because JVM consumes a lot of memory. So, FaaS is not suitable for Java-based applications. This is why Java usage in FaaS is low.
Spring Boot/Cloud has become the de facto standard for Java developers. Dependency injection is the core of the Spring Framework. What will happen if Spring Boot/Cloud is used to address FaaS scenarios? This involves the Spring Cloud Function being introduced in this article.
Before introducing the Spring Cloud Function, let's take a look at definitions of core functions in Java.
JDK 1.8 released a new feature called Lambda expressions. Many functions are provided in the java.util.function
package. The following three functions are particularly important:
java.util.function.Function
: A Parameter Is Required to Obtain Another Result@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
For example, the map method in Stream API can change an uppercase string to a lowercase one through the Function:
Stream.of("a", "b", "c").map(String::toUpperCase);
The map method requires a Function parameter:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
java.util.function.Consumer
: A Parameter Is Required for Operations without Returning Any Value@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
For example, the forEach
method of Stream API can traverse each element and perform corresponding business logic processing:
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());
});
java.util.function.Supplier
: A Result Is Returned with No Input Parameter@FunctionalInterface
public interface Supplier<T> {
T get();
}
For example, the custom Supplier can return a random number:
Random random = new Random();
Supplier supplier100 = () -> random.nextInt(100);
Supplier supplier1000 = () -> random.nextInt(1000);
System.out.println(supplier100.get());
System.out.println(supplier1000.get());
The programming model of the Java Function is very simple, which is essentially the three core functions:
The Spring Cloud Function is a Spring ecosystem project related to Serverless (FaaS). It is designed to enhance the Java Function in the following aspects:
HttpMessageConverter
or MessageConverter
is very familiar. This converter automatically converts the HTTP BODY (or Message Payload), HTTP Query Parameter, and HTTP HEADER (or Message HEADER) to the corresponding POJO. With this feature, no attention is needed to the input parameters and returned values of the function. The original input parameter information can be obtained through String parameters. Moreover, with the POJO parameter called User, the original input parameters can be automatically converted into User objects.FunctionCatalog
and FunctionRegistry
interfaces are provided for Function management. They allow Function management in ApplicationContext
and dynamic Function Registration.The Spring Cloud Function automatically and deeply integrates with the original components in the Spring ecosystem:
The unified FaaS programming model of cloud vendors is explained below to give a better understanding of the Spring Cloud Function.
AWS Lambda was the first cloud vendor to provide the FaaS service. RequestStreamHandler
is the interface provided by AWS for Java developers. Use the code below to implement the following interface:
public class HandlerStream implements RequestStreamHandler {
@Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException
{
...
Azure Functions provides the @HttpTrigger annotation for Java developers:
public class Function {
public String echo(@HttpTrigger(name = "req",
methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS)
String req, ExecutionContext context) {
...
}
}
As shown above, different codes have to be written for different cloud vendors. This process is very difficult if users want to change the cloud vendor.
Interfaces or annotations provided by AWS, Azure, or GCP have no initialization logic related to the Spring context. If a Spring Boot/Cloud application is migrated to the FaaS platform, modifications like the Spring context initialization logic are required. Spring Cloud Function was created to solve these problems.
The Spring Cloud Function and 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();
}
}
Access the corresponding 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}
The Spring Cloud Function and 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;
}
}
Add the function-related configurations. For each message in an input-topic, the payload is converted into uppercase type and mounted with a prefix of prefix-. Then, the payload is written to the output-topic. For example:
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 and 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);
};
}
}
Add function-related configurations. The Supplier simulates the job input source. The Function simulates the processing of the job input source, and the Consumer simulates the processing of data processed by the Function. For example:
spring.cloud.function.task.function=function
spring.cloud.function.task.supplier=supplier
spring.cloud.function.task.consumer=consumer
Fang Jian is the Founder and Head of the Spring Cloud Alibaba open-source project. Fang is the author of In-depth Understanding and Practices of Spring Cloud. He is also an Apache RocketMQ Committer and Alibaba Nacos Committer. He has written series of articles, like SpringMVC Source Code Analysis and SpringBoot Source Code Analysis on his blog. Currently, Fang is focusing on microservices, cloud-native, and Kubernetes.
Unveiling Alibaba's Hybrid Scheduling Technology for Complex Task Resources
507 posts | 48 followers
FollowAlibaba Clouder - April 16, 2019
Alibaba Cloud Native Community - September 13, 2023
Lee Li - January 19, 2021
Aliware - March 19, 2021
Alibaba Cloud Native Community - May 18, 2023
Alibaba Clouder - June 23, 2020
507 posts | 48 followers
FollowMSE provides a fully managed registration and configuration center, and gateway and microservices governance capabilities.
Learn MoreVisualization, O&M-free orchestration, and Coordination of Stateful Application Scenarios
Learn MoreServerless Application Engine (SAE) is the world's first application-oriented serverless PaaS, providing a cost-effective and highly efficient one-stop application hosting solution.
Learn MoreAlibaba Cloud Function Compute is a fully-managed event-driven compute service. It allows you to focus on writing and uploading code without the need to manage infrastructure such as servers.
Learn MoreMore Posts by Alibaba Cloud Native Community