Mark Fisher, Dave Syer, Oleg Zhurakousky, Anshul Mehra, Dan Dobrin

4.0.5


導入

Spring Cloud Function は、次の高レベルのゴールを持つプロジェクトです。

  • 関数を介したビジネスロジックの実装を推進します。

  • 同じコードを Web エンドポイント、ストリームプロセッサー、タスクとして実行できるように、ビジネスロジックの開発ライフサイクルを特定のランタイムターゲットから分離します。

  • サーバーレスプロバイダー全体で統一されたプログラミングモデルをサポートし、スタンドアロン(ローカルまたは PaaS)で実行する機能をサポートします。

  • サーバーレスプロバイダーで Spring Boot 機能(自動構成、依存性注入、メトリクス)を有効にします。

すべてのトランスポートの詳細とインフラストラクチャを抽象化し、開発者が使い慣れたツールとプロセスをすべて維持し、ビジネスロジックにしっかりと集中できるようにします。

完全な実行可能かつテスト可能な Spring Boot アプリケーション(単純な文字列操作を実装)は次のとおりです。

@SpringBootApplication
public class Application {

  @Bean
  public Function<String, String> uppercase() {
    return value -> value.toUpperCase();
  }

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

これは単なる Spring Boot アプリケーションであるため、他の Spring Boot アプリケーションと同じように、ローカルおよび CI ビルドでビルド、実行、テストできます。Function は java.util からのものであり、Flux はプロジェクト Reactor (英語) からの Reactive Streams (英語)  Publisher です。この機能には、HTTP またはメッセージングを介してアクセスできます。

Spring Cloud Function には次の機能があります。

  • プログラミングスタイルの選択 - リアクティブ、命令形またはハイブリッド

  • 関数の構成と適応(たとえば、リアクティブ型の命令型関数の構成)

  • 複数の入力と出力を備えたリアクティブ関数のサポートにより、マージ、結合、その他の複雑なストリーミング操作を関数で処理できます

  • 入力と出力の透過的な型変換

  • ターゲットプラットフォームに固有のデプロイのパッケージ化関数(Project Riff、AWS Lambda など)

  • HTTP エンドポイントとして外部に関数を公開するためのアダプターなど

  • そのようなアプリケーションコンテキストを含む JAR ファイルを分離されたクラスローダーでデプロイし、単一の JVM にまとめてパックできるようにします

