gRPC

Google リモートプロシージャコール(gRPC)は、バイナリメッセージを使用したクライアントサーバー間通信を可能にする高性能 RPC フレームワークです。Spring Boot は、クライアントおよびサーバー両方の gRPC アプリケーションの開発とテストをサポートしています。

gRPC で使用される基本的なメッセージ形式はプロトコルバッファであり、これにより、さまざまなプログラミング言語でメッセージを作成および消費することが可能になります。

サービス定義

gRPC アプリケーションを開発するには、まず Protocol Buffers サービス定義ファイルが必要です。.proto ファイルは、アプリケーションが利用または提供できるサービスとメッセージを定義します。

以下は、プロトコルバッファ言語の proto3 改訂版 (英語) を使用した、典型的な .proto ファイルの例です。

syntax = "proto3";

option java_package = "com.example.grpc.proto";
option java_multiple_files = true;

service HelloWorld {
    rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
    string name = 1;
}

message HelloReply {
    string message = 1;
}

このファイルは、HelloRequest メッセージを受け取り、HelloReply メッセージを返す単一のメソッドを持つ HelloWorld サービスを定義します。HelloRequest メッセージには name 文字列フィールドが含まれます。HelloReply メッセージには message 文字列フィールドが含まれます。

java_package と java_multiple_files のオプションを除けば、.proto ファイルには Java プログラミング言語に特有のものは何も含まれていません。

Java コードの生成

.proto ファイルは言語に依存しないため、使用可能な Java コードに変換するプロセスが必要です。生成されたコードを使用して、実行中のサービスに対してリモートプロシージャ呼び出しを行うか、サービスを独自に実装して他のユーザーが呼び出せるようにすることができます。

コード生成に使用する具体的な手順は、使用するビルドシステムによって異なります。Spring Boot は Maven と Gradle の両方の protobuf プラグインをサポートしていますが、ご自身にとって最適なソリューションを自由に選択できます。

Maven プラグインの使用

Spring Boot には、io.github.ascopes:protobuf-maven-plugin および Maven プラグインの依存関係管理機能が含まれています。spring-boot-starter-parent POM を使用している場合は、すぐに使える適切な設定も提供されます。

以下は、プラグインを使用する典型的な Maven POM ファイルの例です。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
    <parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>4.1.1</version>
	</parent>
	<groupId>com.example</groupId>
	<artifactId>myproject</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<build>
		<plugins>
			<plugin>
				<groupId>io.github.ascopes</groupId>
				<artifactId>protobuf-maven-plugin</artifactId>
			</plugin>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

上記の POM は spring-boot-starter-parent を継承しているため、以下のようになります。

  • protoc バージョンの設定。

  • binary-maven プラグインの設定。

  • generate ゴールの実行構成。

.proto のファイルを src/main/proto に追加する必要があります。

spring-boot-starter-parent を使用しない場合、またはプラグインを直接設定したい場合は、protobuf-maven-plugin のドキュメント (英語) を参照してください。Spring Boot の依存関係管理を使用する場合は、${protobuf-java.version} および ${grpc-java.version} プロパティが役立ちます。

Gradle プラグインの使用

Spring Boot には、com.google.protobuf:protobuf-gradle-plugin Gradle プラグインの依存関係管理機能が含まれています。さらに、spring-boot-gradle-plugin は protobuf プラグインの存在を検知し、適切に設定を行います。

以下に、プラグインを使用した典型的な Gradle ファイルを示します。

plugins {
	id 'java'
	id 'org.springframework.boot' version '4.1.1'
	id 'io.spring.dependency-management' version '1.1.7'
	id 'com.google.protobuf' version '0.9.6'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
	toolchain {
		languageVersion = JavaLanguageVersion.of(17)
	}
}

repositories {
	mavenCentral()
}

protobuf {
	plugins {
		grpc {}
	}
}

org.springframework.boot は com.google.protobuf プラグインに反応し、protoc とそのバージョンを設定します。さらに、protobuf 拡張機能が grpc プラグインを使用するように設定されているため、org.springframework.boot プラグインも protoc-gen-grpc-java とそのバージョンを設定します。

