Spring Cloud Function 用於 Azure Function

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

什麼是 Spring Cloud Function?

Spring Cloud Function 是一個基於 SpringBoot 的框架,它允許使用者將業務邏輯實現為 Java Function(即 Supplier、Function、Consumer),從而專注於業務邏輯本身。該框架提供了必要的抽象層,以便在各種環境(例如 REST、Streaming)以及諸如 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 執行。

為了將 Spring Cloud Function 與 Azure Java Functions 一起使用,您需要在 classpath 中包含 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 Function 程式設計模型,但底層基礎設施仍然是 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 請求或來自 Event Hub、Service Bus 等 Azure 服務的事件來觸發您的函式。

您還可以使用 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:轉換遺留程式碼。從 FunctinoInvoker 到 DI Azure Function 整合

任何使用 FunctionInvoker 的現有應用程式都可以輕鬆轉換為新的 DI Azure Function 整合風格。

例如,讓我們轉換以下使用傳統 FunctionInvoker 風格的示例應用程式。

Spring Boot 定義了 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 必須與 @Bean 函式名(在 Config 類中)匹配。

重構 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 社群即將舉行的所有活動。

檢視全部