Mark Fisher, Dave Syer, Oleg Zhurakousky, Anshul Mehra, Dan Dobrin
4.0.1
導入
Spring Cloud Function は、次の高レベルのゴールを持つプロジェクトです。
関数を介したビジネスロジックの実装を推進します。
同じコードを Web エンドポイント、ストリームプロセッサー、タスクとして実行できるように、ビジネスロジックの開発ライフサイクルを特定のランタイムターゲットから分離します。
サーバーレスプロバイダー全体で統一されたプログラミングモデルをサポートし、スタンドアロン(ローカルまたは PaaS)で実行する機能をサポートします。
サーバーレスプロバイダーで Spring Boot 機能(自動構成、依存性注入、メトリクス)を有効にします。
すべてのトランスポートの詳細とインフラストラクチャを抽象化し、開発者が使い慣れたツールとプロセスをすべて維持し、ビジネスロジックにしっかりと集中できるようにします。
完全な実行可能かつテスト可能な Spring Boot アプリケーション(単純な文字列操作を実装)は次のとおりです。
@SpringBootApplication
public class Application {
@Bean
public Function<Flux<String>, Flux<String>> uppercase() {
return flux -> flux.map(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>
簡単に言うと、アプリケーションコンテキスト内の Supplier、Function、Consumer 型の Bean は、FunctionCatalog に登録できます。これは、このリファレンスマニュアルで説明されているすべての機能の恩恵を受けることができることを意味します。
不適格な関数のフィルタリング
一般的なアプリケーションコンテキストには、有効な java 関数であるが、FunctionCatalog に登録される候補となることを意図していない Bean が含まれる場合があります。このような 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 にはルーティング機能が搭載されており、1 つの関数を呼び出すと、その関数が実際に呼び出したい関数へのルーターとして機能します。この機能は、複数の機能の設定を維持することが面倒であったり、複数の機能を公開することができない特定の FAAS 環境において非常に有用です。
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
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> など)に対してのみ機能することを理解することが重要です。つまり、命令関数を使用してメッセージごとにルーティングすることしかできません。 リアクティブ関数では、メッセージごとにルーティングすることはできません。ルーティング命令はアプリケーションプロパティとしてのみ提供できます。それはすべて作業単位に関するものです。命令機能では、作業単位はメッセージであるため、このような作業単位に基づいてルーティングできます。リアクティブ関数では、作業単位がストリーム全体であるため、アプリケーションプロパティを介して提供された命令のみに基づいて動作し、ストリーム全体をルーティングします。 |
ルーティング命令の優先順位
ルーティング命令を提供するメカニズムがいくつかあることを考えると、複数のメカニズムが同時に使用される場合の競合解決の優先順位を理解することが重要です。そのため、順序は次のとおりです。
MessageRoutingCallback(関数が命令型の場合、他に何かが定義されているかどうかに関係なく引き継ぎます)メッセージヘッダー (機能が必須で、
MessageRoutingCallbackが提供されていない場合)アプリケーションのプロパティ (任意の関数)
ルーティング不可能なメッセージ
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 つのメカニズムに依存しています。
MessageConverter- 受信メッセージデータから関数によって宣言された型に変換します。
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 が使用されます)。
JsonMessageConverter: Jackson または Gson ライブラリ(DEFAULT)を使用してcontentTypeがapplication/jsonである場合に、Messageのペイロードを POJO との間で変換することをサポートします。ByteArrayMessageConverter:contentTypeがapplication/octet-streamの場合に、Messageのペイロードをbyte[]からbyte[]に変換することをサポートします。これは本質的にパススルーであり、主に下位互換性のために存在します。StringMessageConverter:contentTypeが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 で Supplier、Function、Consumer に相当するものにマップされるため、フレームワークによってサポート / 認識される署名になります。Kotlin から Java へのマッピングの仕組みはこのドキュメントの範囲外ですが、「Java 8 関数のサポート」セクションで概説されている署名変換の同じルールがここでも適用されることを理解することが重要です。
Kotlin サポートを有効にするには、クラスパスに Kotlin SDK ライブラリを追加するだけで、適切な自動構成とサポートクラスがトリガーされます。
関数コンポーネントスキャン
Spring Cloud Function は、functions と呼ばれるパッケージ内の Function、Consumer、Supplier の実装が存在する場合、それをスキャンします。この機能を使用すると、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 です。
| メソッド | パス | リクエスト | レスポンス | ステータス |
|---|---|---|---|---|
GET | /{supplier} | - | 指定されたサプライヤーからのアイテム | 200 OK |
POST | /{consumer} | JSON オブジェクトまたはテキスト | 入力をミラーリングし、リクエスト本文をコンシューマーにプッシュします | 202 承認済み |
POST | /{consumer} | JSON 配列または新しい行のテキスト | 入力をミラーリングし、本文を 1 つずつコンシューマーにプッシュします | 202 承認済み |
POST | /{function} | JSON オブジェクトまたはテキスト | 名前付き関数を適用した結果 | 200 OK |
POST | /{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 のみがエクスポートされます。
スタンドアロンストリーミングアプリケーション
ブローカー(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.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
. . . .
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-web</artifactId>
<version>4.0.1</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 を指定します。

以上です。この関数の場合、関数が大文字で返される文字列であることが期待されるいくつかのサンプルデータを使用して、関数を保存して実行します。
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]
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
Azure (英語) アダプターは、Spring Cloud Function コンテキストをブートストラップし、必要に応じて Spring Boot 構成を使用して、Azure フレームワークからユーザー関数に関数呼び出しをチャネルします。Azure Functions は、Azure プラットフォームに固有のユーザーコード内のアノテーションを含む、非常にユニークで侵襲的なプログラミングモデルを備えています。ただし、Spring Cloud Function (具体的には org.springframework.cloud.function.adapter.azure.FunctionInvoker) によって提供される統合スタイルのため、このアノテーションベースのプログラミングモデルは、単純な java 関数 (Azure を意識しない関数) を構成する型 セーフな方法にすぎないことを理解することが重要です。Azure function として認識されます。必要なのは、FunctionInvoker を継承するハンドラーを作成し、関数ハンドラーメソッドを定義して構成し、handleRequest(..) メソッドへのコールバックを行うことだけです。このハンドラーメソッドは、アノテーション付きのメソッドパラメーターとして入力および出力の型を提供します (Azure がクラスをインスペクションし、JSON バインディングを作成できるようにします)。
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) {
Message<String> message = MessageBuilder.withPayload(request.getBody().get()).copyHeaders(request.getHeaders()).build();
return handleRequest(message, context);
}
}
Azure アノテーションを介して構成を提供するフォームとは別に、このハンドラーメソッドの本体内に Message のインスタンスを作成し、その結果を返す handleRequest(..) メソッドへのコールバックを作成することに注意してください。
遅延している実際のユーザー関数は次のようになります
@Bean
public Function<String, String> uppercase() {
return payload -> payload.toUpperCase();
}
OR
@Bean
public Function<Message<String>, String> uppercase() {
return message -> message.getPayload().toUpperCase();
}
メッセージを作成するときに、必要に応じて HTTP ヘッダーを効果的にコピーして利用できるようにすることができます。
org.springframework.cloud.function.adapter.azure.FunctionInvoker クラスには 2 つの便利なメソッド(handleRequest と handleOutput)があり、実際の関数呼び出しを委譲できるため、ほとんどの場合、関数には 1 行しかありません。
関数名(定義)は Azure の ExecutionContext.getFunctionName() メソッドから取得され、アプリケーションコンテキストで複数の関数を効果的にサポートします。
Azure へのアクセス ExecutionContext
Azure ランタイムによって com.microsoft.azure.functions.ExecutionContext の形式で提供されるターゲット実行コンテキストにアクセスする必要がある場合があります。たとえば、そのようなニーズの 1 つはロギングであるため、Azure コンソールに表示できます。
そのために、FunctionInvoker は ExecutionContext のインスタンスをメッセージヘッダーとして追加し、executionContext キーを介してそれを取得できるようにします。
@Bean
public Function<Message<String>, String> uppercase(JsonMapper mapper) {
return message -> {
String value = message.getPayload();
ExecutionContext context = (ExecutionContext) message.getHeaders().get("executionContext");
. . .
}
}JAR レイアウトに関する注記
Azure の実行時には Spring Cloud Function Web は必要ないため、Azure にデプロイする JAR を作成する前にこれを除外できますが、含めても使用されないため、そのままにしても問題ありません。Azure 上の関数アプリケーションは、Maven プラグインによって生成されたアーカイブです。この関数は、このプロジェクトによって生成された JAR ファイル内に存在します。このサンプルでは、Azure がハンドラークラスを見つけられるように、シンレイアウトを使用して実行可能ファイル jar として作成します。必要に応じて、通常のフラット JAR ファイルを使用することもできます。依存関係は含めないでください。
ビルドファイルの設定
Microsoft Azure で Spring Cloud Function アプリケーションを実行するには、クラウドプラットフォームプロバイダーが提供する Maven プラグインを利用できます。
Maven のアダプタープラグインを使用するには、プラグインの依存関係を pom.xml ファイルに追加します。
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-azure</artifactId>
</dependency>
</dependencies> 次に、プラグインを構成します。resourceGroup、appName、その他のオプションのプロパティを指定して、アプリケーションに Azure 固有の構成を提供し、Azure に必要な function.json ファイルが生成されるように package ゴールの実行を追加する必要があります。完全なプラグインドキュメントは、プラグインリポジトリにあります [GitHub] (英語) 。
<plugin>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-functions-maven-plugin</artifactId>
<configuration>
<resourceGroup>${functionResourceGroup}</resourceGroup>
<appName>${functionAppName}</appName>
</configuration>
<executions>
<execution>
<id>package-functions</id>
<goals>
<goal>package</goal>
</goals>
</execution>
</executions>
</plugin>また、プラグインによってスキャンされるファイルが Azure functions ステージングディレクトリにあることを確認する必要があります (ステージングディレクトリとそのデフォルトの場所の詳細については、プラグインリポジトリ [GitHub] (英語) を参照してください)。
Spring Cloud Function アプリケーションを Maven を使用して Microsoft Azure にデプロイするためのサンプル pom.xml ファイル全体は、ここにあります。
| 現在のところ、Maven プラグインのみが利用可能です。Gradle プラグインは、クラウドプラットフォームプロバイダーによって作成されていません。 |
ビルド
./mvnw -U clean package
サンプルの実行
他の Spring Cloud Function サンプルと同様に、サンプルをローカルで実行できます。
および curl -H "Content-Type: text/plain" localhost:8080/api/uppercase -d '{"value": "hello foobar"}'。
az CLI アプリが必要になります(詳細については、https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-java-maven を参照してください)。Azure ランタイムに関数をデプロイするには:
$ az login $ mvn azure-functions:deploy
別のターミナルでこれを試してください: curl https://<azure-function-url-from-the-log>/api/uppercase (英語) -d '{"value": "hello foobar!"}'。上記の機能に正しい URL を使用していることを確認してください。または、Azure ダッシュボード UI で関数をテストすることもできます(関数名をクリックし、右側に移動してテストをクリックし、右下の実行をクリックします)。
Azure サンプルの関数の入力型は、"value" と呼ばれる単一のプロパティを持つ Foo です。以下のようなものでテストするには、これが必要です。
{
"value": "foobar"
}Azure サンプルアプリは、「機能しない」スタイルで記述されています(@Bean を使用)。機能スタイル(Function または ApplicationContextInitializer のみ)は、Azure での起動時に、従来の @Bean スタイルよりもはるかに高速であるため、@Beans (または @EnableAutoConfiguration)が必要ない場合は、これを選択することをお勧めします。ウォームスタートは影響を受けません。: ブランチ: マスター |
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] (英語) を実行するための手順を参照してください。
サンプル関数
プロジェクトは、参照として次のサンプル関数を提供します。
function-sample-gcp-http [GitHub] (英語) は、ローカルでテストしてデプロイを試すことができる HTTP 関数です。
function-sample-gcp-background [GitHub] (英語) は、指定された Pub/Sub トピックに公開されているメッセージによってトリガーされるバックグラウンド関数の例を示しています。