.proto のファイルを src/main/proto に追加する必要があります。

org.springframework.boot プラグインを使用しない場合、またはプラグインを直接設定したい場合は、protobuf-gradle-plugin のドキュメント [GitHub] (英語) を参照してください。

gRPC サーバーアプリケーションの作成

Spring Boot は、サーバーアプリケーションで使用できる spring-boot-grpc-server モジュールと spring-boot-starter-grpc-server スターター POM を提供します。

実際のサーバーコードを記述するには、.proto ファイルから生成された基本クラスを 1 つ以上拡張し、Spring Bean として公開する必要があります。Spring gRPC は、BindableService (英語) を実装する Bean を自動的に gRPC サーバーとして公開します。.proto で生成されたクラスはすべて BindableService (英語) を実装しているため、Bean として追加するだけで、gRPC 経由で公開できます。

詳細については、Spring gRPC のドキュメントを参照してください。

次の例は、上記の .proto ファイルに含まれる HelloWorld サービスをどのように実装できるかを示しています。この例では、@GrpcService (Javadoc) アノテーションを使用し、コードがコンポーネントスキャンによって検出されるパッケージ内にあることを前提としています。

  • Java

  • Kotlin

import io.grpc.stub.StreamObserver;

import org.springframework.grpc.server.service.GrpcService;

@GrpcService
public class MyHelloWorldService extends HelloWorldGrpc.HelloWorldImplBase {

	@Override
	public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
		String message = "Hello '%s'".formatted(request.getName());
		HelloReply reply = HelloReply.newBuilder().setMessage(message).build();
		responseObserver.onNext(reply);
		responseObserver.onCompleted();
	}

}
import io.grpc.stub.StreamObserver
import org.springframework.grpc.server.service.GrpcService

@GrpcService
class MyHelloWorldService : HelloWorldGrpc.HelloWorldImplBase() {

	override fun sayHello(request: HelloRequest,  responseObserver: StreamObserver<HelloReply>) {
		val message = "Hello '${request.getName()}'"
		val reply = HelloReply.newBuilder().setMessage(message).build()
		responseObserver.onNext(reply)
		responseObserver.onCompleted()
	}

}

アプリケーションが spring-boot-starter-grpc-server を使用する場合、Netty がポート 9090 でリッスンするサーバー実装として使用されます。

grpcurl [GitHub] (英語) を使用してアプリケーションをテストできます。

$ grpcurl -d '{"name":"Spring"}' -plaintext localhost:9090 HelloWorld.SayHello
{
  "message": "Hello 'Spring'"
}

Netty シェードサーバーへの切り替え

spring-boot-starter-grpc-server スターター POM に含まれる Netty のバージョンが、使用している他のライブラリと互換性がないことが判明した場合は、「シェーディング」バージョンに切り替えることができます。

切り替えるには、io.grpc:grpc-netty を除外して io.grpc:grpc-netty-shaded を含めることができます。例:

  • Maven

  • Gradle

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-grpc-server</artifactId>
	<exclusions>
		<!-- Exclude the gRPC Netty dependency -->
		<exclusion>
			<groupId>io.grpc</groupId>
			<artifactId>grpc-netty</artifactId>
		</exclusion>
	</exclusions>
</dependency>
<!-- Use gRPC Netty Shaded instead -->
<dependency>
	<groupId>io.grpc</groupId>
	<artifactId>grpc-netty-shaded</artifactId>
</dependency>
dependencies {
	implementation('org.springframework.boot:spring-boot-starter-grpc-server') {
		// Exclude the gRPC Netty dependency
		exclude group: 'io.grpc', module: 'grpc-netty'
	}
	// Use gRPC Netty Shaded instead
	implementation "io.grpc:grpc-netty-shaded"
}

サーブレットコンテナーへの切り替え

Netty ではなく、Tomcat などの通常のサーブレットコンテナーを使用して gRPC サービスを公開することも可能です。そのためには、サーブレットコンテナーが HTTP/2 をサポートするように設定する必要があります。