  • AWS Lambda [GitHub] (英語) Azure [GitHub] (英語) Google クラウド機能 [GitHub] (英語) 、その他の「サーバーレス」サービスプロバイダー用のアダプター

Spring Cloud は、制限のない Apache 2.0 ライセンスでリリースされています。ドキュメントのこのセクションに貢献したい場合、またはエラーを見つけた場合は、github (英語) のプロジェクトでソースコードと課題追跡ツールを見つけてください。

入門

コマンドラインからビルド(およびサンプルを「インストール」):

$ ./mvnw clean install

(YOLO が必要な場合は、-DskipTests を追加してください)

サンプルの 1 つを実行します。

$ java -jar spring-cloud-function-samples/function-sample/target/*.jar

これにより、アプリが実行され、HTTP を介してその関数が公開されるため、次のように文字列を大文字に変換できます。

$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d Hello
HELLO

複数の文字列(Flux<String>)を新しい行で区切ることで変換できます

$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d 'Hello
> World'
HELLOWORLD

(ターミナルで QJ を使用して、そのようなリテラル文字列に改行を挿入できます)

プログラミングモデル

関数カタログと柔軟な関数シグネチャー

Spring Cloud Function の主な機能の 1 つは、一貫した実行モデルを提供しながら、ユーザー定義関数のさまざまな型シグネチャーを適応させてサポートすることです。そのため、すべてのユーザー定義関数は FunctionCatalog によって標準表現に変換されます。

通常、ユーザーは FunctionCatalog をまったく気にする必要はありませんが、ユーザーコードでサポートされている機能の種類を知っておくと便利です。

Spring Cloud Function はプロジェクト Reactor (英語) によって提供されるリアクティブ API のファーストクラスのサポートを提供し、Mono や Flux などのリアクティブプリミティブをユーザー定義関数の型として使用できるようにし、関数実装のプログラミングモデルを選択する際の柔軟性を高めることを理解することも重要です。リアクティブプログラミングモデルは、命令型プログラミングスタイルを使用して実装することが困難または不可能な機能の関数サポートも可能にします。詳細については、関数アリティセクションを参照してください。

Java 8 関数のサポート

Spring Cloud Function は、Java によって定義され、Java 8 以降で使用できる 3 つのコア関数インターフェースを採用して構築されています。

  • Supplier<O>

  • Function<I, O>

  • Consumer<I>

SupplierFunctionConsumer について頻繁にメンションすることを避けるため、このマニュアルの残りの部分では、必要に応じて SupplierFunctionConsumer を Functional Bean と呼びます。

一言で言えば、アプリケーションコンテキスト内の Bean である 関数 Bean は、遅延して FunctionCatalog に登録されます。これは、このリファレンスマニュアルで説明されているすべての追加機能から恩恵を受ける可能性があることを意味します。

最も単純なアプリケーションでは、アプリケーション構成で SupplierFunction または Consumer 型の @Bean を宣言するだけです。その後、FunctionCatalog にアクセスし、その名前に基づいて特定の関数を検索できます。

例:

@Bean
public Function<String, String> uppercase() {
    return value -> value.toUpperCase();
}

. . .

FunctionCatalog catalog = applicationContext.getBean(FunctionCatalog.class);
Function uppercase =  catalog.lookup(“uppercase”);

uppercase は Bean であるため、ApplicationContext から直接取得することはできますが、取得できるのは宣言したとおりの Bean のみであり、SCF によって提供される追加機能は含まれません。FunctionCatalog を介して関数を検索すると、取得するインスタンスは、このマニュアルで説明されている追加機能 (つまり、型変換、構成など) でラップ (インストルメント化) されます。また、一般的なユーザーは Spring Cloud Function を直接使用しないことを理解することが重要です。代わりに、一般的なユーザーは、追加作業なしでさまざまな実行コンテキストで使用するという考え方で Java Function/Supplier/Consumer を実装します。たとえば、同じ java 関数は、Spring Cloud Function が提供するアダプターや、コアプログラミングモデルとして Spring Cloud Function を使用する他のフレームワーク (Spring Cloud Stream など) を介して、REST エンドポイントストリーミングメッセージハンドラーAWS Lambda などとして表現できます。つまり、要約すると、Spring Cloud Function は、さまざまな実行コンテキストで利用できる追加機能を備えた java 関数を実現します。

関数定義

前の例では、プログラムで FunctionCatalog の関数を検索する方法を示していますが、Spring Cloud Function が別のフレームワーク (例: Spring Cloud Stream) によってプログラミングモデルとして使用される一般的な統合ケースでは、spring.cloud.function.definition プロパティを介して使用する関数を宣言します。FunctionCatalog の関数を検出する場合、いくつかのデフォルトの動作を理解することが重要であることを認識します。例: ApplicationContext に 関数 Bean が 1 つだけある場合、FunctionCatalog の単一関数は空の名前または任意の名前で検索できるため、通常は spring.cloud.function.definition プロパティは必要ありません。例: uppercase がカタログ内の唯一の関数であると仮定すると、catalog.lookup(null)catalog.lookup(“”)catalog.lookup(“foo”) として検索できます。とはいえ、spring.cloud.function.definition を使用する Spring Cloud Stream などのフレームワークを使用している場合は、常に spring.cloud.function.definition プロパティを使用することがベストプラクティスであり、推奨されます。

以下に例を示します。

spring.cloud.function.definition=uppercase

不適格な関数のフィルタリング

一般的なアプリケーションコンテキストには、有効な java 関数である Bean が含まれている場合がありますが、FunctionCatalog に登録する候補となることは意図されていません。このような Bean は、他のプロジェクトからの自動構成、または Java 関数として適格な他の Bean である可能性があります。このフレームワークは、関数カタログへの登録の候補ではない既知の Bean のデフォルトのフィルタリングを提供します。spring.cloud.function.ineligible-definitions プロパティを使用して Bean 定義名のカンマ区切りリストを提供することで、このリストに Bean を追加することもできます。

以下に例を示します。

spring.cloud.function.ineligible-definitions=foo,bar

サプライヤー

サプライヤーは、リアクティブSupplier<Flux<T>>)または命令型Supplier<T>)にすることができます。呼び出しの観点からは、このようなサプライヤーの実装者にとって違いはありません。ただし、フレームワーク内(Spring Cloud Stream など)で使用される場合、サプライヤー、特にリアクティブは、ストリームのソースを表すために使用されることが多いため、コンシューマーがサブスクライブできるストリーム(Flux など)を取得するために一度呼び出されます。言い換えると、このようなサプライヤーは無限ストリームと同等です。ただし、同じリアクティブサプライヤーは、有限ストリーム(ポーリングされた JDBC データの結果セットなど)を表すこともできます。そのような場合、このようなリアクティブサプライヤーは、基盤となるフレームワークの何らかのポーリングメカニズムに接続する必要があります。

その支援のために、Spring Cloud Function はマーカーアノテーション org.springframework.cloud.function.context.PollableBean を提供して、そのようなサプライヤーが有限のストリームを生成し、再度ポーリングする必要がある可能性があることを通知します。とはいえ、Spring Cloud Function 自体はこのアノテーションの動作を提供しないことを理解することが重要です。

さらに、PollableBean アノテーションは、生成されたストリームを分割する必要があることを示すために、分割可能な属性を公開します。 ( スプリッター EIP (英語) を参照してください)

次に例を示します。

@PollableBean(splittable = true)
public Supplier<Flux<String>> someSupplier() {
	return () -> {
		String v1 = String.valueOf(System.nanoTime());
		String v2 = String.valueOf(System.nanoTime());
		String v3 = String.valueOf(System.nanoTime());
		return Flux.just(v1, v2, v3);
	};
}

関数

関数は命令型またはリアクティブ型の方法で記述することもできますが、サプライヤーやコンシューマーとは異なり、Spring Cloud Stream などのフレームワーク内で使用される場合、リアクティブ関数はストリーム (Flux または Mono) への参照を渡すために 1 回だけ呼び出され、命令型はイベントごとに 1 回呼び出されること以外に、実装者にとって特別な考慮事項はありません。

コンシューマー

Consumer は、少なくとも潜在的にはブロッキングを意味する void 戻り型を持っているため、少し特殊です。ほとんどの場合、Consumer<Flux<?>> を記述する必要はありませんが、そうする必要がある場合は、入力 flux をサブスクライブすることを忘れないでください。

関数合成

関数の合成は、複数の関数を 1 つに合成できる機能です。コアサポートは、Java 8 以降で利用可能な Function.andThen(..) (標準 Javadoc) サポートで利用可能な関数合成機能に基づいています。ただし、それに加えて、いくつかの追加機能を提供します。

宣言型関数の合成

この機能を使用すると、spring.cloud.function.definition プロパティを提供するときに、| (パイプ)または , (コンマ)区切り文字を使用して宣言的な方法で構成命令を提供できます。

次に例を示します

--spring.cloud.function.definition=uppercase|reverse

ここでは、それ自体が関数 uppercase と関数 reverse の合成である単一の関数の定義を効果的に提供しました。関数の定義は、いくつかの名前の関数の合成することができるため実際には、プロパティ名を定義していない名前である理由の一つです。また、前述のように、パイプの代わりに , を使用できます(…​definition=uppercase,reverse など)。

非関数の作成

Spring Cloud Function は、Consumer または Function を使用したサプライヤーの作成、および Consumer を使用した Function の作成もサポートします。ここで重要なのは、そのような定義の最終製品を理解することです。機能を使用してサプライヤーを構成すると、サプライヤーになりますが、コンシューマーを使用してサプライヤーを構成すると、実質的に実行可能になります。コンシューマーで関数を構成する同じロジックに従うと、コンシューマーになります。

そしてもちろん、コンシューマーと機能、コンシューマーとサプライヤーなどの構成不可能なものを作成することはできません。

関数のルーティングとフィルタリング

バージョン 2.2 から Spring Cloud Function はルーティング機能を提供し、呼び出したい実際の機能へのルーターとして機能する単一の機能を呼び出すことができます。機能はできません。

RoutingFunction は、functionRouter という名前で FunctionCatalog に登録されています。単純さと一貫性のために、RoutingFunction.FUNCTION_NAME 定数を参照することもできます。

この関数には次のシグネチャーがあります。

public class RoutingFunction implements Function<Object, Object> {
. . .
}

ルーティング命令は、いくつかの方法で伝達できます。メッセージヘッダー、システムプロパティ、プラグ可能な戦略を介した指示の提供をサポートします。それでは、いくつかの詳細を見てみましょう

MessageRoutingCallback

MessageRoutingCallback は、ルート先関数定義の名前の決定を支援する戦略です。

public interface MessageRoutingCallback {
	FunctionRoutingResult routingResult(Message<?> message);
	. . .
}

必要なのは、RoutingFunction によってピックアップされる Bean としてそれを実装して登録することです。例:

@Bean
public MessageRoutingCallback customRouter() {
	return new MessageRoutingCallback() {
		@Override
		public FunctionRoutingResult routingResult(Message<?> message) {
			return new FunctionRoutingResult((String) message.getHeaders().get("func_name"));
		}
	};
}

前の例では、受信メッセージの func_name メッセージヘッダーから関数定義を決定し、呼び出す関数の定義を含む FunctionRoutingResult のインスタンスを返す MessageRoutingCallback の非常に単純な実装を見ることができます。

メッセージヘッダー

入力引数が Message<?> 型の場合、spring.cloud.function.definition または spring.cloud.function.routing-expression メッセージヘッダーのいずれかを設定することで、ルーティング命令を伝達できます。プロパティの名前が示すように、spring.cloud.function.routing-expression は Spring 式言語 (SpEL) に依存しています。より静的なケースでは、単一の関数の名前 (例: …​definition=foo) または合成命令 (例: …​definition=foo|bar|baz) を提供できる spring.cloud.function.definition ヘッダーを使用できます。より動的なケースでは、spring.cloud.function.routing-expression ヘッダーを使用して、関数の定義に解決される SpEL 式を提供できます (上記のとおり)。

SpEL 評価コンテキストのルートオブジェクトは実際の入力引数であるため、Message<?> の場合、payload と headers の両方にアクセスできる式(spring.cloud.function.routing-expression=headers.function_name など)を作成できます。
SpEL を使用すると、ユーザーは実行する Java コードの文字列表現を提供できます。メッセージヘッダーを介して spring.cloud.function.routing-expression を提供できるということは、そのような表現を設定する機能がエンドユーザー (つまり、Web モジュールを使用する場合の HTTP ヘッダー) に公開され、いくつかの問題 (悪意のあるコードなど) が発生する可能性があることを意味します。これを管理するために、Message ヘッダーを経由するすべての式は、機能が制限され、コンテキストオブジェクト (この場合は Message) のみを評価するように設計された SimpleEvaluationContext に対してのみ評価されます。一方、プロパティまたはシステム変数を介して設定されたすべての式は、Java 言語の完全な柔軟性を可能にする StandardEvaluationContext に対して評価されます。システム / アプリケーションプロパティまたは環境変数を介した式の設定は、通常はエンドユーザーに公開されないため、一般的に安全であると見なされますが、システム、アプリケーション、環境変数を更新する機能だけでなく、可視性が実際に公開される場合もあります。一部の Spring プロジェクトまたはサードパーティによって提供される Spring Boot Actuator エンドポイント、またはエンドユーザーによるカスタム実装を介してエンドユーザーに提供されます。このようなエンドポイントは、業界標準の Web セキュリティプラクティスを使用して保護する必要があります。Spring Cloud Function は、そのようなエンドポイントを公開しません。

特定の実行環境 / モデルでは、アダプターはメッセージヘッダーを介して spring.cloud.function.definition および / または spring.cloud.function.routing-expression を変換および通信する責任があります。例: spring-cloud-function-web を使用する場合、HTTP ヘッダーとして spring.cloud.function.definition を提供でき、フレームワークはそれと他の HTTP ヘッダーをメッセージヘッダーとして伝播します。

アプリケーションのプロパティ

ルーティング命令は、アプリケーションプロパティとして spring.cloud.function.definition または spring.cloud.function.routing-expression を介して通信することもできます。前のセクションで説明したルールは、ここでも適用されます。唯一の違いは、これらの命令をアプリケーションプロパティ(--spring.cloud.function.definition=foo など)として提供することです。

spring.cloud.function.definition または spring.cloud.function.routing-expression をメッセージヘッダーとして提供することは、命令型関数(Function<Foo, Bar> など)に対してのみ機能することを理解することが重要です。つまり、命令関数を使用してメッセージごとにルーティングすることしかできません。 リアクティブ関数では、メッセージごとにルーティングすることはできません。ルーティング命令はアプリケーションプロパティとしてのみ提供できます。それはすべて作業単位に関するものです。命令機能では、作業単位はメッセージであるため、このような作業単位に基づいてルーティングできます。リアクティブ関数では、作業単位がストリーム全体であるため、アプリケーションプロパティを介して提供された命令のみに基づいて動作し、ストリーム全体をルーティングします。

ルーティング命令の優先順位

ルーティング命令を提供するメカニズムがいくつかあることを考えると、複数のメカニズムが同時に使用される場合の競合解決の優先順位を理解することが重要です。そのため、順序は次のとおりです。

  1. MessageRoutingCallback (関数が命令型の場合、他に何かが定義されているかどうかに関係なく引き継ぎます)

  2. メッセージヘッダー (機能が必須で、MessageRoutingCallback が提供されていない場合)

  3. アプリケーションのプロパティ (任意の関数)

ルーティング不可能なメッセージ

route-to 関数がカタログで利用できない場合、それを示す例外が発生します。

このような動作が望ましくない場合があり、そのようなメッセージを処理できる「キャッチオール」型の関数が必要になる場合があります。それを達成するために、フレームワークは org.springframework.cloud.function.context.DefaultMessageRoutingHandler 戦略を提供します。Bean として登録するだけです。そのデフォルトの実装は、メッセージがルーティング不可能であるという事実を単にログに記録しますが、例外なくメッセージフローを続行できるようにし、ルーティング不可能なメッセージを効果的にドロップします。より洗練されたものが必要な場合は、この戦略の独自の実装を提供し、それを Bean として登録するだけです。

@Bean
public DefaultMessageRoutingHandler defaultRoutingHandler() {
	return new DefaultMessageRoutingHandler() {
		@Override
		public void accept(Message<?> message) {
			// do something really cool
		}
	};
}

関数フィルタリング

フィルタリングは、"go" または "discard" の 2 つのパスしかないルーティングの型です。関数に関しては、ある条件が "true" を返した場合にのみ特定の関数を呼び出したい、そうでない場合は入力を破棄したいという意味です。ただし、入力を破棄することになると、アプリケーションのコンテキストでそれが何を意味するかについて多くの解釈があります。例: ログに記録したい場合や、破棄されたメッセージのカウンターを維持したい場合があります。また、何もしたくない場合もあります。これらの異なるパスのため、破棄されたメッセージを処理する方法の一般的な構成オプションは提供していません。代わりに、「破棄」パスを意味する単純なコンシューマーを定義することをお勧めします。

@Bean
public Consumer<?> devNull() {
   // log, count or whatever
}

これで、実際には 2 つのパスのみが効果的にフィルターになるルーティング式を作成できます。例:

--spring.cloud.function.routing-expression=headers.contentType.toString().equals('text/plain') ? 'echo' : 'devNull'

'echo' 関数に送られる条件に合わないすべてのメッセージは 'devNull' に送られ、そこでは何もできません。署名 Consumer<?> により、型変換が試行されないため、実行オーバーヘッドがほとんど発生しません。

リアクティブ入力(パブリッシャーなど)を処理する場合、ルーティング命令は関数プロパティを介してのみ提供する必要があります。これは、パブリッシャーを渡すために 1 回だけ呼び出され、残りはリアクターによって処理されるリアクティブ関数の性質によるものです。個々の値(メッセージなど)を介して通信されるルーティング命令にアクセスしたり、依存したりすることはできません。

複数のルーター

デフォルトでは、前のセクションで説明したように、フレームワークには常に単一のルーティング機能が構成されています。ただし、複数のルーティング機能が必要になる場合もあります。その場合、functionRouter 以外の名前を付ければ、既存のインスタンスに加えて RoutingFunction Bean の独自のインスタンスを作成できます。

マップ内のキー / 値ペアとして spring.cloud.function.routing-expression または spring.cloud.function.definition を RoutinFunction に渡すことができます。

これが簡単な例です

@Configuration
protected static class MultipleRouterConfiguration {

	@Bean
	RoutingFunction mySpecialRouter(FunctionCatalog functionCatalog, BeanFactory beanFactory, @Nullable MessageRoutingCallback routingCallback) {
		Map<String, String> propertiesMap = new HashMap<>();
		propertiesMap.put(FunctionProperties.PREFIX + ".routing-expression", "'reverse'");
		return new RoutingFunction(functionCatalog, propertiesMap, new BeanFactoryResolver(beanFactory), routingCallback);
	}

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

	@Bean
	public Function<String, String> uppercase() {
		return String::toUpperCase;
	}
}

そしてそれがどのように機能するかを示すテスト

`

@Test
public void testMultipleRouters() {
	System.setProperty(FunctionProperties.PREFIX + ".routing-expression", "'uppercase'");
	FunctionCatalog functionCatalog = this.configureCatalog(MultipleRouterConfiguration.class);
	Function function = functionCatalog.lookup(RoutingFunction.FUNCTION_NAME);
	assertThat(function).isNotNull();
	Message<String> message = MessageBuilder.withPayload("hello").build();
	assertThat(function.apply(message)).isEqualTo("HELLO");

	function = functionCatalog.lookup("mySpecialRouter");
	assertThat(function).isNotNull();
	message = MessageBuilder.withPayload("hello").build();
	assertThat(function.apply(message)).isEqualTo("olleh");
}

入力 / 出力エンリッチメント

受信メッセージまたは発信メッセージを変更または改善し、機能以外の問題からコードをクリーンに保つ必要がある場合がよくあります。ビジネスロジック内でそれを実行したくありません。

いつでも関数合成を介してそれを達成することができます。このようなアプローチには、いくつかの利点があります。

  • これにより、この非機能関心事を、ビジネス機能を機能定義として構成できる別の機能に分離することができます。

  • 受信メッセージが実際のビジネス機能に到達する前に何を変更できるかについて、完全な自由(および危険)を提供します。

@Bean
public Function<Message<?>, Message<?>> enrich() {
    return message -> MessageBuilder.fromMessage(message).setHeader("foo", "bar").build();
}

@Bean
public Function<Message<?>, Message<?>> myBusinessFunction() {
    // do whatever
}

次に、次の関数定義 enrich|myBusinessFunction を指定して、関数を作成します。

説明されているアプローチは最も柔軟性がありますが、コードを記述したり、Bean にするか、関数として手動で登録してから、ビジネス関数で構成する必要があるため、最も複雑です。前の例。

しかし、前の例のように、実行しようとしている変更(エンリッチメント)が簡単な場合はどうでしょうか。同じことを達成するための、より単純でより動的で構成可能なメカニズムはありますか?

バージョン 3.1.3 以降、フレームワークでは、SpEL 式を提供して、関数に入力される入力と関数から出力される出力の両方の個々のメッセージヘッダーを強化できます。例としてテストの 1 つを見てみましょう。

@Test
public void testMixedInputOutputHeaderMapping() throws Exception {
	try (ConfigurableApplicationContext context = new SpringApplicationBuilder(
			SampleFunctionConfiguration.class).web(WebApplicationType.NONE).run(
					"--logging.level.org.springframework.cloud.function=DEBUG",
					"--spring.main.lazy-initialization=true",
					"--spring.cloud.function.configuration.split.output-header-mapping-expression.keyOut1='hello1'",
					"--spring.cloud.function.configuration.split.output-header-mapping-expression.keyOut2=headers.contentType",
					"--spring.cloud.function.configuration.split.input-header-mapping-expression.key1=headers.path.split('/')[0]",
					"--spring.cloud.function.configuration.split.input-header-mapping-expression.key2=headers.path.split('/')[1]",
					"--spring.cloud.function.configuration.split.input-header-mapping-expression.key3=headers.path")) {

		FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class);
		FunctionInvocationWrapper function = functionCatalog.lookup("split");
		Message<byte[]> result = (Message<byte[]>) function.apply(MessageBuilder.withPayload("helo")
				.setHeader(MessageHeaders.CONTENT_TYPE, "application/json")
				.setHeader("path", "foo/bar/baz").build());
		assertThat(result.getHeaders().containsKey("keyOut1")).isTrue();
		assertThat(result.getHeaders().get("keyOut1")).isEqualTo("hello1");
		assertThat(result.getHeaders().containsKey("keyOut2")).isTrue();
		assertThat(result.getHeaders().get("keyOut2")).isEqualTo("application/json");
	}
}

ここでは、関数名 (つまり、split) に続いて、設定するメッセージヘッダーキーの名前と SpEL 式の値が続く、input-header-mapping-expression および output-header-mapping-expression というプロパティが表示されます。最初の式 ('keyOut1' 用) は、一重引用符で囲まれたリテラル SpEL 式で、実質的に 'keyOut1' を値 hello1 に設定します。keyOut2 は、既存の 'contentType' ヘッダーの値に設定されます。

また、既存のヘッダー「パス」の値を実際に分割し、key1 と key2 の個々の値をインデックスに基づいて分割された要素の値に設定する、入力ヘッダーマッピングのいくつかの興味深い機能を確認できます。

何らかの理由で提供された式の評価が失敗した場合、関数の実行は何も起こらないかのように続行されます。ただし、ログに警告メッセージが表示され、それが通知されます。
o.s.c.f.context.catalog.InputEnricher    : Failed while evaluating expression "hello1"  on incoming message. . .

複数の入力を持つ関数を扱っている場合(次のセクション)、input-header-mapping-expression の直後にインデックスを使用できます。

--spring.cloud.function.configuration.echo.input-header-mapping-expression[0].key1=‘hello1'
--spring.cloud.function.configuration.echo.input-header-mapping-expression[1].key2='hello2'

関数アリティ

データのストリームを分類して整理する必要がある場合があります。例: たとえば、「オーダー」と「請求書」を含む組織化されていないデータを処理する典型的なビッグデータのユースケースを考えてみましょう。それぞれを別々のデータストアに入れたいとします。ここで、関数アリティ(複数の入力と出力を持つ関数)のサポートが役立ちます。

そのような関数の例を見てみましょう(完全な実装の詳細はここにあります)

@Bean
public Function<Flux<Integer>, Tuple2<Flux<String>, Flux<String>>> organise() {
	return flux -> ...;
}

プロジェクト Reactor が SCF のコア依存関係であることを考えると、そのタプルライブラリを使用しています。タプルは、カーディナリティ情報の両方を伝達することにより、独自の利点を提供します。どちらも SCSt の文脈では非常に重要です。カーディナリティにより、作成して関数の対応する入力と出力にバインドする必要のある入力バインディングと出力バインディングの数を知ることができます。型情報を認識することで、適切な型変換が保証されます。

また、この関数では、2 つの出力バインディング名が organise-out-0 と organise-out-1 であるため、ここでバインディング名の命名規則の「インデックス」部分が機能します。

IMPORTANT: 現時点では、関数のアリティは、複合イベント処理を中心としたリアクティブ関数(Function<TupleN<Flux<?>…​>, TupleN<Flux<?>…​>>)でのみサポートされています。複合イベント処理では、イベントの合流点の評価と計算では、通常、単一のイベントではなく、イベントのストリームを表示する必要があります。

入力ヘッダーの伝播

典型的なシナリオでは、入力メッセージヘッダーは出力に伝播されず、当然のことながら、関数の出力は、それ自体のメッセージヘッダーのセットを必要とする他の何かへの入力である可能性があるためです。ただし、そのような伝播が必要になる場合があるため、Spring Cloud Function はこれを実現するためのいくつかのメカニズムを提供します。

まず、ヘッダーをいつでも手動でコピーできます。例: Message を受け取り、Message (つまり、Function<Message, Message>)を返すシグネチャーを持つ関数がある場合は、ヘッダーを自分で簡単かつ選択的にコピーできます。関数が Message を返す場合、フレームワークはペイロードを適切に変換する以外は何もしません。ただし、このようなアプローチは、特にすべてのヘッダーを単にコピーしたい場合は、少し面倒なことがわかる場合があります。このような場合を支援するために、入力ヘッダーを伝播する関数にブールフラグを設定できる単純なプロパティを提供します。プロパティは copy-input-headers です。

例: 次の構成があると仮定します。

@EnableAutoConfiguration
@Configuration
protected static class InputHeaderPropagationConfiguration {

	@Bean
	public Function<String, String> uppercase() {
		return x -> x.toUpperCase();
	}
}

ご存知のように、メッセージを送信することでこの関数を呼び出すことができます (フレームワークは型変換とペイロード抽出を処理します)

spring.cloud.function.configuration.uppercase.copy-input-headers を true に設定するだけで、次のアサーションも当てはまります。

Function<Message<String>, Message<byte[]>> uppercase = catalog.lookup("uppercase", "application/json");
Message<byte[]> result = uppercase.apply(MessageBuilder.withPayload("bob").setHeader("foo", "bar").build());
assertThat(result.getHeaders()).containsKey("foo");

型変換 (コンテンツ型のネゴシエーション)

コンテンツ型ネゴシエーションは Spring Cloud Function のコア機能の 1 つです。これにより、受信データを関数シグネチャーで宣言された型に変換できるだけでなく、関数の合成中に同じ変換を実行して、他の方法では(型ごとに)合成できない関数にすることができます。構成可能。

コンテンツ型ネゴシエーションの背後にあるメカニズムと必要性をよりよく理解するために、例として次の関数を使用して、非常に単純なユースケースを見ていきます。

@Bean
public Function<Person, String> personFunction {..}

前の例に示されている関数は、引数として Person オブジェクトを想定し、出力として文字列型を生成します。このような関数が型 Person で呼び出された場合、すべてが正常に機能します。ただし、通常、関数は、byte[]JSON String などの生の形式で提供されることが最も多い受信データのハンドラーのロールを果たします。フレームワークが受信データを引数としてこの関数に渡すことに成功するには、次のことを行う必要があります。どういうわけか、受信データを Person 型に変換します。

Spring Cloud Function は、それを実現するために Spring メカニズムに固有の 2 つのメカニズムに依存しています。

  1. MessageConverter- 受信メッセージデータから関数によって宣言された型に変換します。

  2. ConversionService- 受信非メッセージデータから関数によって宣言された型に変換します。

これは、生データの型(メッセージまたは非メッセージ)に応じて、Spring Cloud Function がいずれかのメカニズムを適用することを意味します。

他のリクエスト(HTTP、メッセージングなど)の一部として呼び出される関数を処理する場合、ほとんどの場合、フレームワークは MessageConverters に依存します。これは、そのようなリクエストがすでに Spring Message に変換されているためです。つまり、フレームワークは適切な MessageConverter を見つけて適用します。これを実現するには、フレームワークにユーザーからの指示が必要です。これらの命令の 1 つは、関数自体の署名(Person 型)によってすでに提供されています。理論的には、それで十分なはずです(場合によってはそれで十分です)。ただし、ほとんどのユースケースでは、適切な MessageConverter を選択するために、フレームワークに追加の情報が必要です。その欠落している部分は contentType ヘッダーです。

このようなヘッダーは通常、メッセージの一部として提供され、最初にそのようなメッセージを作成した対応するアダプターによって挿入されます。例: HTTP POST リクエストでは、コンテンツ型の HTTP ヘッダーがメッセージの contentType ヘッダーにコピーされます。

そのようなヘッダーが存在しない場合、フレームワークは application/json などのデフォルトのコンテンツ型に依存します。

コンテンツ型と引数型

前述のように、フレームワークが適切な MessageConverter を選択するには、引数型と、オプションでコンテンツ型情報が必要です。適切な MessageConverter を選択するためのロジックは、ユーザー定義関数の呼び出しの直前(実際の引数型がフレームワークに認識されている場合)にトリガーされる引数リゾルバーにあります。引数の型が現在のペイロードの型と一致しない場合、フレームワークは事前構成された MessageConverters のスタックに委譲して、それらのいずれかがペイロードを変換できるかどうかを確認します。

contentType と引数型の組み合わせは、フレームワークが適切な MessageConverter を見つけることによってメッセージをターゲット型に変換できるかどうかを判断するメカニズムです。適切な MessageConverter が見つからない場合は、例外がスローされます。これは、カスタム MessageConverter を追加することで処理できます(User-defined Message Converters を参照)。

Message が contentType のみに基づいて他の型に変換されることを期待しないでください。contentType はターゲット型を補完することを忘れないでください。これは、MessageConverter が考慮に入れる場合と考慮しない場合があるヒントです。

メッセージコンバーター

MessageConverters は、次の 2 つのメソッドを定義します。

Object fromMessage(Message<?> message, Class<?> targetClass);

Message<?> toMessage(Object payload, @Nullable MessageHeaders headers);

特に Spring Cloud Stream のコンテキストでは、これらのメソッドの契約とその使用箇所を理解することが重要です。

fromMessage メソッドは、受信 Message を引数型に変換します。Message のペイロードは任意の型にすることができ、複数の型をサポートするのは MessageConverter の実際の実装次第です。

提供された MessageConverters

前述のように、フレームワークは、最も一般的なユースケースを処理するための MessageConverters のスタックをすでに提供しています。次のリストは、提供された MessageConverters を優先順に説明しています(最初に機能する MessageConverter が使用されます)。

  1. JsonMessageConverter: Jackson (デフォルト) または Gson ライブラリを使用して contentType が application/json である場合に、Message のペイロードと POJO 間の変換をサポートします。このメッセージコンバーターは、type パラメーター (例: application/json;type=foo.bar.person ) も認識します。これは、関数の開発時に型が不明であるため、関数のシグネチャーが Function<?, ?>Function、または Function<Object, Object> のように見える場合に便利です。言い換えれば、型変換の場合、通常は関数シグネチャーから型を派生します。mime-type パラメーターを使用すると、より動的な方法で型を伝達できます。

  2. ByteArrayMessageConvertercontentType が application/octet-stream の場合に、Message のペイロードを byte[] から byte[] に変換することをサポートします。これは本質的にパススルーであり、主に下位互換性のために存在します。

  3. StringMessageConvertercontentType が text/plain の場合、任意の型の String への変換をサポートします。

適切なコンバーターが見つからない場合、フレームワークは例外をスローします。その場合は、コードと構成をチェックして、何も見逃していないことを確認する必要があります(つまり、バインディングまたはヘッダーを使用して contentType を提供したことを確認してください)。ただし、ほとんどの場合、いくつかのまれなケース(おそらく、カスタム contentType など)が見つかり、提供されている MessageConverters の現在のスタックは変換方法を認識していません。その場合は、カスタム MessageConverter を追加できます。ユーザー定義のメッセージコンバーターを参照してください。

ユーザー定義のメッセージコンバーター

Spring Cloud Function は、追加の MessageConverters を定義および登録するメカニズムを公開しています。これを使用するには、org.springframework.messaging.converter.MessageConverter を実装し、@Bean として構成します。次に、`MessageConverter` の既存のスタックに追加されます。

カスタム MessageConverter 実装が既存のスタックの先頭に追加されることを理解することが重要です。その結果、カスタム MessageConverter 実装は既存の実装よりも優先され、既存のコンバーターをオーバーライドしたり、追加したりすることができます。

次の例は、application/bar と呼ばれる新しいコンテンツ型をサポートするメッセージコンバーター Bean を作成する方法を示しています。

@SpringBootApplication
public static class SinkApplication {

    ...

    @Bean
    public MessageConverter customMessageConverter() {
        return new MyCustomMessageConverter();
    }
}

public class MyCustomMessageConverter extends AbstractMessageConverter {

    public MyCustomMessageConverter() {
        super(new MimeType("application", "bar"));
    }

    @Override
    protected boolean supports(Class<?> clazz) {
        return (Bar.class.equals(clazz));
    }

    @Override
    protected Object convertFromInternal(Message<?> message, Class<?> targetClass, Object conversionHint) {
        Object payload = message.getPayload();
        return (payload instanceof Bar ? payload : new Bar((byte[]) payload));
    }
}

JSON オプションに関する注意

Spring Cloud Function では、JSON を処理するための Jackson および Gson メカニズムをサポートしています。そして、あなたの利益のために、それ自体が 2 つのメカニズムを認識し、選択したものを使用するか、デフォルトのルールに従う org.springframework.cloud.function.json.JsonMapper でそれを抽象化しました。デフォルトのルールは次のとおりです。

  • 使用されるメカニズムであるクラスパス上にあるライブラリ。クラスパスに com.fasterxml.jackson.* がある場合は、Jackson が使用され、com.google.code.gson がある場合は、Gson が使用されます。

  • 両方がある場合は、Gson がデフォルトになります。または、gson または jackson の 2 つの値のいずれかを使用して spring.cloud.function.preferred-json-mapper プロパティを設定できます。

とはいえ、型変換は通常、開発者には透過的ですが、org.springframework.cloud.function.json.JsonMapper も Bean として登録されているため、必要に応じてコードに簡単に挿入できます。

Kotlin Lambda サポート

また、Kotlin ラムダのサポートも提供しています(v2.0 以降)。次のことを考慮してください。

@Bean
open fun kotlinSupplier(): () -> String {
    return  { "Hello from Kotlin" }
}

@Bean
open fun kotlinFunction(): (String) -> String {
    return  { it.toUpperCase() }
}

@Bean
open fun kotlinConsumer(): (String) -> Unit {
    return  { println(it) }
}

上記は、Spring Bean として構成された Kotlin ラムダを表しています。各署名は、Java で SupplierFunctionConsumer に相当するものにマップされるため、フレームワークによってサポート / 認識される署名になります。Kotlin から Java へのマッピングの仕組みはこのドキュメントの範囲外ですが、「Java 8 関数のサポート」セクションで概説されている署名変換の同じルールがここでも適用されることを理解することが重要です。

Kotlin サポートを有効にするには、クラスパスに Kotlin SDK ライブラリを追加するだけで、適切な自動構成とサポートクラスがトリガーされます。

関数コンポーネントスキャン

Spring Cloud Function は、functions と呼ばれるパッケージ内の FunctionConsumerSupplier の実装が存在する場合、それをスキャンします。この機能を使用すると、Spring に依存しない関数を記述できます。@Component アノテーションも必要ありません。別のパッケージを使用する場合は、spring.cloud.function.scan.packages を設定できます。spring.cloud.function.scan.enabled=false を使用して、スキャンを完全にオフにすることもできます。

スタンドアロン Web アプリケーション

関数は、HTTP エンドポイントとして自動的にエクスポートできます。

spring-cloud-function-web モジュールには、Spring Boot Web アプリケーション(MVC サポート付き)に含まれているときにアクティブになる自動構成があります。簡単な開始体験が必要な場合に備えて、すべてのオプションの依存関係を収集する spring-cloud-starter-function-web もあります。

Web 構成をアクティブにすると、アプリには MVC エンドポイント(デフォルトでは "/" にありますが、spring.cloud.function.web.path で構成可能)があり、関数名が URL パスの一部になるアプリケーションコンテキストで関数にアクセスするために使用できます。サポートされているコンテンツ型はプレーンテキストと JSON です。

SCF は Functional Bean を REST エンドポイントとしてエクスポートする機能を提供しますが、Spring MVC/WebFlux などの代替ではないことを理解することが重要です。SCF は主に、ステートレス機能を HTTP 経由で公開したいだけのステートレスサーバーレスパターンに対応するためのものです。
メソッド パス リクエスト レスポンス ステータス

GET

/{supplier}

-

指定されたサプライヤーからのアイテム

200 OK

POST

/{consumer}

JSON オブジェクトまたはテキスト

入力をミラーリングし、リクエスト本文をコンシューマーにプッシュします

202 承認済み

PUT

/{consumer}

JSON オブジェクトまたはテキスト

入力をミラーリングし、リクエスト本文をコンシューマーにプッシュします

202 承認済み

DELETE

/{consumer}

JSON オブジェクトまたはテキスト

-

204 コンテンツがありません

POST

/{function}

JSON オブジェクトまたはテキスト

名前付き関数を適用した結果

200 OK

PUT

/{function}

JSON オブジェクトまたはテキスト

名前付き関数を適用した結果

200 OK

GET

/{function}/{item}

-

アイテムをオブジェクトに変換し、関数を適用した結果を返します

200 OK

上の表が示すように、エンドポイントの動作はメソッドと受信リクエストデータの型によって異なります。受信データが単一値であり、ターゲット関数が明らかに単一値として宣言されている場合 (つまり、コレクションまたは Flux を返さない場合)、レスポンスにも単一値が含まれます。複数値のレスポンスの場合、クライアントは Accept: text/event-stream を送信することで、サーバーから送信されたイベントストリームをリクエストできます。

Message<?> で入力と出力を使用して宣言された関数とコンシューマーは、リクエストヘッダーをメッセージヘッダーとして認識し、出力メッセージヘッダーは HTTP ヘッダーに変換されます。body がない場合、または null の場合、メッセージのペイロードは body または空の文字列になります。

テキストを POST する場合、レスポンス形式は、コンテンツネゴシエーションに応じて、Spring Boot 2.0 以前のバージョンとは異なる場合があります(最良の結果を得るには、コンテンツ型を指定し、ヘッダーを受け入れます)。

このようなアプリケーションをテストする方法の詳細と例については、関数アプリケーションのテストを参照してください。

HTTP リクエストパラメーター

前の表からお気づきのように、引数をパス変数(つまり、/{function}/{item})として関数に渡すことができます。例: http://localhost:8080/uppercase/foo は、入力パラメーターが foo である uppercase 関数を呼び出します。

これは推奨されるアプローチであり、ほとんどのユースケースに適合しますが、HTTP リクエストパラメーター (例: http://localhost:8080/uppercase/foo?name=Bill) を処理する必要がある場合があります。フレームワークは、HTTP リクエストパラメーターを HTTP ヘッダーと同様に、ヘッダーキー http_request_param の Message ヘッダーは、その値がリクエストパラメーターの Map であるため、それらにアクセスするには、関数入力シグネチャーで Message 型 (例: Function<Message<String>, String>) を受け入れる必要があります。便宜上、HeaderUtils.HTTP_REQUEST_PARAM 定数を提供しています。

関数マッピングルール

カタログに関数(コンシューマーなど)が 1 つしかない場合、パス内の名前はオプションです。つまり、カタログ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d hello に uppercase 関数しかない場合、curl -H "Content-Type: text/plain" localhost:8080/ -d hello 呼び出しは同じです。

複合関数は、パイプまたはコンマを使用して関数名を区切ることができます(パイプは、URL パスでは有効ですが、コマンドラインで入力するのは少し厄介です)。例: curl -H "Content-Type: text/plain" localhost:8080/uppercase,reverse -d hello

カタログに複数の関数がある場合、各関数はエクスポートされ、関数名がパスの一部としてマップされます (例: localhost:8080/uppercase)。このシナリオでは、spring.cloud.function.definition プロパティを提供することで、特定の関数または関数構成をルートパスにマップできます。

以下に例を示します。

--spring.cloud.function.definition=foo|bar

上記のプロパティは、"foo" および "bar" 関数を構成し、構成された関数を "/" パスにマップします。

同じプロパティは、URL を介して関数を解決できない場合にも機能します。例: URL は localhost:8080/uppercase である可能性がありますが、uppercase 関数はありません。ただし、関数 foo と bar があります。この場合、localhost:8080/uppercase は foo|bar に解決されます。これは、URL を使用して特定の情報を伝達する場合に特に役立ちます。これは、実際の URL の値を含む uri というメッセージヘッダーがあり、ユーザーがそれを評価と計算に使用できるようにするためです。

関数フィルタリングルール

カタログに複数の関数がある状況では、特定の関数または関数構成のみをエクスポートする必要がある場合があります。その場合、; で区切られた、エクスポートする同じ spring.cloud.function.definition プロパティリスト関数を使用できます。この場合、ルートパスには何もマップされず、一覧にない関数 (コンポジションを含む) はエクスポートされないことに注意してください。

以下に例を示します。

--spring.cloud.function.definition=foo;bar

これにより、カタログで使用可能な関数の数(localhost:8080/foo など)に関係なく、関数 foo と関数 bar のみがエクスポートされます。

--spring.cloud.function.definition=foo|bar;baz

これにより、カタログで使用可能な関数の数(localhost:8080/foo,bar など)に関係なく、関数構成 foo|bar と関数 baz のみがエクスポートされます。

Spring Cloud Function と CRUD REST

ここまでで、関数が REST エンドポイントとしてエクスポートされ、さまざまな HTTP メソッドを使用して呼び出せることが明確になったはずです。言い換えれば、GET、POST、PUT などを介して単一の関数をトリガーできます。

ただし、これは常に望ましいとは限らず、CRUD の概念には当てはまりません。また、SCF は Spring Web スタックのすべての機能をサポートしておらず、サポートするつもりもありませんが、フレームワークは、単一の関数を特定の HTTP メソッドにマッピングできる CRUD マッピングのサポートを提供します。これは、spring.cloud.function.http.<method-name> プロパティによって実行されます。

以下に例を示します。

spring.cloud.function.http.GET=uppercase;reverse;foo|bar
spring.cloud.function.http.POST=reverse
spring.cloud.function.http.DELETE=deleteById

ご覧のとおり、ここでは spring.cloud.function.definition プロパティと同じルールを使用して関数をさまざまな HTTP メソッドにマッピングしています。を使用すると、いくつかの関数を定義できます。」は機能構成を意味します。

スタンドアロンストリーミングアプリケーション

ブローカー (RabbitMQ や Kafka など) からメッセージを送受信するには、spring-cloud-stream プロジェクトと Spring Cloud Function との統合を利用できます。詳細と例については、Spring Cloud Stream リファレンスマニュアルの Spring Cloud Function (英語) セクションを参照してください。

パッケージ化された関数のデプロイ

Spring Cloud Function は、分離されたクラスローダーを使用して jar ファイル(またはデプロイされたアーカイブ、または jar ファイルのセット)を起動し、そこで定義された関数を公開できる「デプロイヤー」ライブラリを提供します。これは非常に強力なツールであり、たとえば、ターゲットの jar ファイルを変更せずに、さまざまな入出力アダプターに関数を適合させることができます。サーバーレスプラットフォームにはこの種の機能が組み込まれていることが多いため、このようなプラットフォームの関数呼び出し側の構成要素と見なすことができます(実際、リフ (英語) Java 関数呼び出し側はこのライブラリを使用します)。

標準のエントリポイントは、spring-cloud-function-deployer をクラスパスに追加することです。デプロイヤーが起動し、関数 jar の場所を指定するための構成を探します。

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-function-deployer</artifactId>
	<version>${spring.cloud.function.version}</version>
</dependency>

少なくとも、ユーザーは、関数を含むアーカイブの URL またはリソースの場所である spring.cloud.function.location を提供する必要があります。オプションで、maven: プレフィックスを使用して、依存関係ルックアップを介してアーティファクトを見つけることができます(詳細については、FunctionProperties を参照してください)。Spring Boot アプリケーションは jar ファイルからブートストラップされ、MANIFEST.MF を使用して開始クラスが検索されるため、たとえば、標準の Spring Boot fat jar が適切に機能します。ターゲット jar を正常に起動できる場合、結果はメインアプリケーションの FunctionCatalog に登録された関数になります。登録された関数は、分離されたクラスローダーで(deault によって)作成された場合でも、メインアプリケーションのコードで適用できます。

これは、「大文字」関数を含む JAR をデプロイし、それを呼び出す例です。

@SpringBootApplication
public class DeployFunctionDemo {

	public static void main(String[] args) {
		ApplicationContext context = SpringApplication.run(DeployFunctionDemo.class,
				"--spring.cloud.function.location=..../target/uppercase-0.0.1-SNAPSHOT.jar",
				"--spring.cloud.function.definition=uppercase");

		FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
		Function<String, String> function = catalog.lookup("uppercase");
		System.out.println(function.apply("hello"));
	}
}

そして、これが Maven URI を使用した例です(FunctionDeployerTests のテストの 1 つから取得):

@SpringBootApplication
public class DeployFunctionDemo {

	public static void main(String[] args) {
		String[] args = new String[] {
				"--spring.cloud.function.location=maven://oz.demo:demo-uppercase:0.0.1-SNAPSHOT",
				"--spring.cloud.function.function-class=oz.demo.uppercase.MyFunction" };

		ApplicationContext context = SpringApplication.run(DeployerApplication.class, args);
		FunctionCatalog catalog = context.getBean(FunctionCatalog.class);
		Function<String, String> function = catalog.lookup("myFunction");

		assertThat(function.apply("bob")).isEqualTo("BOB");
	}
}

ローカルおよびリモートリポジトリ、ユーザー、パスワードなどの Maven リソースは、ローカルのデフォルトを効果的に使用し、ほとんどの場合に機能するデフォルトの MavenProperties を使用して解決されることに注意してください。ただし、カスタマイズする必要がある場合は、型 MavenProperties の Bean を提供するだけで、追加のプロパティを設定できます(以下の例を参照)。

@Bean
public MavenProperties mavenProperties() {
	MavenProperties properties = new MavenProperties();
	properties.setLocalRepository("target/it/");
	return properties;
}

サポートされているパッケージングシナリオ

現在、Spring Cloud Function はいくつかのパッケージングシナリオをサポートしており、機能のデプロイに関して最も柔軟性があります。

シンプルな JAR

このパッケージオプションは、Spring に関連するものに依存しないことを意味します。たとえば ; このような JAR には次のクラスが含まれていると考えてください。

package function.example;
. . .
public class UpperCaseFunction implements Function<String, String> {
	@Override
	public String apply(String value) {
		return value.toUpperCase();
	}
}

このようなパッケージをデプロイするときに行う必要があるのは、location および function-class プロパティを指定することだけです。

--spring.cloud.function.location=target/it/simplestjar/target/simplestjar-1.0.0.RELEASE.jar
--spring.cloud.function.function-class=function.example.UpperCaseFunction

場合によっては、複数の関数を一緒にパッケージ化することも考えられます。このようなシナリオでは、spring.cloud.function.function-class プロパティを使用して、; で区切るいくつかのクラスを一覧表示できます。

以下に例を示します。

--spring.cloud.function.function-class=function.example.UpperCaseFunction;function.example.ReverseFunction

ここでは、デプロイする 2 つの関数を特定しています。これらの関数には、関数カタログで名前でアクセスできます(例: catalog.lookup("reverseFunction");)。

詳細については、こちらから [GitHub] (英語) 入手できる完全なサンプルを参照してください。対応するテストは FunctionDeployerTests [GitHub] (英語) にもあります。

  • コンポーネントスキャン *

バージョン 3.1.4 以降、関数コンポーネントスキャンで説明されているコンポーネントスキャン機能を使用して構成を簡素化できます。関数クラスを functions という名前のパッケージに配置する場合、フレームワークが関数カタログにロードする関数クラスを自動検出するため、spring.cloud.function.function-class プロパティを省略できます。関数ルックアップを実行するときに従う命名規則に注意してください。たとえば、関数クラス functions.UpperCaseFunction は、upperCaseFunction という名前で FunctionCatalog で使用可能になります。

Spring Boot JAR

このパッケージ化オプションは、Spring Boot に依存しており、JAR が Spring Boot JAR として生成されたことを意味します。とはいえ、デプロイされた JAR が分離されたクラスローダーで実行されることを考えると、実際のデプロイヤーが使用する Spring Boot バージョンとバージョンの競合は発生しません。たとえば ; このような JAR には、次のクラスが含まれていることを考慮してください(Spring/Spring Boot がクラスパス上にある場合は、Spring の依存関係が追加される可能性があります)。

package function.example;
. . .
public class UpperCaseFunction implements Function<String, String> {
	@Override
	public String apply(String value) {
		return value.toUpperCase();
	}
}

以前と同様に、このようなパッケージをデプロイするときに、location プロパティと function-class プロパティを指定するだけです。

--spring.cloud.function.location=target/it/simplestjar/target/simplestjar-1.0.0.RELEASE.jar
--spring.cloud.function.function-class=function.example.UpperCaseFunction

詳細については、こちらから [GitHub] (英語) 入手できる完全なサンプルを参照してください。対応するテストは FunctionDeployerTests [GitHub] (英語) にもあります。

Spring Boot アプリケーション

このパッケージ化オプションは、JAR がマネージド Spring Bean として機能する完全なスタンドアロン Spring Boot アプリケーションであることを意味します。以前と同様に、Spring Boot に依存しており、JAR が Spring Boot JAR として生成されたという明らかな仮定があります。とはいえ、デプロイされた JAR が分離されたクラスローダーで実行されることを考えると、実際のデプロイヤーが使用する Spring Boot バージョンとバージョンの競合は発生しません。たとえば ; このような JAR には次のクラスが含まれていると考えてください。

package function.example;
. . .
@SpringBootApplication
public class SimpleFunctionAppApplication {

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

	@Bean
	public Function<String, String> uppercase() {
		return value -> value.toUpperCase();
	}
}

別の Spring アプリケーションコンテキストを効果的に処理しており、関数が Spring マネージド Bean であることを考えると、location プロパティに加えて、function-class の代わりに definition プロパティも指定します。

--spring.cloud.function.location=target/it/bootapp/target/bootapp-1.0.0.RELEASE-exec.jar
--spring.cloud.function.definition=uppercase

詳細については、こちらから [GitHub] (英語) 入手できる完全なサンプルを参照してください。対応するテストは FunctionDeployerTests [GitHub] (英語) にもあります。

この特定のデプロイオプションでは、クラスパスに Spring Cloud Function が含まれる場合と含まれない場合があります。デプロイヤーの観点からは、これは重要ではありません。

関数 Bean の定義

Spring Cloud Function は、高速起動が必要な小さなアプリ向けに、「関数」スタイルの Bean 宣言をサポートしています。Bean 宣言の機能スタイルは、5.1 が大幅に強化された Spring Framework 5.0 の機能でした。

関数と従来の Bean 定義の比較

これは、おなじみの @Configuration および @Bean 宣言スタイルを備えたバニラ Spring Cloud Function アプリケーションです。

@SpringBootApplication
public class DemoApplication {

  @Bean
  public Function<String, String> uppercase() {
    return value -> value.toUpperCase();
  }

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

}

関数 Bean の場合: ユーザーアプリケーションコードは、次のように「関数」形式に再キャストできます。

@SpringBootConfiguration
public class DemoApplication implements ApplicationContextInitializer<GenericApplicationContext> {

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

  public Function<String, String> uppercase() {
    return value -> value.toUpperCase();
  }

  @Override
  public void initialize(GenericApplicationContext context) {
    context.registerBean("demo", FunctionRegistration.class,
        () -> new FunctionRegistration<>(uppercase())
            .type(FunctionTypeUtils.functionType(String.class, String.class)));
  }

}

主な違いは次のとおりです。

  • メインクラスは ApplicationContextInitializer です。

  • @Bean メソッドは context.registerBean() への呼び出しに変換されました

  • @SpringBootApplication は @SpringBootConfiguration に置き換えられ、Spring Boot 自動構成を有効にしていないことを示していますが、クラスは「エントリポイント」としてマークされています。

  • Spring Boot の SpringApplication は、Spring Cloud Function の FunctionalSpringApplication に置き換えられました(これはサブクラスです)。

Spring Cloud Function アプリに登録するビジネスロジック Bean の型は FunctionRegistration です。これは、関数と、入力型と出力型に関する情報の両方を含むラッパーです。アプリケーションの @Bean 形式では、その情報は反射的に取得できますが、関数 Bean 登録では、FunctionRegistration を使用しない限り、その一部が失われます。

ApplicationContextInitializer および FunctionRegistration を使用する代わりに、アプリケーション自体に Function (または Consumer または Supplier)を実装させることもできます。例(上記と同等):

@SpringBootConfiguration
public class DemoApplication implements Function<String, String> {

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

  @Override
  public String apply(String value) {
    return value.toUpperCase();
  }

}

型 Function の別個のスタンドアロンクラスを追加し、run() メソッドの代替形式を使用して SpringApplication に登録する場合にも機能します。主なことは、ジェネリクス型の情報は、実行時にクラス宣言を通じて利用できるということです。

持っているとしましょう

@Component
public class CustomFunction implements Function<Flux<Foo>, Flux<Bar>> {
	@Override
	public Flux<Bar> apply(Flux<Foo> flux) {
		return flux.map(foo -> new Bar("This is a Bar object from Foo value: " + foo.getValue()));
	}

}

それをそのように登録します:

@Override
public void initialize(GenericApplicationContext context) {
		context.registerBean("function", FunctionRegistration.class,
				() -> new FunctionRegistration<>(new CustomFunction()).type(CustomFunction.class));
}

関数 Bean 宣言の制限

ほとんどの Spring Cloud Function アプリは、Spring Boot 全体に比べてスコープが比較的小さいため、これらの関数 Bean 定義に簡単に適合させることができます。その限られた範囲を超えた場合は、@Bean スタイルの構成に戻すか、ハイブリッドアプローチを使用して、Spring Cloud Function アプリを継承できます。たとえば、外部データストアとの統合に Spring Boot 自動構成を利用する場合は、@EnableAutoConfiguration を使用する必要があります。必要に応じて関数宣言を使用して関数を定義することもできますが(つまり、「ハイブリッド」スタイル)、その場合は、Spring Boot が制御を取り戻すことができるように、spring.functional.enabled=false を使用して「完全関数モード」を明示的にオフにする必要があります。

機能の視覚化と制御

Spring Cloud Function は、アクチュエーターエンドポイントおよびプログラムによる方法で FunctionCatalog で使用可能な機能の視覚化をサポートします。

プログラム的な方法

プログラムでアプリケーションコンテキスト内で使用可能な機能を確認するには、FunctionCatalog にアクセスするだけです。ここでは、カタログのサイズを取得するためのメソッド、ルックアップ関数、使用可能なすべての関数の名前を一覧表示できます。

以下に例を示します。

FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class);
int size = functionCatalog.size(); // will tell you how many functions available in catalog
Set<String> names = functionCatalog.getNames(null); will list the names of all the Function, Suppliers and Consumers available in catalog
. . .
アクチュエーター

アクチュエーターと Web はオプションであるため、最初に Web 依存関係の 1 つを追加し、アクチュエーター依存関係を手動で追加する必要があります。次の例は、Web フレームワークの依存関係を追加する方法を示しています。

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
</dependency>

次の例は、WebFlux フレームワークの依存関係を追加する方法を示しています。

<dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

次のように、アクチュエーターの依存関係を追加できます。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

また、次のプロパティを設定して、functions アクチュエーターのエンドポイントを有効にする必要があります: --management.endpoints.web.exposure.include=functions

次の URL にアクセスして、FunctionCatalog の機能を確認してください。http://<host>:<port>/actuator/functions (英語)

以下に例を示します。

curl http://localhost:8080/actuator/functions

出力は次のようになります。

{"charCounter":
	{"type":"FUNCTION","input-type":"string","output-type":"integer"},
 "logger":
 	{"type":"CONSUMER","input-type":"string"},
 "functionRouter":
 	{"type":"FUNCTION","input-type":"object","output-type":"object"},
 "words":
 	{"type":"SUPPLIER","output-type":"string"}. . .

関数アプリケーションのテスト

Spring Cloud Function には、Spring Boot ユーザーにとって非常に馴染みのある統合テスト用のユーティリティもいくつかあります。

これがアプリケーションであるとします。

@SpringBootApplication
public class SampleFunctionApplication {

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

    @Bean
    public Function<String, String> uppercase() {
        return v -> v.toUpperCase();
    }
}

このアプリケーションをラップする HTTP サーバーの統合テストは次のとおりです。

@SpringBootTest(classes = SampleFunctionApplication.class,
            webEnvironment = WebEnvironment.RANDOM_PORT)
public class WebFunctionTests {

    @Autowired
    private TestRestTemplate rest;

    @Test
    public void test() throws Exception {
        ResponseEntity<String> result = this.rest.exchange(
            RequestEntity.post(new URI("/uppercase")).body("hello"), String.class);
        System.out.println(result.getBody());
    }
}

または、関数 Bean 定義スタイルが使用されている場合:

@FunctionalSpringBootTest
public class WebFunctionTests {

    @Autowired
    private TestRestTemplate rest;

    @Test
    public void test() throws Exception {
        ResponseEntity<String> result = this.rest.exchange(
            RequestEntity.post(new URI("/uppercase")).body("hello"), String.class);
        System.out.println(result.getBody());
    }
}

このテストは、同じアプリの @Bean バージョンに対して作成するテストとほぼ同じです。唯一の違いは、通常の @SpringBootTest ではなく @FunctionalSpringBootTest アノテーションです。@Autowired TestRestTemplate のような他のすべての部品は、標準の Spring Boot 機能です。

そして、ここで正しい依存関係を支援するために、POM からの抜粋があります

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.9</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    . . . .
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-function-web</artifactId>
        <version>4.0.5</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

または、FunctionCatalog のみを使用して非 HTTP アプリのテストを作成することもできます。例:

@FunctionalSpringBootTest
public class FunctionalTests {

	@Autowired
	private FunctionCatalog catalog;

	@Test
	public void words() {
		Function<String, String> function = catalog.lookup(Function.class,
				"uppercase");
		assertThat(function.apply("hello")).isEqualTo("HELLO");
	}

}

サーバーレスプラットフォームアダプター

Spring Cloud Function アプリケーションは、スタンドアロンプロセスとして実行できるだけでなく、既存のサーバーレスプラットフォームの 1 つを実行するように適合させることができます。プロジェクトには、AWS Lambda [GitHub] (英語) および Azure [GitHub] (英語) 用のアダプターがあります。Oracle Fn プラットフォーム [GitHub] (英語) には独自の Spring Cloud Function アダプターがあります。また、リフ (英語) は Java 関数をサポートし、その Java 関数呼び出し元 [GitHub] (英語) はネイティブに機能し、Spring Cloud Function jar のアダプターです。

AWS Lambda

AWS [Amazon] アダプターは Spring Cloud Function アプリを受け取り、AWSLambda で実行できる形式に変換します。

AWS Lambda をじっと見つめる方法の詳細はこのドキュメントの範囲外であるため、ユーザーは AWS と AWS Lambda にある程度精通しており、Spring が提供する付加価値を知りたいと考えています。

入門

Spring Cloud Function フレームワークのゴールの 1 つは、単純な関数アプリケーションが特定の環境で特定の方法で相互作用できるようにするために必要なインフラストラクチャ要素を提供することです。単純な関数アプリケーション(コンテキストまたは Spring)は、型 Supplier、Function、Consumer の Bean を含むアプリケーションです。つまり、AWS では、単純な関数 Bean が AWSLambda 環境で何らかの形で認識および実行される必要があることを意味します。

例を見てみましょう:

@SpringBootApplication
public class FunctionConfiguration {

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

	@Bean
	public Function<String, String> uppercase() {
		return value -> value.toUpperCase();
	}
}

これは、関数 Bean が定義された完全な Spring Boot アプリケーションを示しています。興味深いのは、これは表面上は単なる別の Boot アプリですが、AWS Adapter のコンテキストでは、完全に有効な AWSLambda アプリケーションでもあります。他のコードや構成は必要ありません。パッケージ化してデプロイするだけなので、その方法を見てみましょう。

物事を簡単にするために、ビルドおよびデプロイの準備ができたサンプルプロジェクトを提供しており、ここから [GitHub] (英語) アクセスできます。

./mvnw clean package を実行するだけで JAR ファイルを生成できます。必要なすべての maven プラグインは、適切な AWS デプロイ可能な JAR ファイルを生成するようにすでにセットアップされています。(JAR レイアウトの詳細については JAR レイアウトに関する注記を参照してください)。

次に、JAR ファイルを(AWS ダッシュボードまたは AWS CLI を介して)AWS にアップロードする必要があります。

ハンドラーについて確認するときは、一般的なリクエストハンドラーである org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest を指定します。

AWS deploy

以上です。この関数の場合、関数が大文字で返される文字列であることが期待されるいくつかのサンプルデータを使用して、関数を保存して実行します。

org.springframework.cloud.function.adapter.aws.FunctionInvoker は、AWS Lambda API の詳細から完全に分離することを目的とした汎用 AWS の RequestHandler 実装ですが、場合によっては、使用する特定の AWS の RequestHandler を指定することもできます。次のセクションでは、それを実現する方法について説明します。

AWS リクエストハンドラー

アダプターには、使用できる汎用リクエストハンドラーがいくつかあります。最も一般的なのは(そして「入門」セクションで使用したもの)は、AWS の RequestStreamHandler の実装である org.springframework.cloud.function.adapter.aws.FunctionInvoker です。ユーザーは他に何もする必要はなく、関数をデプロイするときに AWS ダッシュボードで「ハンドラー」として指定します。Kinesis、ストリーミングなどを含むほとんどのケースを処理します。

アプリに Function などの型の @Bean が複数ある場合は、spring.cloud.function.definition プロパティまたは環境変数を構成して使用するものを選択できます。関数は Spring Cloud FunctionCatalog から抽出されます。spring.cloud.function.definition を指定しない場合、フレームワークは、最初に Function、次に Consumer、最後に Supplier を検索する検索順序に従って、デフォルトを見つけようとします)。

AWS 関数ルーティング

Spring Cloud Function のコア機能の 1 つはルーティングです。これは、ユーザーが提供したルーティング指示に基づいて、他の機能に委譲する 1 つの特別な機能を持つ関数です。

AWS Lambda 環境では、この機能により 1 つの追加の利点が提供されます。これにより、単一の関数 (ルーティング関数) を AWS Lambda としてバインドできるため、API Gateway の単一の HTTP エンドポイントにバインドできます。最終的には 1 つの関数と 1 つのエンドポイントのみを管理し、アプリケーションの一部として使用できる多くの関数の恩恵を受けることができます。

詳細は提供されたサンプル [GitHub] (英語) で入手できますが、メンションする価値のある一般的なことはほとんどありません。

org.springframework.cloud.function.adapter.aws.FunctionInvoker は AWS Lambda としてバインドする関数を決定できないため、アプリケーションに複数の関数がある場合は常に、ルーティング機能がデフォルトで有効になります。そのため、デフォルトは RoutingFunction です。つまり、必要なことは、いくつかのメカニズムを使用して実行できるルーティング指示を提供することだけです (詳細については、サンプル [GitHub] (英語) を参照してください)。

また、AWS では環境変数の名前にドット . やハイフン `-` を使用できないため、Boot サポートを利用して、ドットをアンダースコアに、ハイフンをキャメルケースに置き換えることができます。たとえば、spring.cloud.function.definition は spring_cloud_function_definition になり、spring.cloud.function.routing-expression は spring_cloud_function_routingExpression になります。

カスタムランタイムを使用した AWS 関数ルーティング

[ カスタムランタイム ] を使用する場合、機能ルーティングは同じように機能します。必要なのは、関数の名前をハンドラーとして使用するのと同じ方法で、functionRouter を AWS ハンドラーとして指定することだけです。

JAR レイアウトに関する注記

Lambda では実行時に Spring Cloud Function Web または Stream アダプターは必要ないため、AWS に送信する JAR を作成する前に除外する必要がある場合があります。Lambda アプリケーションはシェーディングする必要がありますが、Spring Boot スタンドアロンアプリケーションはシェーディングしないため、2 つの別々の jar を使用して同じアプリを実行できます(サンプルのとおり)。サンプルアプリは、2 つの jar ファイルを作成します。1 つは Lambda にデプロイするための aws 分類子を使用し、もう 1 つは実行時に spring-cloud-function-web を含む 実行可能(シン)jar を作成します。Spring Cloud Function は、Start-Class 属性(親スターターを使用する場合は Spring Boot ツールによって追加されます)を使用して、JAR ファイルマニフェストから「メインクラス」を見つけようとします。マニフェストに Start-Class がない場合は、関数を AWS にデプロイするときに環境変数またはシステムプロパティ MAIN_CLASS を使用できます。

関数 Bean 定義を使用しておらず、Spring Boot の自動構成に依存しており、spring-boot-starter-parent に依存していない場合は、追加のトランスフォーマーを maven-shade-plugin 実行の一部として構成する必要があります。

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-shade-plugin</artifactId>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
			<version>2.7.4</version>
		</dependency>
	</dependencies>
	<executions>
		<execution>
			<goals>
			     <goal>shade</goal>
			</goals>
			<configuration>
				<createDependencyReducedPom>false</createDependencyReducedPom>
				<shadedArtifactAttached>true</shadedArtifactAttached>
				<shadedClassifierName>aws</shadedClassifierName>
				<transformers>
					<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
						<resource>META-INF/spring.handlers</resource>
					</transformer>
					<transformer implementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer">
						<resource>META-INF/spring.factories</resource>
					</transformer>
					<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
						<resource>META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports</resource>
					</transformer>
					<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
						<resource>META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports</resource>
					</transformer>
					<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
						<resource>META-INF/spring.schemas</resource>
					</transformer>
					<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
						<resource>META-INF/spring.components</resource>
					</transformer>
				</transformers>
			</configuration>
		</execution>
	</executions>
</plugin>

ビルドファイルの設定

AWS Lambda で Spring Cloud Function アプリケーションを実行するために、クラウドプラットフォームプロバイダーが提供する Maven または Gradle プラグインを利用できます。

Maven

Maven のアダプタープラグインを使用するには、プラグインの依存関係を pom.xml ファイルに追加します。

<dependencies>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-function-adapter-aws</artifactId>
	</dependency>
</dependencies>

JAR レイアウトに関する注記で指摘されているように、AWS Lambda にアップロードするには、シェーディングされた jar が必要です。そのために Maven Shade プラグイン [Apache] (英語) を使用できます。セットアップの例は上記にあります。

Spring Boot Maven プラグインを使用して、シン jar を生成できます。

<plugin>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-maven-plugin</artifactId>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot.experimental</groupId>
			<artifactId>spring-boot-thin-layout</artifactId>
			<version>${wrapper.version}</version>
		</dependency>
	</dependencies>
</plugin>

Maven を使用して AWSLambda に Spring Cloud Function アプリケーションをデプロイするためのサンプル pom.xml ファイル全体は ​ ここにあります [GitHub] (英語)

Gradle

Gradle のアダプタープラグインを使用するには、build.gradle ファイルに依存関係を追加します。

dependencies {
	compile("org.springframework.cloud:spring-cloud-function-adapter-aws:${version}")
}

JAR レイアウトに関する注記で指摘されているように、AWS Lambda にアップロードするには、シェーディングされた jar が必要です。そのために Gradle Shadow プラグイン (英語) を使用できます。

buildscript {
	dependencies {
		classpath "com.github.jengelman.gradle.plugins:shadow:${shadowPluginVersion}"
	}
}
apply plugin: 'com.github.johnrengelman.shadow'

assemble.dependsOn = [shadowJar]

import com.github.jengelman.gradle.plugins.shadow.transformers.*

shadowJar {
	classifier = 'aws'
	dependencies {
		exclude(
			dependency("org.springframework.cloud:spring-cloud-function-web:${springCloudFunctionVersion}"))
	}
	// Required for Spring
	mergeServiceFiles()
	append 'META-INF/spring.handlers'
	append 'META-INF/spring.schemas'
	append 'META-INF/spring.tooling'
	append 'META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports'
	append 'META-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports'
	transform(PropertiesFileTransformer) {
		paths = ['META-INF/spring.factories']
		mergeStrategy = "append"
	}
}

Spring Boot Gradle プラグインと Spring Boot シン Gradle プラグインを使用してシン jar を生成できます。

buildscript {
	dependencies {
		classpath("org.springframework.boot.experimental:spring-boot-thin-gradle-plugin:${wrapperVersion}")
		classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
	}
}
apply plugin: 'org.springframework.boot'
apply plugin: 'org.springframework.boot.experimental.thin-launcher'
assemble.dependsOn = [thinJar]

Gradle を使用して AWSLambda に Spring Cloud Function アプリケーションをデプロイするためのサンプル build.gradle ファイル全体は ​ ここにあります [GitHub] (英語)

アップロード

spring-cloud-function-samples/function-sample-aws でサンプルをビルドし、-aws jar ファイルを Lambda にアップロードします。ハンドラーは example.Handler または org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler にすることができます(Lambda はメソッド参照を受け入れますが、クラスの FQN であり、メソッド参照ではありません)。

./mvnw -U clean package

AWS コマンドラインツールを使用すると、次のようになります。

aws lambda create-function --function-name Uppercase --role arn:aws:iam::[USERID]:role/service-role/[ROLE] --zip-file fileb://function-sample-aws/target/function-sample-aws-2.0.0.BUILD-SNAPSHOT-aws.jar --handler org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler --description "Spring Cloud Function Adapter Example" --runtime java8 --region us-east-1 --timeout 30 --memory-size 1024 --publish

AWS サンプルの関数の入力型は、"value" と呼ばれる単一のプロパティを持つ Foo です。テストするにはこれが必要になります。

{
  "value": "test"
}
AWS サンプルアプリは「関数」スタイルで記述されています(ApplicationContextInitializer として)。これは、Lambda での起動時に、従来の @Bean スタイルよりもはるかに高速であるため、@Beans (または @EnableAutoConfiguration)が必要ない場合は、これを選択することをお勧めします。ウォームスタートは影響を受けません。

型変換

Spring Cloud Function は、生の入力ストリームと関数によって宣言された型との間の型変換を透過的に処理しようとします。

例: 関数シグネチャーがそのような Function<Foo, Bar> である場合、受信ストリームイベントを Foo のインスタンスに変換しようとします。

イベント型が不明であるか、判別できない場合(Function<?, ?> など)、受信ストリームイベントを汎用 Map に変換しようとします。

生の入力

生の入力にアクセスしたい場合があります。この場合、必要なのは、InputStream を受け入れるように関数シグネチャーを宣言することだけです。例: Function<InputStream, ?>。この場合、変換は試行せず、生の入力を関数に直接渡します。

Microsoft Azure Functions

Spring Cloud Function アプリケーションをネイティブ Azure Java 関数としてデプロイするための Azure (英語) 関数アダプター。

Azure Functions  プログラミングモデル (英語) は、関数のハンドラーメソッドとその入出力型を定義するための Java アノテーション (英語) を広範囲に中継します。コンパイル時に、アノテーション付きクラスは提供された Azure Maven/Gradle プラグインによって処理され、必要な Azure Function バインディングファイル、構成、パッケージアーティファクトが生成されます。Azure アノテーションは、java 関数が Azure function として認識されるように構成する型 セーフな方法にすぎません。

spring-cloud-function-adapter-azure [GitHub] (英語) は、基本的なプログラミングモデルを継承して、Spring および Spring Cloud Function のサポートを提供します。アダプターを使用すると、依存関係の注入を使用して Spring Cloud Function アプリケーションを構築し、必要なサービスを Azure ハンドラーメソッドに自動接続できます。

scf azure adapter
Web ベースの関数アプリケーションの場合、汎用 adapter-azure を特殊な spring-cloud-function-adapter-azure-web [GitHub] (英語) に置き換えることができます。Azure Web アダプターを使用すると、任意の Spring Web アプリケーションを Azure、HttpTrigger 関数としてデプロイできます。このアダプターは、Azure アノテーションの複雑さを隠し、代わりに使い慣れた Spring Web プログラミングモデルを使用します。詳細については、以下の Azure Web アダプターセクションを参照してください。

.1。Azure アダプター

Azure Functions に Spring および Spring Cloud Function の統合を提供します。

.1.1。依存関係

Azure Function 統合を有効にするには、Azure アダプターの依存関係を pom.xml または build.gradle ファイルに追加します。

Maven
<dependencies>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-function-adapter-azure</artifactId>
	</dependency>
</dependencies>
Gradle
dependencies {
    implementation 'org.springframework.cloud:spring-cloud-function-adapter-azure'
}
バージョン 4.0.0+ が必要です。アダプターをクラスパス上に置くと、Azure Java ワーカー統合がアクティブ化されます。

.1.2。開発ガイドライン

@Component (または @Service) アノテーションを使用して、既存の Azure Function クラス (@FunctionName ハンドラーなど) を Spring コンポーネントに変換します。その後、必要な依存関係 (または Spring Cloud Function 構成の関数カタログ ) を自動接続し、Azure function ハンドラー内で使用できます。

@Component (1)
public class MyAzureFunction {

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

	// The FunctionCatalog leverages the Spring Cloud Function framework.
	@Autowired private FunctionCatalog functionCatalog; (2)

	@FunctionName("spring") (3)
	public String plainBean( (4)
			@HttpTrigger(name = "req", authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
			ExecutionContext context) {

		return this.uppercase.apply(request.getBody().get());
	}

	@FunctionName("scf") (3)
	public String springCloudFunction( (5)
			@HttpTrigger(name = "req", authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
			ExecutionContext context) {

		// Use SCF composition. Composed functions are not just spring beans but SCF such.
		Function composed = this.functionCatalog.lookup("reverse|uppercase"); (6)

		return (String) composed.apply(request.getBody().get());
	}
}
1MyAzureFunction クラスが、Spring Framework によって自動検出およびクラスパススキャンの候補として考慮される「コンポーネント」であることを示します。
2HttpTriggerDemoApplication で定義された uppercase Bean と functionCatalog Bean を自動接続します (下記)。
3@FunctionName (英語) アノテーションは、指定された Azure function ハンドラーを識別します。トリガー ( @HttpTrigger など) によって呼び出されると、関数はそのトリガーおよびその他の入力を処理して 1 つ以上の出力を生成します。
4plainBean メソッドハンドラーは、オートワイヤーされた uppercase Spring Bean を使用して結果を計算する Azure function にマップされます。Azure ハンドラーで「プレーン」Spring コンポーネントを使用する方法を示します。
5springCloudFunction メソッドハンドラーは別の Azure function にマップされ、自動接続された FunctionCatalog インスタンスを使用して結果を計算します。
6Spring Cloud Function 関数カタログ合成 API を活用する方法を示します。
com.microsoft.azure.functions.annotation.* (英語) パッケージに含まれる Java アノテーションを使用して、入力と出力をメソッドにバインドします。

Azure ハンドラー内で使用されるビジネスロジックの実装は、一般的な Spring アプリケーションと似ています。

@SpringBootApplication (1)
public class HttpTriggerDemoApplication {

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

	@Bean
	public Function<String, String> uppercase() { (2)
		return payload -> payload.toUpperCase();
	}

	@Bean
	public Function<String, String> reverse() { (2)
		return payload -> new StringBuilder(payload).reverse().toString();
	}
}
1 メインクラスの設定で説明したように、@SpringBootApplication アノテーション付きクラスは Main-Class として使用されます。
2 関数はオートワイヤーされ、Azure function ハンドラーで使用されます。
関数カタログ

Spring Cloud Function は、一貫した実行モデルを提供しながら、ユーザー定義関数のさまざまな型シグネチャーをサポートします。このために、関数カタログを使用して、すべてのユーザー定義関数を正規表現に変換します。

Azure アダプターは、上記の uppercase などの Spring コンポーネントをオートワイヤーできます。ただし、これらは正規の Spring Cloud 関数としてではなく、プレーンな Java クラスインスタンスとして扱われます。

Spring Cloud Function を利用して正規関数表現にアクセスするには、FunctionCatalog をオートワイヤーし、上記の functionCatalog インスタンスや springCloudFunction() ハンドラーと同様に、それをハンドラーで使用する必要があります。

Azure へのアクセス ExecutionContext

Azure ランタイムによって com.microsoft.azure.functions.ExecutionContext の形式で提供されるターゲット実行コンテキストにアクセスする必要がある場合があります。たとえば、そのようなニーズの 1 つはロギングであるため、Azure コンソールに表示できます。

その目的のために、AzureFunctionUtil.enhanceInputIfNecessary を使用すると、ExecutionContext のインスタンスをメッセージヘッダーとして追加できるため、executionContext キーを介して取得できます。

@FunctionName("myfunction")
public String execute(
	@HttpTrigger(name = "req", authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
	ExecutionContext context) {

	Message message =
		(Message) AzureFunctionUtil.enhanceInputIfNecessary(request.getBody().get(), context); (1)

	return this.uppercase.apply(message);
}
1AzureFunctionUtil ユーティリティを利用して、AzureFunctionUtil.EXECUTION_CONTEXT ヘッダーキーを使用して context をメッセージヘッダーとしてインライン化します。

これで、メッセージヘッダーから ExecutionContext を取得できるようになりました。

@Bean
public Function<Message<String>, String> uppercase(JsonMapper mapper) {
	return message -> {
		String value = message.getPayload();
		ExecutionContext context =
			(ExecutionContext) message.getHeaders().get(AzureFunctionUtil.EXECUTION_CONTEXT); (1)
		. . .
	}
}
1 ヘッダーから ExecutionContext インスタンスを取得します。

.1.3。構成

Microsoft Azure で関数アプリケーションを実行するには、function.json や host.json などの必要な構成を提供し、必須のパッケージ化形式 (英語) に従う必要があります。

通常、Azure Maven (または Gradle) プラグインは、アノテーション付きクラスから必要な構成を生成し、必要なパッケージ形式を生成するために使用されます。

Azure パッケージ形式 (英語) は、デフォルトの Spring Boot パッケージ (例: uber jar) と互換性がありません。以下の Spring Boot プラグインを無効にするセクションでは、これに対処する方法について説明します。
Azure Maven/Gradle プラグイン

Azure は、アノテーション付きクラスを処理し、必要な構成を生成し、期待されるパッケージレイアウトを生成するための Maven [GitHub] (英語) および Gradle [GitHub] (英語) プラグインを提供します。プラグインは、次のようにプラットフォーム、ランタイム、アプリ設定のプロパティを設定するために使用されます。

Maven
<plugin>
	<groupId>com.microsoft.azure</groupId>
	<artifactId>azure-functions-maven-plugin</artifactId>
	<version>1.22.0 or higher</version>

	<configuration>
		<appName>YOUR-AZURE-FUNCTION-APP-NAME</appName>
		<resourceGroup>YOUR-AZURE-FUNCTION-RESOURCE-GROUP</resourceGroup>
		<region>YOUR-AZURE-FUNCTION-APP-REGION</region>
		<appServicePlanName>YOUR-AZURE-FUNCTION-APP-SERVICE-PLANE-NAME</appServicePlanName>
		<pricingTier>YOUR-AZURE-FUNCTION-PRICING-TIER</pricingTier>

		<hostJson>${project.basedir}/src/main/resources/host.json</hostJson>

		<runtime>
			<os>linux</os>
			<javaVersion>11</javaVersion>
		</runtime>

		<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>
Gradle
plugins {
    id "com.microsoft.azure.azurefunctions" version "1.11.0"
	// ...
}

apply plugin: "com.microsoft.azure.azurefunctions"

azurefunctions {
	appName = 'YOUR-AZURE-FUNCTION-APP-NAME'
    resourceGroup = 'YOUR-AZURE-FUNCTION-RESOURCE-GROUP'
    region = 'YOUR-AZURE-FUNCTION-APP-REGION'
    appServicePlanName = 'YOUR-AZURE-FUNCTION-APP-SERVICE-PLANE-NAME'
    pricingTier = 'YOUR-AZURE-FUNCTION-APP-SERVICE-PLANE-NAME'

    runtime {
      os = 'linux'
      javaVersion = '11'
    }

    auth {
      type = 'azure_cli'
    }

    appSettings {
      FUNCTIONS_EXTENSION_VERSION = '~4'
    }
	// Uncomment to enable local debug
    // localDebug = "transport=dt_socket,server=y,suspend=n,address=5005"
}

ランタイム構成の詳細については、Java バージョン (英語) デプロイ OS (英語) を参照してください。

Spring Boot プラグインを無効にする

予想どおり、Azure Functions は SpringBoot ランタイム内ではなく、Azure 実行ランタイム内で実行されます。さらに、Azure は、Azure Maven/Gradle プラグインによって生成される、デフォルトの Spring Boot パッケージ化と互換性のない特定のパッケージ化形式を想定しています。

SpringBoot Maven/Gradle プラグインを無効にするか、次の Maven スニペットに示すように Spring Boot シンランチャー [GitHub] (英語) を使用する必要があります。

<plugin>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-maven-plugin</artifactId>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot.experimental</groupId>
			<artifactId>spring-boot-thin-layout</artifactId>
		</dependency>
	</dependencies>
</plugin>
メインクラスの構成

Main-Class/Start-Class を指定して、Spring アプリケーションエントリポイント (上の例の HttpTriggerDemoApplication クラスなど) を指します。

Maven start-class プロパティを使用するか、MANIFEST/META-INFO の Main-Class 属性を設定できます。

Maven
<properties>
	<start-class>YOUR APP MAIN CLASS</start-class>
	...
</properties>
Gradle
jar {
    manifest {
        attributes(
            "Main-Class": "YOUR-APP-MAIN-CLASS"
        )
    }
}
あるいは、MAIN_CLASS 環境変数を使用してクラス名を明示的に設定することもできます。ローカル実行の場合は、MAIN_CLASS 変数を local.settings.json ファイルに追加し、Azure ポータルデプロイの場合はアプリの設定 (英語) に変数を設定します。
MAIN_CLASS 変数が設定されていない場合、Azure アダプターはクラスパス上で見つかった jar から MANIFEST/META-INFO 属性を検索し、@SpringBootApplication または @SpringBootConfiguration アノテーションのいずれかが付けられた最初の Main-Class: を選択します。
メタデータの構成

共有 host.json (英語) ファイルを使用して関数アプリを構成できます。

{
	"version": "2.0",
	"extensionBundle": {
		"id": "Microsoft.Azure.Functions.ExtensionBundle",
		"version": "[4.*, 5.0.0)"
	}
}

host.json メタデータファイルには、関数アプリインスタンス内のすべての関数に影響を与える構成オプションが含まれています。

ファイルがプロジェクトのトップフォルダーにない場合は、それに応じてプラグインを設定する必要があります (hostJson maven 属性など)。

.2。Azure Web アダプター

純粋な Web ベースの関数アプリケーションの場合は、汎用 adapter-azure を特殊な spring-cloud-function-adapter-azure-web [GitHub] (英語) に置き換えることができます。Azure Web アダプターは、内部で HttpTrigger を使用して、任意の Spring Web アプリケーションをネイティブ Azure function としてデプロイできます。Azure アノテーションの複雑さを隠し、代わりに使い慣れた Spring Web プログラミングモデルに依存します。

Azure Web アダプターを有効にするには、アダプターの依存関係を pom.xml または build.gradle ファイルに追加します。

Maven
<dependencies>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-function-adapter-azure-web</artifactId>
	</dependency>
</dependencies>
Gradle
dependencies {
    implementation 'org.springframework.cloud:spring-cloud-function-adapter-azure-web'
}

同じ構成および使用方法命令が Azure Web Adapter にも適用されます。

.2.1。サンプル

詳細については、Azure Web アダプターのサンプルを参照してください。

.3。使用箇所

Azure Adapter 型と Azure Web Adapter タイプのアプリケーションの両方を構築およびデプロイするための共通の手順。

.3.1。建てる

Maven
./mvnw -U clean package
Gradle
./gradlew azureFunctionsPackage

.3.2。ローカルで実行する

Azure Functions 上でローカルに実行し、ライブ Azure 環境にデプロイするには、Azure CLI と共に Azure Functions Core Tools をインストールする必要があります ( こちら を参照)。構成によっては、アズライトエミュレーター (英語) も必要になる場合があります。

次に、サンプルを実行します。

Maven
./mvnw azure-functions:run
Gradle
./gradlew azureFunctionsRun

.3.3。Azure で実行

Azure アカウントにログインしていることを確認してください。

az login

そしてデプロイ

Maven
./mvnw azure-functions:deploy
Gradle
./gradlew azureFunctionsDeploy

.3.4。ローカルでデバッグする

関数をデバッグモードで実行します。

Maven
./mvnw azure-functions:run -DenableDebug
Gradle
// If you want to debug your functions, please add the following line
// to the azurefunctions section of your build.gradle.
azurefunctions {
  ...
  localDebug = "transport=dt_socket,server=y,suspend=n,address=5005"
}

別の方法として、次のように local.settings.json に JAVA_OPTS の値を指定します。

{
	"IsEncrypted": false,
	"Values": {
		...
		"FUNCTIONS_WORKER_RUNTIME": "java",
		"JAVA_OPTS": "-Djava.net.preferIPv4Stack=true -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=127.0.0.1:5005"
	}
}

VSCode リモートデバッグ構成のスニペットを次に示します。

{
	"version": "0.2.0",
	"configurations": [
		{
			"type": "java",
			"name": "Attach to Remote Program",
			"request": "attach",
			"hostName": "localhost",
			"port": "5005"
		},
	]
}

.4. FunctionInvoker (非推奨)

従来の FunctionInvoker プログラミングモデルは非推奨となり、今後はサポートされなくなります。

関数統合アプローチに関する追加のドキュメントとサンプルについては、azure-sample [GitHub] (英語) README とコードに従ってください。

Google クラウド機能

Google Cloud Functions アダプターを使用すると、Spring Cloud Function アプリを Google クラウド機能 サーバーレスプラットフォームで実行できるようになります。この関数は、オープンソース Google 関数 フレームワーク (Java 用) [GitHub] (英語) を使用してローカルで実行することも、GCP 上で実行することもできます。

プロジェクトの依存関係

プロジェクトに spring-cloud-function-adapter-gcp 依存関係を追加することから始めます。

<dependencies>
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-function-adapter-gcp</artifactId>
	</dependency>

	...
</dependencies>

さらに、デプロイする関数の JAR をビルドする spring-boot-maven-plugin を追加します。

spring-boot-maven-plugin の依存関係として spring-cloud-function-adapter-gcp も参照していることに注意してください。これは、Google Cloud Functions 上の デプロイ用の正しい JAR 形式で関数をパッケージ化するようにプラグインを変更するために必要です。
<plugin>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-maven-plugin</artifactId>
	<configuration>
		<outputDirectory>target/deploy</outputDirectory>
	</configuration>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-function-adapter-gcp</artifactId>
		</dependency>
	</dependencies>
</plugin>

最後に、Java 用 Google 関数 フレームワークの一部として提供される Maven プラグインを追加します。これにより、mvn function:run を介して関数をローカルでテストできるようになります。

関数ターゲットは常に org.springframework.cloud.function.adapter.gcp.GcfJarLauncher に設定する必要があります。これは、Google Cloud Functions プラットフォームから Spring Cloud Function へのエントリポイントとして機能するアダプタークラスです。
<plugin>
	<groupId>com.google.cloud.functions</groupId>
	<artifactId>function-maven-plugin</artifactId>
	<version>0.9.1</version>
	<configuration>
		<functionTarget>org.springframework.cloud.function.adapter.gcp.GcfJarLauncher</functionTarget>
		<port>8080</port>
	</configuration>
</plugin>

動作する pom.xml の完全な例は、Spring Cloud 関数 GCP サンプル [GitHub] (英語) にあります。

HTTP 関数

Google Cloud Functions は、HTTP リクエストによって呼び出される関数である HTTP 関数 のデプロイをサポートしています。以下のセクションでは、Spring Cloud Function を HTTP 関数としてデプロイする手順について説明します。

入門

簡単な Spring Cloud Function の例から始めましょう。

@SpringBootApplication
public class CloudFunctionMain {

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

	@Bean
	public Function<String, String> uppercase() {
		return value -> value.toUpperCase();
	}
}

resources/META-INF/MANIFEST.MF で構成のメインクラスを指定します。

Main-Class: com.example.CloudFunctionMain

次に、関数をローカルで実行します。これは、プロジェクトの依存関係セクションで説明されている Google Cloud Functions function-maven-plugin によって提供されます。

mvn function:run

HTTP 関数を呼び出します。

curl http://localhost:8080/ -d "hello"
GCP にデプロイする

アプリケーションをパッケージ化することから始めます。

mvn package

上記で定義したカスタム spring-boot-maven-plugin プラグインを追加した場合は、結果の JAR が target/deploy ディレクトリに表示されるはずです。この JAR は、デプロイから Google Cloud Functions 用に正しくフォーマットされています。

次に、Cloud SDK CLI がインストールされていることを確認します。

プロジェクトのベースディレクトリから、次のコマンドを実行してデプロイします。

gcloud functions deploy function-sample-gcp-http \
--entry-point org.springframework.cloud.function.adapter.gcp.GcfJarLauncher \
--runtime java11 \
--trigger-http \
--source target/deploy \
--memory 512MB

HTTP 関数を呼び出します。

curl https://REGION-PROJECT_ID.cloudfunctions.net/function-sample-gcp-http -d "hello"

カスタム HTTP statusCode の設定:

Functions can specify a custom HTTP response code by setting the `FunctionInvoker.HTTP_STATUS_CODE` header.
@Bean
public Function<String, Message<String>> function() {

	String payload = "hello";

	Message<String> message = MessageBuilder.withPayload(payload).setHeader(FunctionInvoker.HTTP_STATUS_CODE, 404).build();

	return input -> message;
};

バックグラウンド関数

Google Cloud Functions は、Cloud Pub/Sub トピックのメッセージ、Cloud Storage バケットの変更、Firebase (英語) イベントなどのイベントに応じて間接的に呼び出されるバックグラウンド関数 のデプロイもサポートしています。

spring-cloud-function-adapter-gcp を使用すると、関数をバックグラウンド関数としてデプロイすることもできます。

以下のセクションでは、Cloud Pub/Sub トピックの背景関数を作成するプロセスについて説明します。ただし、ここでは説明されていない、バックグラウンド関数の実行をトリガーできるさまざまなイベント型がいくつかあります。これらはバックグラウンド関数がドキュメントをトリガーします で説明されています。

入門

GCF バックグラウンド関数として実行される単純な Spring Cloud Function から始めましょう。

@SpringBootApplication
public class BackgroundFunctionMain {

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

	@Bean
	public Consumer<PubSubMessage> pubSubFunction() {
		return message -> System.out.println("The Pub/Sub message data: " + message.getData());
	}
}

さらに、以下の定義でプロジェクトに PubSubMessage クラスを作成します。このクラスは、Pub/Sub トピックイベントで関数に渡される Pub/Sub イベント構造 を表します。

public class PubSubMessage {

	private String data;

	private Map<String, String> attributes;

	private String messageId;

	private String publishTime;

	public String getData() {
		return data;
	}

	public void setData(String data) {
		this.data = data;
	}

	public Map<String, String> getAttributes() {
		return attributes;
	}

	public void setAttributes(Map<String, String> attributes) {
		this.attributes = attributes;
	}

	public String getMessageId() {
		return messageId;
	}

	public void setMessageId(String messageId) {
		this.messageId = messageId;
	}

	public String getPublishTime() {
		return publishTime;
	}

	public void setPublishTime(String publishTime) {
		this.publishTime = publishTime;
	}

}

resources/META-INF/MANIFEST.MF で構成のメインクラスを指定します。

Main-Class: com.example.BackgroundFunctionMain

次に、関数をローカルで実行します。これは、プロジェクトの依存関係セクションで説明されている Google Cloud Functions function-maven-plugin によって提供されます。

mvn function:run

HTTP 関数を呼び出します。

curl localhost:8080 -H "Content-Type: application/json" -d '{"data":"hello"}'

ログを表示して、関数が呼び出されたことを確認します。

GCP にデプロイする

バックグラウンド関数を GCP にデプロイするには、最初にアプリケーションをパッケージ化します。

mvn package

上記で定義したカスタム spring-boot-maven-plugin プラグインを追加した場合は、結果の JAR が target/deploy ディレクトリに表示されるはずです。この JAR は、デプロイから Google Cloud Functions 用に正しくフォーマットされています。

次に、Cloud SDK CLI がインストールされていることを確認します。

プロジェクトのベースディレクトリから、次のコマンドを実行してデプロイします。

gcloud functions deploy function-sample-gcp-background \
--entry-point org.springframework.cloud.function.adapter.gcp.GcfJarLauncher \
--runtime java11 \
--trigger-topic my-functions-topic \
--source target/deploy \
--memory 512MB

Google クラウド関数は、--trigger-topic で指定されたトピックにメッセージがパブリッシュされるたびに関数を呼び出すようになります。

バックグラウンド機能のテストと検証のウォークスルーについては、GCF バックグラウンド関数のサンプル [GitHub] (英語) を実行するための手順を参照してください。

サンプル関数

プロジェクトは、参照として次のサンプル関数を提供します。