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 函数?

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

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

Spring Cloud Function 作为 Azure 函数

Spring Cloud Function 提供了一个Azure 适配器来部署和运行作为 Azure Java 函数的 Java 函数。

为了将 Spring Cloud Function 与 Azure Java 函数一起使用,您需要在类路径中包含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 函数上使用简单的 Java 函数编程模型,但底层基础设施仍然是 Azure 函数,您仍然需要管理 Azure 函数应用程序的扩展、安全性和监控,以及与其他 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 函数上运行此函数,您需要在 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 函数。

部署到 Microsoft Azure

您需要将函数打包为一个 fat jar,然后将其部署到您的 Azure 函数应用。部署后,您可以通过 HTTP 请求或来自 Azure 服务(如事件中心、服务总线等)的事件来触发您的函数。

您还可以使用 maven 插件com.microsoft.azure:azure-functions-maven-plugin将函数部署到 azure 函数,可以通过将以下内容添加到您的 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 函数与 Spring Cloud Function 集成,并且您可以利用 Spring Cloud Function 的强大功能,例如函数组合、基于 POJO 的开发等等。

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

附录 1:转换遗留代码。从 FunctinoInvoker 到 DI Azure 函数集成

任何使用 FunctionInvoker 的现有应用程序都可以轻松地转换为新的 DI Azure 函数集成样式。

例如,让我们转换以下使用遗留 FunctionInvoker 样式的示例应用程序。

Spring Boot 定义引导应用程序和一个名为 uppercase 的 Spring Cloud 函数

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 函数的 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必须与 @Bean 函数名称(在 Config 类中)匹配。

重构 UppercaseHandler 类以替换遗留的 FunctionInvoker 并使用 DI 非常简单,如下所示

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 社区所有即将举行的活动。

查看全部