サーブレット gRPC 実装に切り替えるには、io.grpc:grpc-netty を除外し、io.grpc:grpc-servlet-jakarta を含めることができます。例:

  • Maven

  • Gradle

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-grpc-server</artifactId>
	<exclusions>
		<!-- Exclude the gRPC Netty dependency -->
		<exclusion>
			<groupId>io.grpc</groupId>
			<artifactId>grpc-netty</artifactId>
		</exclusion>
	</exclusions>
</dependency>
<!-- Use gRPC Servlet Jakarta instead -->
<dependency>
	<groupId>io.grpc</groupId>
	<artifactId>grpc-servlet-jakarta</artifactId>
</dependency>
dependencies {
	implementation('org.springframework.boot:spring-boot-starter-webmvc') {
	implementation('org.springframework.boot:spring-boot-starter-grpc-server') {
		// Exclude the gRPC Netty dependency
		exclude group: 'io.grpc', module: 'grpc-netty'
	}
	// Use gRPC Servlet Jakarta instead
	implementation "io.grpc:grpc-servlet-jakarta"
}
サーブレットコンテナーの依存関係(たとえば spring-boot-starter-tomcat)を含め、server.http2.enabled を true に設定することを忘れないでください。
サーブレットコンテナーを使用する場合、特定の gRPC サーバー構成プロパティは関係なく無視されます。例: server.port は Web サーバーポートを設定するために使用されるため、spring.grpc.server.port は無視されます。

SSL サポート

SSL は、SSL バンドルを使用して grpc-netty サーバーと grpc-netty-shaded サーバーの両方で設定できます。SSL バンドルの宣言方法の詳細については、SSL コアドキュメントを参照してください。

バンドルが定義されたら、gRPC サーバーアプリケーションで以下のプロパティを使用してバンドルを使用できます。

  • プロパティ

  • YAML

spring.grpc.server.ssl.bundle=mysslbundle
spring:
  grpc:
    server:
      ssl:
        bundle: mysslbundle

クライアント認証は、spring.grpc.server.ssl.client-auth を optional または require に設定することによっても構成できます。

サーバーの SSL サポートを一時的に無効にするには (たとえばテストを支援するため)、spring.grpc.server.ssl.enabled を false に設定します。

インプロセスサーバーの使用

クラスパスに io.grpc:grpc-inprocess の依存関係を追加し、spring.grpc.server.inprocess.name プロパティを定義することで、インプロセスサーバーを実行できます。このモードでは、通常のサーバーファクトリに加えて、インプロセスサーバーファクトリも自動的に構成されます。

ご提供いただいた名前は、in-process:<name> の形式でクライアントチャネルのターゲットとして使用できます。

リフレクション

Spring Boot は、利用可能になると gRPC リフレクションサービス (英語) を自動的に構成します。これにより、クライアントはサービスのメタデータを参照し、.proto ファイルをダウンロードできるようになります。

リフレクションサービスは、オプションの依存関係である io.grpc:grpc-services ライブラリに含まれています。自動構成を適用するには、プロジェクトにこの依存関係を追加する必要があります。

io.grpc:grpc-services ライブラリをお持ちで、リフレクションを自動構成したくない場合は、spring.grpc.server.reflection.enabled を false に設定できます。

サーバーの状態

gRPC サーバーは、標準サービス API(ヘルス /v1 [GitHub] (英語) )を使用して健全性情報を提供できます。これにより、クライアントはサーバーサービスの健全性を確認し、トラフィックを適切にルーティングできます。

Spring Boot は、独自の spring-boot-health モジュールと標準の gRPC ヘルスサービスとの間の橋渡し役を果たします。io.grpc:grpc-services モジュールと org.springframework.boot:spring-boot-health モジュールがクラスパス上に存在する場合、ヘルス情報が提供されます。

ヘルス指標を公開したくない場合は、spring.grpc.server.health.enabled を false に設定できます。

サービス別ヘルスマッピング

デフォルトでは、利用可能なすべてのヘルスインジケーターを使用して、サーバー全体のステータス("")に関するヘルス情報が提供されます。

また、特定のサービスに対して、ヘルス指標のサブセットのみを含めることで、より詳細なヘルス情報を提供することも可能です。サービスごとに、カスタムマッピングおよび順序付けルールを定義することもできます。

例: 以下の設定では、db および redis インジケーターのみを使用して "myservice" の健全性を提供します。

  • プロパティ

  • YAML

spring.grpc.server.health.service.myservice.include[0]=db
spring.grpc.server.health.service.myservice.include[1]=redis
spring:
  grpc:
    server:
      health:
        service:
          myservice:
            include:
            - db
            - redis
サービス固有の状態情報のみを提供したい場合は、spring.grpc.server.health.include-overall-health を false に設定することで、サーバー全体の状態情報表示を無効にできます。

プッシュ構成

Web ベースのヘルスチェックとは異なり、gRPC のヘルス情報はプルではなくプッシュで定期的に送信されます。デフォルトでは、最初のヘルス情報のプッシュはアプリケーション起動後 5 秒後に行われ、その後は 5 秒ごとに送信されます。

これを微調整するには、以下のプロパティを使用できます。

  • プロパティ

  • YAML

spring.grpc.server.health.schedule.period=5m
spring.grpc.server.health.schedule.delay=2s
spring:
  grpc:
    server:
      health:
        schedule:
          period: 5m
          delay: 2s
ヘルス状態の更新情報を別の方法で送信したい場合は、spring.grpc.server.health.schedule.enabled を false に設定することもできます。

gRPC サーバーアプリケーションのセキュリティ保護

Netty ベースのサーバー

Spring gRPC には、Spring Security を使用して Netty ベースのサーバーアプリケーションを宣言的に保護できる機能が含まれています。これは、通常の Web アプリケーションを保護する際に使用するパターンと同様のものです。

Spring Boot は、GrpcSecurity (Javadoc) および SecurityGrpcExceptionHandler (Javadoc) の両方の Bean に対して自動構成機能を提供します。通常、gRPC アプリケーションは、gRPC サービス Bean に @PreAuthorize (Javadoc) アノテーション、または AuthenticationProcessInterceptor (Javadoc) Bean を使用して保護されます。

詳細については、Spring gRPC のドキュメントを参照してください。

サーブレットコンテナーベースのサーバー

gRPC サーバーが標準のサーブレットコンテナー内で実行されている場合は、一般的な Web セキュリティ設定を使用してアプリケーションを保護できます。Spring Boot は、GrpcServerExecutorProvider (Javadoc) および SecurityContextServerInterceptor (Javadoc) の Bean を自動的に構成し、Spring Security が正しく動作するようにします。

クロスサイトリクエストフォージェリ(CSRF)対策は gRPC プロトコルと互換性がないため、すべての gRPC リクエストでデフォルトで無効になっています。独自の CSRF 対策を設定したい場合は、spring.grpc.server.security.csrf.enabled を false に設定することで無効にできます。

手動でのセキュリティ設定を支援するために、Spring Boot は gRPC サービス用のリクエストマッチャーを提供します。マッチはサーブレットスタックとリアクティブスタックの両方で使用できます。例: 以下は "special" を除くすべての gRPC サービスを含みます。

  • Java

  • Kotlin

import org.springframework.boot.grpc.server.autoconfigure.security.web.servlet.GrpcRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;

@Configuration(proxyBeanMethods = false)
public class MySecurityConfiguration {

	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) {
		http.securityMatcher(GrpcRequest.toAnyService().excluding("special"));
		http.authorizeHttpRequests((requests) -> requests.anyRequest().hasRole("GRPC_ADMIN"));
		http.httpBasic(withDefaults());
		return http.build();
	}

}
import org.springframework.boot.grpc.server.autoconfigure.security.web.servlet.GrpcRequest
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.Customizer.withDefaults
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.web.SecurityFilterChain

