Spring Cloud Function for Azure Function

工程 | Christian Tzolov | 2023 年 2 月 24 日 | ...

什么是 Spring Cloud Function?

Spring Cloud Function 是一个基于 Spring Boot 的框架,它允许用户通过实现 Java 函数(即 Supplier、Function、Consumer)来专注于他们的业务逻辑。反过来,该框架提供了必要的抽象,使其能够在各种环境中(例如 REST、流式处理)以及 AWS Lambda 或 Azure Functions 等无服务器环境中执行这些函数,而无需担心底层平台特定的细节。这使得开发人员可以专注于编写他们的业务逻辑,而框架则负责其余部分。

Spring Cloud Function 使用 java.util.function.Function/Supplier/Consumer 接口作为定义函数结构(包括输入和输出类型)的构建块。

这是一个简单的 Spring Cloud Function 示例,它接收一个字符串并返回该字符串的大写形式。

首先,我们定义函数接口

public interface UppercaseFunction extends Function<String, String> { }

接下来,我们将函数注册为一个 bean

@Bean
public UppercaseFunction uppercase() {
    return value -> value.toUpperCase();
}

这是您可以使用 Spring Cloud Function 进行操作的基本示例,您还可以将其用于更复杂的用例,例如连接到数据库、从队列消费消息等。它本身只是一个作为 Java 函数实现的、注册为 Spring Bean 的代码片段。但是,借助 Spring Cloud Function,此函数可以成为 REST 请求的处理程序,或被 Kafka 等消息传递系统触发的消息处理程序。相同的函数也可以在 AWS Lambda 或 Microsoft Azure 等无服务器环境中执行,而无需更改其实现。这正是本文要介绍的内容,特别是 Spring Cloud Function 与 Microsoft Azure 的集成。

什么是 Azure Java Function?

Azure Java Functions 是一项服务,它允许您编写基于 Java 的无服务器函数,并在 Azure 基础设施上运行它们,同时能够与 Spring Boot 等其他 Azure 服务和框架集成。

Azure Functions 运行时负责函数应用的扩展、安全和监控,并提供与其他 Azure 服务的简便集成。您可以在 此处 阅读有关 Azure Java Functions 的更多信息。

Spring Cloud Function 作为 Azure Function

Spring Cloud Function 提供了一个 Azure 适配器,用于将 Java 函数部署和运行为 Azure Java Functions。

为了在 Azure Java Functions 中使用 Spring Cloud Function,您需要在类路径中包含 spring-cloud-function-adapter-azure 依赖项。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-function-adapter-azure</artifactId>
    <version>4.0.4</version>
</dependency>

值得注意的是,使用 Spring Cloud Function 允许您在 Azure Java Functions 中使用简单的 Java 函数编程模型,但底层基础设施仍然是 Azure Functions,您仍然需要管理 Azure Function 应用的扩展、安全和监控,以及与其他 Azure 服务的集成。

让我们来看一个例子。为此,我们需要将业务逻辑(即字符串大写化)提取到一个名为 uppercase 的专用函数中。

