介紹 Spring Cloud Function

工程 | Mark Fisher | 2017 年 7 月 5 日 | ...

Spring Cloud Function 是一個新專案,具有以下高階目標

  • 透過函式推廣業務邏輯的實現。
  • 將業務邏輯的開發生命週期與任何特定的執行時目標解耦,以便相同的程式碼可以作為 Web 端點、流處理器或任務執行。
  • 支援跨無伺服器提供商的統一程式設計模型,以及獨立執行(本地或在 PaaS 中)的能力。
  • 在無伺服器提供商上啟用 Spring Boot 特性(自動配置、依賴注入、指標)。

正如Spring一直推崇基於普通Java物件(POJO)的程式設計模型一樣,Spring Cloud Function推崇一種基於普通舊函式的程式設計模型。我們指的是java.util.function包中定義的核心介面:FunctionConsumerSupplier

這些型別的實現可以作為bean註冊,可以透過顯式註冊,也可以透過@FunctionScan啟用的類路徑掃描隱式註冊。引數和/或返回型別可以選擇使用Reactor的Flux,它是一個Reactive Streams Publisher。這使得與Fux Reactive Streams的其他元件(甚至那些基於RxJava 2等其他實現的元件)的互操作性成為可能,並且它為這種處理模型帶來了非阻塞I/O和背壓等響應式特性(有關更多資訊,請參閱Project Reactor)。無論引數和/或返回型別是否為Flux,Spring Cloud Function都會將它們包裝起來,以便函式可以透過Flux進行互操作。對於簡單的逐項處理用例,您可以保持簡單。

public class Greeter implements Function<String, String> {
  public String apply(String name) {
    return "Hello " + name;
  }
}

但是,如果您需要實現透過視窗或歸約操作將資料集作為處理單元的函式,則可以使用Flux型別。

public static class WordCount
    implements Function<Flux<String>, Flux<Map<String, Integer>>> {
  public Flux<Map<String, Integer>> apply(Flux<String> phrases) {
    return phrases.window(3)
      .flatMap(f -> f.flatMap(phrase -> Flux.fromArray(phrase.split("\\W")))
      .reduce(new HashMap<String, Integer>(),
        (map, word) -> { map.merge(word, 1, Integer::sum); return map; }));
  }
}

依賴函式型別也使得組合功能變得容易,例如:

twistAndShout = twist.andThen(shout);

當然,函式也可以使用lambda表示式定義,例如:

Function<String, String> shout =  s -> s.toUpperCase() + “!”;

事實上,Spring Cloud Function支援將基於字串的lambda表示式動態編譯成函式例項。這在原型設計或新增一些簡單的轉換邏輯時特別有用,就像當今常用的Spring表示式語言一樣。

您可能會問,既然您可以輕鬆建立FunctionConsumerSupplier例項,為什麼Spring還需要推廣這種模型。答案涉及控制反轉,這並不奇怪。多年來,從基本的依賴注入到Spring普遍使用的模板模式,都被好萊塢原則所描述:“不要打電話給我們,我們會打電話給你”。上面提到的Flux適配實際上就是控制反轉的一個例子,但更重要的是業務邏輯與部署配置檔案的解耦。在這種情況下,業務邏輯指的是函式,而部署配置檔案可以是REST應用程式、流處理應用程式或有限任務。Spring Cloud Function為每種型別的部署配置檔案提供了一個JAR包,並且在每種情況下,都使用一個自動配置的FunctionCatalog來定位ApplicationContext中的FunctionsConsumersSuppliers

例如,要將上面顯示的Greeter函式部署為REST端點,只需新增“spring-cloud-function-web”依賴項,如本POM所示。其中還包括Spring Boot Maven外掛,以便構建生成一個可執行的JAR。

./mvnw clean install
java -jar greeter/target/greeter-0.0.1-SNAPSHOT.jar

然後可以使用curl呼叫它:

$ curl -H "Content-Type: text/plain" :8080/greeter -d World
Hello World

同樣,要將函式部署為流處理器,只需新增“spring-cloud-function-stream”依賴項,該依賴項又基於Spring Cloud Stream構建。正如Spring Cloud Stream提供了Binder抽象以消除定義通道介面卡的需要一樣,Spring Cloud Function消除了宣告Service Activators、Transformers等元件,甚至Spring Cloud Stream委託的@StreamListener註解方法的需要。“spring-cloud-function-stream”JAR本身提供了所有這些。這又是將控制反轉提升到另一個層次的又一個例子。

在本系列部落格的第2部分中,我們將提供示例,說明如何在下一版本的Spring Cloud Data Flow中使用SuppliersFunctionsConsumers。基本思想是,無論何時您需要提供一些自定義邏輯,您都可以只實現簡單的函式。這是一個完美的有主見的模型示例,您不僅不需要提供樣板程式碼,而且讓框架來處理它會更好。例如,您將能夠只註冊函式——無論是內聯的還是打包為JAR(而不是Spring Cloud Stream應用程式),然後您可以在DSL中引用這些函式,同時依靠Spring Cloud Data Flow為您包裝它們。

mySupplier | myFunction | myConsumer

部署配置檔案甚至擴充套件到無伺服器(又名函式即服務)提供商的領域,例如AWS Lambda和Apache OpenWhisk(以及Azure Functions和Google Cloud Functions,一旦它們提供對Java的支援)。在本系列部落格的第3部分中,我們將深入探討該主題的更多細節,但現在您可以查閱AWS Lambda介面卡Apache OpenWhisk介面卡的文件。即將釋出的部落格還將涵蓋與基於Kubernetes的無伺服器框架(例如Fission)的整合。

除了解耦業務邏輯和基礎設施的作用之外,各種部署配置檔案JAR和FaaS介面卡還促進了可移植性。開發人員可以完全獨立地實現函式,包括只關心輸入和輸出引數的單元測試。然後可以將該函式與允許它在目標環境中執行的依賴項一起打包,範圍從獨立的REST應用程式到Spring Cloud Data Flow或FaaS提供商。

這就引出了本入門部落格的最後一點。“無伺服器”這個詞引起了很多反彈,並且幾乎總是伴隨著解釋:“當然仍然有伺服器,但你不需要考慮它們。”因此,雖然我們會抵制引入“無框架”這個詞,但同樣的概念確實可以應用於框架。在上面的Spring Cloud Data Flow示例中,函式開發人員不需要考慮框架,甚至不需要生成其依賴項中包含任何框架程式碼的工件。同樣的想法也適用於FaaS介面卡。我們基本上將控制反轉推到了我們可以將好萊塢原則扭曲成:“不要依賴我們,我們將依賴你”的地步。這在好萊塢可能不太受歡迎,但對於開發人員來說,這意味著您只需編寫一個函式,將其打包成JAR,然後註冊它以用於各種端點或介面卡。Spring一如既往地遵循艾倫·凱(Alan Kay)雄辯地闡述的原則:“簡單的事情應該簡單。複雜的事情應該成為可能。”在即將釋出的部落格文章中,我們將深入探討Spring Cloud Function可能實現的更復雜的事情,但我們永遠不會忘記保持簡單事情的簡單。

敬請期待!

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有