@Configuration(proxyBeanMethods = false)
class MySecurityConfiguration {

	@Bean
	fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
		http.securityMatcher(GrpcRequest.toAnyService().excluding("special"))
		http.authorizeHttpRequests { requests -> requests.anyRequest().hasRole("GRPC_ADMIN") }
		http.httpBasic(withDefaults())
		return http.build()
	}

}
リアクティブ型の試合には org.springframework.boot.grpc.server.autoconfigure.security.web.servlet.GrpcRequest (Javadoc) を使用してください。

OAuth2 リソースサーバー

OAuth2 (英語) は広く利用されている認証フレームワークです。Spring Boot の OAuth2 リソースサーバーのサポートは gRPC と互換性があり、通常の方法で設定できます。

gRPC サーバーアプリケーションで使用する OAuth2 リソースサーバーの設定方法の詳細については、「セキュリティ」の "OAuth2" セクションを参照してください。

gRPC クライアントアプリケーションの作成

Spring Boot は、クライアントアプリケーションで使用できる spring-boot-grpc-client モジュールと spring-boot-starter-grpc-client スターター POM を提供します。

クライアントは、.proto ファイルから生成された「スタブ」クラスを 1 つ以上インポートすることで、リモート gRPC サービスを呼び出すことができます。使用するスタブクラスをインポートするには、@ImportGrpcClients (Javadoc) アノテーションを使用できます。

