適用於 Azure Function 的 Spring Cloud Function

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

什麼是 Spring Cloud Function?

Spring Cloud Function 是一個基於 SpringBoot 的框架,允許使用者專注於他們的業務邏輯,並將其實現為 Java 函式 (例如,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 基礎架構上執行它們,並能夠與其他 Azure 服務和框架(如 Spring Boot)整合。

Azure Functions 運行時負責擴展、保護和監控您的函數應用程式,並提供與其他 Azure 服務的輕鬆整合。 您可以在這裡閱讀更多關於 Azure Java Functions 的資訊

Spring Cloud Function 作為 Azure Function

Spring Cloud Function 提供一個 Azure Adapter 來部署和執行 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 依賴注入提供與 Spring Cloud Function 和其他 Spring 管理元件的整合點,例如自動裝配 uppercasefunctionCatalog bean。 請注意,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:轉換舊程式碼。 從 FunctinoInvoker 到 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 必須與 @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 社群中所有即將舉行的活動。

查看所有