Spring gRPC reference documentation covering server, client, security, and configuration
92
Pending
Does it follow best practices?
Impact
92%
2.19xAverage score across 3 eval scenarios
Pending
The risk profile of this skill
gRPC clients are built from Protobuf-generated stub classes bound to a channel. Spring gRPC provides a GrpcChannelFactory for constructing channels from Spring configuration.
@Bean
SimpleGrpc.SimpleBlockingStub stub(GrpcChannelFactory channels) {
return SimpleGrpc.newBlockingStub(channels.createChannel("local"));
}spring.grpc.client.channels.local.address=0.0.0.0:9090Default channel configured via spring.grpc.client.default-channel.*.
@ImportGrpcClientsAutomatic stub bean creation. Enabled by default on Spring Boot apps.
@ImportGrpcClients(basePackageClasses = MyApplication.class)
@SpringBootApplication
class MyApplication { }@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.
@Bean
SimpleGrpc.SimpleBlockingStub stub(GrpcChannelFactory channels) {
return SimpleGrpc.newBlockingStub(channels.createChannel("0.0.0.0:9090"));
}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")
}
}
}Include io.grpc:grpc-inprocess:
spring.grpc.client.inprocess.enabled=trueTarget format: in-process:<name>. Disable with:
spring.grpc.client.inprocess.enabled=false@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);
}@Bean
SimpleGrpc.SimpleBlockingStub stub(GrpcChannelFactory channelFactory) {
ChannelBuilderOptions options = ChannelBuilderOptions.defaults()
.withCustomizer((__, b) -> b.disableRetry());
ManagedChannel channel = channelFactory.createChannel("localhost", options);
return SimpleGrpc.newBlockingStub(channel);
}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}@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);
}@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);
}Merge global and per-channel interceptors with combined ordering:
ChannelBuilderOptions options = ChannelBuilderOptions.defaults()
.withInterceptorsMerge("true");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@Bean
@Lazy
Channel basic(GrpcChannelFactory channels) {
return channels.createChannel("my-channel", ChannelBuilderOptions.defaults()
.withInterceptors(List.of(new BasicAuthenticationInterceptor("user", "password"))));
}@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();
}evals