各インポートには、論理チャネル名またはリモートサーバーのベース URL のいずれかである target が含まれます。通常、ターゲットをハードコードするよりもチャネル名を使用することをお勧めします。

典型的な例を挙げましょう。

  • Java

  • Kotlin

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.grpc.client.ImportGrpcClients;

@SpringBootApplication(proxyBeanMethods = false)
@ImportGrpcClients(target = "hello", types = HelloWorldGrpc.HelloWorldBlockingStub.class)
public class MyApplication {

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

}
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.docs.features.springapplication.MyApplication
import org.springframework.boot.runApplication
import org.springframework.grpc.client.ImportGrpcClients

@SpringBootApplication(proxyBeanMethods = false)
@ImportGrpcClients(target = "hello", types = [HelloWorldGrpc.HelloWorldBlockingStub::class])
class MyApplication

fun main(args: Array<String>) {
	runApplication<MyApplication>(*args)
}
target を指定しない場合は、「デフォルト」が使用されます。
@ImportGrpcClients (Javadoc) の basePackageClasses または basePackages 属性を使用すると、指定されたパッケージ内のすべてのスタブをインポートできます。

チャネルプロパティ

@ImportGrpcClients (Javadoc) の target 属性で論理チャネル名を使用する場合、Spring Boot が実際に呼び出すべき gRPC サーバーを見つけることができるように、いくつかのプロパティを指定する必要があります。

そのためには、spring.grpc.channel.<name>.* のプロパティを使用してエントリを追加します。通常は、実際の target と、チャネル固有のその他の設定を構成します。

例: 以下の設定により、myservice は static://grpc.example.com:9090 の実際のターゲットを使用するように構成されます。また、キープアライブタイムアウトと許可される最大メッセージサイズも変更されます。

  • プロパティ

  • YAML

spring.grpc.client.channel.myservice.target=static://grpc.example.com:9090
spring.grpc.client.channel.myservice.inbound.keepalive.timeout=40s
spring.grpc.client.channel.myservice.inbound.message.max-size=8MB
spring:
  grpc:
    client:
      channel:
        myservice:
          target: static://grpc.example.com:9090
          inbound:
            keepalive:
              timeout: 40s
            message:
              max-size: 8MB

茎の短い Bean を使う

@ImportGrpcClients (Javadoc) アノテーションが適切に配置され、プロパティが記述されていれば、他の Bean と同様にスタブを使用できます。

例: HelloWorldStub が ApplicationRunner Bean に注入される例です。

  • Java

  • Kotlin

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.docs.io.grpc.client.stubbeans.HelloWorldGrpc.HelloWorldBlockingStub;
import org.springframework.stereotype.Component;

@Component
class MyApplicationRunner implements ApplicationRunner {

	private final HelloWorldBlockingStub helloStub;

	MyApplicationRunner(HelloWorldGrpc.HelloWorldBlockingStub helloStub) {
		this.helloStub = helloStub;
	}

