CtrlK
BlogDocsLog inGet started
Tessl Logo

finkel/spring-grpc

Spring gRPC reference documentation covering server, client, security, and configuration

92

2.19x
Quality

Pending

Does it follow best practices?

Impact

92%

2.19x

Average score across 3 eval scenarios

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

client.mddocs/

gRPC Client

gRPC clients are built from Protobuf-generated stub classes bound to a channel. Spring gRPC provides a GrpcChannelFactory for constructing channels from Spring configuration.

Channel Factory

@Bean
SimpleGrpc.SimpleBlockingStub stub(GrpcChannelFactory channels) {
    return SimpleGrpc.newBlockingStub(channels.createChannel("local"));
}
spring.grpc.client.channels.local.address=0.0.0.0:9090

Default channel configured via spring.grpc.client.default-channel.*.

@ImportGrpcClients

Automatic stub bean creation. Enabled by default on Spring Boot apps.

@ImportGrpcClients(basePackageClasses = MyApplication.class)
@SpringBootApplication
class MyApplication { }

Targeted Import with Customizer

@ImportGrpcClients(target = "stub", prefix = "secure", types = SimpleBlockingStub.class)
@Configuration
class ExtraConfiguration {

    @Bean
    GrpcChannelBuilderCustomizer<?> stubs() {
        return GrpcChannelBuilderCustomizer.matches("stub", builder ->
            builder.intercept(new BearerTokenAuthenticationInterceptor(() -> token(context))));
    }
}

Custom StubFactory beans must expose a static supports(Class<?> type) method.

Manual Client

@Bean
SimpleGrpc.SimpleBlockingStub stub(GrpcChannelFactory channels) {
    return SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:9090"));
}

Shaded Netty Client

Maven:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-grpc-client</artifactId>
  <exclusions>
    <exclusion>
      <groupId>io.grpc</groupId>
      <artifactId>grpc-netty</artifactId>
    </exclusion>
  </exclusions>
</dependency>
<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-netty-shaded</artifactId>
</dependency>

Gradle:

dependencies {
  implementation "org.springframework.boot:spring-boot-starter-grpc-client"
  implementation 'io.grpc:grpc-netty-shaded'
  modules {
    module("io.grpc:grpc-netty") {
      replacedBy("io.grpc:grpc-netty-shaded", "Use Netty shaded")
    }
  }
}

In-Process Channel

Include io.grpc:grpc-inprocess:

spring.grpc.client.inprocess.enabled=true

Target format: in-process:<name>. Disable with:

spring.grpc.client.inprocess.enabled=false

Channel Customization

Global

@Bean
@Order(100)
GrpcChannelBuilderCustomizer<NettyChannelBuilder> flowControlCustomizer() {
    return (name, builder) -> builder.flowControlWindow(1024 * 1024);
}

@Bean
@Order(200)
<T extends ManagedChannelBuilder<T>> GrpcChannelBuilderCustomizer<T> retryCustomizer() {
    return (name, builder) -> builder.enableRetry().maxRetryAttempts(5);
}

Per-Channel

@Bean
SimpleGrpc.SimpleBlockingStub stub(GrpcChannelFactory channelFactory) {
    ChannelBuilderOptions options = ChannelBuilderOptions.defaults()
            .withCustomizer((__, b) -> b.disableRetry());
    ManagedChannel channel = channelFactory.createChannel("localhost", options);
    return SimpleGrpc.newBlockingStub(channel);
}

Local Server Port

Use @LocalGrpcPort to obtain the allocated port in tests:

@Bean
@Lazy
SimpleGrpc.SimpleBlockingStub stub(GrpcChannelFactory channels, @LocalGrpcPort int port) {
    return SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:" + port));
}

Or use the placeholder:

spring.grpc.client.channels.local.address=0.0.0.0:${local.grpc.port}

Client Interceptors

Global

@Bean
@Order(100)
@GlobalClientInterceptor
ClientInterceptor globalLoggingInterceptor() { return new LoggingInterceptor(); }

@Bean
@Order(200)
@GlobalClientInterceptor
ClientInterceptor globalExtraThingsInterceptor() { return new ExtraThingsInterceptor(); }

Filter:

@Bean
ClientInterceptorFilter myInterceptorFilter() {
    return (interceptor, factory) -> !(interceptor instanceof ExtraThingsInterceptor
            && factory instanceof InProcessGrpcChannelFactory);
}

Per-Channel

@Bean
SimpleGrpc.SimpleBlockingStub stub(GrpcChannelFactory channelFactory) {
    ChannelBuilderOptions options = ChannelBuilderOptions.defaults()
            .withInterceptors(List.of(getChannelInterceptor1(), getChannelInterceptor2()));
    ManagedChannel channel = channelFactory.createChannel("localhost", options);
    return SimpleGrpc.newBlockingStub(channel);
}

Blended

Merge global and per-channel interceptors with combined ordering:

ChannelBuilderOptions options = ChannelBuilderOptions.defaults()
    .withInterceptorsMerge("true");

Security

Mutual TLS

spring.grpc.client.channels.my-channel.ssl.bundle=sslclient
spring.grpc.client.channels.my-channel.negotiation-type=TLS
spring.ssl.bundle.jks.sslclient.keystore.location=classpath:client.jks
spring.ssl.bundle.jks.sslclient.keystore.password=secret
spring.ssl.bundle.jks.sslclient.keystore.type=JKS
spring.ssl.bundle.jks.sslclient.key.password=password

HTTP Basic

@Bean
@Lazy
Channel basic(GrpcChannelFactory channels) {
    return channels.createChannel("my-channel", ChannelBuilderOptions.defaults()
        .withInterceptors(List.of(new BasicAuthenticationInterceptor("user", "password"))));
}

OAuth2 Client

@Bean
@Lazy
SimpleGrpc.SimpleBlockingStub oauth(GrpcChannelFactory channels, ClientRegistrationRepository registry) {
    ClientRegistration reg = registry.findByRegistrationId("spring");
    return SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:9090",
        ChannelBuilderOptions.defaults()
            .withInterceptors(List.of(new BearerTokenAuthenticationInterceptor(() -> token(reg))))));
}

private String token(ClientRegistration reg) {
    RestClientClientCredentialsTokenResponseClient creds = new RestClientClientCredentialsTokenResponseClient();
    return creds.getTokenResponse(new OAuth2ClientCredentialsGrantRequest(reg))
        .getAccessToken()
        .getTokenValue();
}

docs

client.md

getting-started.md

index.md

server.md

tile.json