import java.util.function.Function;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class HttpTriggerDemoApplication {

   @Bean
   public Function<String, String> uppercase() {
       return payload -> {
           String output = payload.toUpperCase();
           return String.format("Input: %s", output);         
       }
   }

	 @Bean
	 public Function<String, String> reverse() {
		  return payload -> new StringBuilder(payload).reverse().toString();
	 }

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

此示例使用 @SpringBootApplication 注解来配置 Spring Boot 应用程序,并使用 @Bean 注解来定义函数 bean。然后,要在 Azure Java Function 上运行此函数,您需要创建一个新的 Azure 函数应用,并将其配置为使用 Java 运行时。

import java.util.Optional;
import java.util.function.Function;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.function.context.FunctionCatalog;
import org.springframework.stereotype.Component;

@Component
public class AzureJavaExampleFunctionWithSpring {

   /**
    * Plain Spring bean (not Spring Cloud Functions!)
    */
   @Autowired
   private Function<String, String> uppercase;

   /**
    * The FunctionCatalog leverages the Spring Cloud Function framework.
    */
   @Autowired
   private FunctionCatalog functionCatalog;

   @FunctionName("bean")
   public String plainBeans(
           @HttpTrigger(name = "req", methods = { HttpMethod.GET,
                   HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
           ExecutionContext context) {

       // Use plain Spring Beans.
       return uppercase.apply(request.getBody().orElse("Hello World"));
   }

   @FunctionName("scf")
   public String springCloudFunction(
            @HttpTrigger(name = "req", methods = { HttpMethod.GET,
                    HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
            ExecutionContext context) {

        // Use SCF composition. 
        Function composed = this.functionCatalog.lookup("reverse|uppercase");

        return (String) composed.apply(request.getBody().orElse("Hello World"));
    }   
}

AzureJavaExampleFunctionWithSpring 类被注解了标准的 Azure 注解,例如 @FunctionName 和 @HttpTrigger,并且在内部调用 HttpTriggerDemoApplication 中定义的 uppercase 函数。@Component 注解使得这个 Azure 应用同时也是一个 Spring 应用,从而通过 Spring 依赖注入(例如自动装配 uppercasefunctionCatalog bean)提供了与 Spring Cloud Function 和其他 Spring 管理组件的集成点。请注意,AzureJavaExampleFunctionWithSpring 是一个功能齐全的 Spring 组件,因此您可以自动装配任何 Spring bean(不仅限于函数),使用属性配置以及任何其他 Spring Framework 功能。

请注意,plainBeans 函数使用普通的 Spring bean,而 springCloudFunctin 则利用 FunctionCatalog 来组合多个 Spring Cloud Function。

部署到 Microsoft Azure

您需要将函数打包成一个 fat jar,然后将其部署到您的 Azure Function App。部署后,您可以通过 HTTP 请求、Azure 服务(如 Event Hub、Service Bus 等)的事件来触发您的函数。

您还可以使用 maven 插件 com.microsoft.azure:azure-functions-maven-plugin 来部署函数到 azure function,可以通过将以下内容添加到您的 pom.xml 来配置 maven 插件:

<plugin>
  <groupId>com.microsoft.azure</groupId>
  <artifactId>azure-functions-maven-plugin</artifactId>
  <version>1.22.0</version>
  <configuration>
      <appName>scf-samples</appName>
      <resourceGroup>java-functions-group</resourceGroup>
      <region>westus</region>
      <appServicePlanName>java-functions-app-service-plan</appServicePlanName>
      <pricingTier>EP1</pricingTier>
      <hostJson>${project.basedir}/src/main/resources/host.json</hostJson>

      <runtime>
          <os>linux</os>
          <javaVersion>17</javaVersion>
      </runtime>

      <funcPort>7072</funcPort>

      <appSettings>
          <property>
              <name>FUNCTIONS_EXTENSION_VERSION</name>
              <value>~4</value>
          </property>
      </appSettings>
  </configuration>
  <executions>
      <execution>
          <id>package-functions</id>
          <goals>
              <goal>package</goal>
          </goals>
      </execution>
  </executions>
</plugin>

这将使您的 Azure Java Function 与 Spring Cloud Function 集成,您可以利用 Spring Cloud Function 的强大功能,如函数组合、基于 POJO 的开发等等。

有关更多信息,请查看更新的 Azure 适配器参考文档,以及 此处 提供的各种示例。

附录 1:转换旧代码。从 FunctionInvoker 到 DI Azure Function 集成

任何使用 FunctionInvoker 的现有应用程序都可以轻松地转换为新的 DI Azure Function 集成风格。

例如,让我们转换以下使用旧 FunctionInvoker 风格的示例应用程序。

Spring Boot 定义了启动应用程序和一个名为 uppercase 的 Spring Cloud Function。

import java.util.Map;
import java.util.function.Function;
import com.microsoft.azure.functions.ExecutionContext;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.function.json.JsonMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.Message;

@SpringBootApplication
public class Config {

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

   @Bean
   public Function<Message<String>, String> uppercase(JsonMapper mapper) {
       return message -> {
           String value = message.getPayload();
           try {
               Map<String, String> map = mapper.fromJson(value, Map.class);

               if(map != null)
                   map.forEach((k, v) -> map.put(k, v != null ? v.toUpperCase() : null));

               return mapper.toString(map);
           } catch (Exception e) {
               e.printStackTrace();
               return ("Function error: - bad request");
           }
       };
   }
}

使用 uppercase 函数作为 Azure Function 的 FunctionInvoker 将如下所示:

import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;
import java.util.Optional;
import org.springframework.cloud.function.adapter.azure.FunctionInvoker;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;

public class UppercaseHandler extends FunctionInvoker<Message<String>, String> {

   @FunctionName("uppercase")
   public String execute(
       @HttpTrigger(
           name = "req",
           methods = {HttpMethod.GET, HttpMethod.POST},
           authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
       ExecutionContext context
   ) {
       context.getLogger().warning("Using Java (" + System.getProperty("java.version") + ")");
       Message<String> message = MessageBuilder.withPayload(request.getBody().get())
           .copyHeaders(request.getHeaders()).build();
       return handleRequest(message, context);
   }
}

请注意,按照约定,@FunctionName 必须与 Config 类中的 @Bean 函数名匹配。

重构 UppercaseHandler 类以使用 DI 替换旧的 FunctionInvoker 是很简单明了的,如下所示:

import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;

import java.util.Optional;

import org.springframework.cloud.function.adapter.azure.FunctionInvoker;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;

@Component
public class UppercaseHandler {

   @Autowired
   private Function<Message<String>, String> uppercase;

   @FunctionName("uppercase")
   public String execute(
       @HttpTrigger(
           name = "req",
           methods = {HttpMethod.GET, HttpMethod.POST},
           authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
       ExecutionContext context
   ) {
       context.getLogger().warning("Using Java (" + System.getProperty("java.version") + ")");
       Message<String> message = MessageBuilder.withPayload(request.getBody().get())
           .copyHeaders(request.getHeaders()).build();
       return uppercase.apply(message);
   }
}
  • 添加 @Component 类注解。
  • 移除 FunctionInvoke 类继承。
  • 自动装配所需的 Function bean。任何 Spring 服务和自动装配技术都得到支持。
  • handleRequest 方法调用替换为显式函数调用。

至此,您就可以构建和部署您的应用程序了。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有