	@Override
	public void run(ApplicationArguments args) throws Exception {
		HelloRequest request = HelloRequest.newBuilder().setName("Spring").build();
		HelloReply reply = this.helloStub.sayHello(request);
		System.out.println(reply.getMessage());
	}

}
import org.springframework.boot.ApplicationArguments
import org.springframework.boot.ApplicationRunner


class MyApplicationRunner(val helloStub: HelloWorldGrpc.HelloWorldBlockingStub) : ApplicationRunner {

	override fun run(args: ApplicationArguments) {
		val request = HelloRequest.newBuilder().setName("Spring").build()
		val reply: HelloReply = helloStub.sayHello(request)
		println(reply.getMessage())
	}

}

Netty シェードクライアントトランスポートへの切り替え

内部的には、リモートの gRPC ネットワーク呼び出しは Netty を使用して行われます。spring-boot-starter-grpc-client スターター POM に含まれる Netty のバージョンが、使用している他のライブラリと互換性がない場合は、「シェーディング」バージョンに切り替えることができます。

切り替えるには、io.grpc:grpc-netty を除外して io.grpc:grpc-netty-shaded を含めることができます。例:

  • Maven

  • Gradle

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-grpc-client</artifactId>
	<exclusions>
		<!-- Exclude the gRPC Netty dependency -->
		<exclusion>
			<groupId>io.grpc</groupId>
			<artifactId>grpc-netty</artifactId>
		</exclusion>
	</exclusions>
</dependency>
<!-- Use gRPC Netty Shaded instead -->
<dependency>
	<groupId>io.grpc</groupId>
	<artifactId>grpc-netty-shaded</artifactId>
</dependency>
dependencies {
	implementation('org.springframework.boot:spring-boot-starter-grpc-client') {
		// Exclude the gRPC Netty dependency
		exclude group: 'io.grpc', module: 'grpc-netty'
	}
	// Use gRPC Netty Shaded instead
	implementation "io.grpc:grpc-netty-shaded"
}

SSL サポート

クライアントの gRPC アプリケーションは、SSL/TLS 暗号化接続を使用して gRPC サービスに接続できます。gRPC 接続は、標準の一方向 TLS または相互 TLS を使用するように構成できます。

標準的な一方向 TLS

標準の一方向 TLS を使用するには、チャネルプロパティで ssl.enabled プロパティを true に設定します。例: 以下の設定で、myservice チャネルの SSL/TLS 接続が有効になります。

  • プロパティ

  • YAML

spring.grpc.client.channel.myservice.target=static://grpc.example.com:9090
spring.grpc.client.channel.myservice.ssl.enabled=true
spring:
  grpc:
    client:
      channel:
        myservice:
          target: static://grpc.example.com:9090
          ssl:
            enabled: true

相互 TLS

相互 TLS(mTLS)は、クライアントとサーバーの両方が互いに証明書を提示する必要があるセキュリティプロトコルです。相互 TLS を使用するには、チャネルプロパティの ssl.bundle プロパティを設定します。SSL バンドルの宣言方法の詳細については、SSL コア機能に関するドキュメントを参照してください。

以下は、myservice チャネルが相互 TLS のために mybundle バンドルを使用するように構成する例です。

  • プロパティ

  • YAML

spring.grpc.client.channel.myservice.target=static://grpc.example.com:9090
spring.grpc.client.channel.myservice.ssl.bundle=mybundle
spring:
  grpc:
    client:
      channel:
        myservice:
          target: static://grpc.example.com:9090
          ssl:
            bundle: mybundle

クライアントの SSL サポートを一時的に無効にするには(たとえばテストのため)、チャネル設定で bypass-certificate-validation を true に変更します。

  • プロパティ

  • YAML

spring.grpc.client.channel.myservice.bypass-certificate-validation=true
spring:
  grpc:
    client:
      channel:
        myservice:
          bypass-certificate-validation: true

インプロセスチャネルの使用

io.grpc.grpc-inprocess の依存関係をクラスパスに含めることで、プロセス内サーバー(つまり、ネットワークポートで待機していないサーバー)と通信できます。

このモードでは、通常のチャネルファクトリ(例: Netty)に加えて、プロセス内チャネルファクトリが自動的に構成されます。ユーザーが複数のチャネルファクトリを扱う必要がないように、複合チャネルファクトリがプライマリチャネルファクトリ Bean として構成されます。この複合ファクトリは、構成対象のファクトリを参照して、チャネルターゲットをサポートする最初のファクトリを検出します。

インプロセスサーバーを使用するには、チャネルターゲットを in-process:<name> に設定する必要があります。

インプロセスチャネルファクトリを無効にするには、spring.grpc.client.inprocess.enabled プロパティを false に設定します。

可観測性

Spring Boot は、Micrometer が利用可能な場合に ObservationGrpcClientInterceptor (英語) の自動構成を提供します。このインターセプターは、gRPC クライアントアプリケーションの可観測性を提供します。

Micrometer を使用しているが、gRPC には使用したくない場合は、spring.grpc.client.observation.enabled を false に設定できます。

チャンネルのカスタマイズ

基本的なプロパティを超えて gRPC チャネルをカスタマイズする必要がある場合は、GrpcChannelBuilderCustomizer (Javadoc) を使用できます。各カスタマイザーは、論理ターゲット名とチャネルを構築する ManagedChannelBuilder (英語) を指定して呼び出されます。また、指定された正規表現パターンに一致するターゲットにカスタマイズを限定する便利な matching(String pattern) ファクトリメソッドも用意されています。

カスタマイザーの一般的な使用例として、セキュリティインターセプターをビルダーに追加することが挙げられます。例: ここでは、"hello" に一致するターゲットに BearerTokenAuthenticationInterceptor (Javadoc) を追加しています。

  • Java

  • Kotlin

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.grpc.client.GrpcChannelBuilderCustomizer;
import org.springframework.grpc.client.interceptor.security.BasicAuthenticationInterceptor;

@Configuration(proxyBeanMethods = false)
public class MyGrpcConfiguration {

	@Bean
	GrpcChannelBuilderCustomizer<?> helloChannelCustomizer() {
		return GrpcChannelBuilderCustomizer.matching("hello",
				(builder) -> builder.intercept(new BasicAuthenticationInterceptor("user", "password")));
	}

}
import io.grpc.ManagedChannelBuilder
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.grpc.client.GrpcChannelBuilderCustomizer
import org.springframework.grpc.client.interceptor.security.BasicAuthenticationInterceptor
import java.util.function.Consumer

@Configuration(proxyBeanMethods = false)
class MyGrpcConfiguration {

	@Bean
	fun helloChannelCustomizer(): GrpcChannelBuilderCustomizer<*> {
		return GrpcChannelBuilderCustomizer.matching("hello", { builder ->
			builder.intercept(BasicAuthenticationInterceptor("user", "password"))
		})
	}

}

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

gRPC クライアントおよびサーバーアプリケーションのテストを支援するために、spring-boot-grpc-test モジュールまたは spring-boot-starter-grpc-client-test / spring-boot-starter-grpc-server-test スターター POM を使用できます。

インプロセステストトランスポートの使用

@AutoConfigureTestGrpcTransport (Javadoc) アノテーションを使用すると、gRPC 通信チャネルを、テスト専用に設計されたプロセス内チャネルにすばやく置き換えることができます。通常のプロセス内チャネルとは異なり、これらのテストチャネルは設定を必要としません。

テスト用の gRPC トランスポートを使用すると、アプリケーションを起動するためにネットワークポートを実際にリッスンする必要がなくなります。これにより、アプリケーションが期待どおりに動作することを保証しながら、テストを迅速に実行できます。

デフォルトでは、@AutoConfigureTestGrpcTransport (Javadoc) を使用すると次のようになります。

次の例は、@AutoConfigureTestGrpcTransport (Javadoc) を使用して gRPC サーバーアプリケーションをテストする方法を示しています。

  • Java

  • Kotlin

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.grpc.test.autoconfigure.AutoConfigureTestGrpcTransport;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.grpc.client.ImportGrpcClients;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
@AutoConfigureTestGrpcTransport
@ImportGrpcClients(types = HelloWorldGrpc.HelloWorldBlockingStub.class)
class MyGrpcTests {

	@Autowired
	private HelloWorldGrpc.HelloWorldBlockingStub helloStub;

	@Test
	void sayHello() {
		HelloRequest request = HelloRequest.newBuilder().setName("Spring").build();
		HelloReply reply = this.helloStub.sayHello(request);
		assertThat(reply.getMessage()).isEqualTo("Hello 'Spring'");
	}

}
import org.assertj.core.api.Assertions.assertThat
import org.jooq.DSLContext
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.grpc.test.autoconfigure.AutoConfigureTestGrpcTransport
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.grpc.client.ImportGrpcClients

@SpringBootTest
@AutoConfigureTestGrpcTransport
@ImportGrpcClients(types = [HelloWorldGrpc.HelloWorldBlockingStub::class])
class MyGrpcTests(@Autowired val helloStub: HelloWorldGrpc.HelloWorldBlockingStub) {

	@Test
	fun sayHello() {
		val request = HelloRequest.newBuilder().setName("Spring").build()
		val reply = helloStub.sayHello(request)
		assertThat(reply.getMessage()).isEqualTo("Hello 'Spring'")
	}

}

実行中のサーバーでのテスト

gRPC アプリケーションをテストする際に、実際のサーバーを起動し、実際のネットワーク接続を使用する場合は、ランダムなポートを使用することをお勧めします。これにより、あらゆる環境でテストを実行でき、誤って実際のサービスを呼び出すことを防ぐことができます。

ランダムなポートを使用して gRPC サーバーを起動するには、spring.grpc.server.port を 0 に設定します。サーバーが実際に起動したポート番号を取得するには、@LocalGrpcServerPort (Javadoc) アノテーションを使用できます。

以下にテスト例を示します。

  • Java

  • Kotlin

import io.grpc.ManagedChannel;
import io.grpc.netty.NettyChannelBuilder;
import org.junit.jupiter.api.Test;

import org.springframework.boot.docs.io.grpc.testing.localserverport.HelloWorldGrpc.HelloWorldBlockingStub;
import org.springframework.boot.grpc.test.autoconfigure.LocalGrpcServerPort;
import org.springframework.boot.test.context.SpringBootTest;

import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(properties = "spring.grpc.server.port=0")
class MyGrpcIntegrationTests {

	@LocalGrpcServerPort
	private int port;

	@Test
	void sayHello() {
		String target = "localhost:%s".formatted(this.port);
		ManagedChannel channel = NettyChannelBuilder.forTarget(target).usePlaintext().build();
		try {
			HelloWorldBlockingStub hello = HelloWorldGrpc.newBlockingStub(channel);
			HelloRequest request = HelloRequest.newBuilder().setName("Spring").build();
			assertThat(hello.sayHello(request).getMessage()).isEqualTo("Hello 'Spring'");
		}
		finally {
			channel.shutdown();
		}
	}

}
import io.grpc.ManagedChannel
import io.grpc.netty.NettyChannelBuilder
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.boot.grpc.test.autoconfigure.LocalGrpcServerPort
import org.springframework.boot.test.context.SpringBootTest

@SpringBootTest(properties = ["spring.grpc.server.port=0"])
class MyGrpcIntegrationTests {

	@LocalGrpcServerPort
	var port = 0

	@Test
	fun sayHello() {
		val target = "localhost:${port}"
		val channel: ManagedChannel = NettyChannelBuilder.forTarget(target).usePlaintext().build()
		try {
			val hello: HelloWorldGrpc.HelloWorldBlockingStub = HelloWorldGrpc.newBlockingStub(channel)
			val request = HelloRequest.newBuilder().setName("Spring").build()
			assertThat(hello.sayHello(request).getMessage()).isEqualTo("Hello 'Spring'")
		} finally {
			channel.shutdown()
		}
	}
}