Skip to content

Commit

Permalink
Merge branch 'master' into fix-GrpcClientBeanPostProcessor
Browse files Browse the repository at this point in the history
  • Loading branch information
yidongnan authored Oct 27, 2024
2 parents 722604a + b83afe6 commit ade0a79
Show file tree
Hide file tree
Showing 22 changed files with 772 additions and 21 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
java: ['17']
java: ['17', '21']
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
java: ['17']
java: ['17', '21']
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down
10 changes: 5 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ buildscript {
}
}
ext {
projectVersion = '3.1.0.RELEASE'
projectVersion = '3.2.0-SNAPSHOT'

// https://github.com/grpc/grpc-java/releases
grpcVersion = '1.63.0'

// https://github.com/google/guava/releases
guavaVersion = '33.1.0-jre'
guavaVersion = '33.2.1-jre'
// https://github.com/protocolbuffers/protobuf/releases
protobufVersion = '3.25.3'
protobufGradlePluginVersion = '0.9.4'

// https://github.com/spring-projects/spring-boot/releases
springBootVersion = '3.2.5'
// https://github.com/spring-cloud/spring-cloud-release/releases
springCloudVersion = '2023.0.0'
springCloudVersion = '2023.0.1'
// https://github.com/alibaba/spring-cloud-alibaba/releases
springCloudAlibabaNacosVersion = '2022.0.0.0'
springCloudAlibabaNacosVersion = '2023.0.1.0'

lombokPluginVersion = '8.6'
versioningPluginVersion = '3.1.0'
Expand All @@ -39,7 +39,7 @@ plugins {
id 'java'
id 'java-library'
id 'org.springframework.boot' version "${springBootVersion}" apply false
id 'io.spring.dependency-management' version '1.1.4'
id 'io.spring.dependency-management' version '1.1.6'
id 'net.nemerosa.versioning' version "${versioningPluginVersion}"
id 'com.google.protobuf' version "${protobufGradlePluginVersion}"
id 'io.freefair.lombok' version "${lombokPluginVersion}" apply false
Expand Down
8 changes: 4 additions & 4 deletions deploy.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ publishing {
pom {
name = 'gRPC Spring Boot Starter'
description = 'gRPC Spring Boot Starter'
url = 'https://github.com/yidongnan/grpc-spring-boot-starter'
url = 'https://github.com/grpc-ecosystem/grpc-spring'
licenses {
license {
name = 'Apache 2.0'
Expand All @@ -50,9 +50,9 @@ publishing {
}
}
scm {
connection = 'scm:git:git://github.com/yidongnan/grpc-spring-boot-starter.git'
developerConnection = 'scm:git:[email protected]:yidongnan/grpc-spring-boot-starter.git'
url = 'https://github.com/yidongnan/grpc-spring-boot-starter'
connection = 'scm:git:git://github.com/grpc-ecosystem/grpc-spring.git'
developerConnection = 'scm:git:[email protected]/grpc-ecosystem/grpc-spring.git'
url = 'https://github.com/grpc-ecosystem/grpc-spring'
}
}

Expand Down
26 changes: 25 additions & 1 deletion docs/en/actuator.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This page focuses on the integration with
This is an optional feature. Supported features:

- Client + server metrics
- Server `InfoContributor`
- Server `InfoContributor` and `GRPC Health API`

## Table of Contents <!-- omit in toc -->

Expand All @@ -18,6 +18,7 @@ This is an optional feature. Supported features:
- [Viewing the metrics](#viewing-the-metrics)
- [Metric configuration](#metric-configuration)
- [InfoContributor](#infocontributor)
- [GRPC Health](#grpc-health)
- [Opt-Out](#opt-out)

## Dependencies
Expand Down Expand Up @@ -178,6 +179,29 @@ You can view the grpc info along with your other info at `/actuator/info` (requi
You can turn of the service listing (for both actuator and grpc) using `grpc.server.reflectionServiceEnabled=false`.
## GRPC Health
By default, the health endpoint will use the standard gRPC implementation for health, which does not integrate with Spring Boot Actuator.
The server provides an optional integration with Actuator health information using the [gRPC Health API](https://grpc.io/docs/guides/health-checking/).
This integration enables the server to respond to gRPC health checks based on the `HealthEndpoint` from Actuator, which is the same used for the web version.
To enable this integration, add the following properties to your application configuration:
````properties
grpc.server.health-service.type=ACTUATOR
````
The integration allows you to check the health status for the whole service or specific health indicators, where the `service` is the key of the [`HealthIndicator`](https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.health.auto-configured-health-indicators).
`Watch` is not yet supported because actuator is pull-based and does not automatically tries to determine the status of the service to notify clients.

To prevent any health service from being served by the GRPC server, you can set the type to `NONE`:

````properties
grpc.server.health-service.type=NONE
````

## Opt-Out

You can opt out from the actuator autoconfiguration using the following annotation:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,13 @@ List<GrpcChannelConfigurer> defaultChannelConfigurers() {
return Collections.emptyList();
}

// First try the shaded netty channel factory
// First try the shaded netty channel factory and in process factory
@ConditionalOnMissingBean(GrpcChannelFactory.class)
@ConditionalOnClass(name = {"io.grpc.netty.shaded.io.netty.channel.Channel",
"io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder"})
"io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder", "io.grpc.inprocess.InProcessChannelBuilder"})
@Bean
@Lazy
GrpcChannelFactory shadedNettyGrpcChannelFactory(
GrpcChannelFactory inProcessOrShadedNettyGrpcChannelFactory(
final GrpcChannelsProperties properties,
final GlobalClientInterceptorRegistry globalClientInterceptorRegistry,
final List<GrpcChannelConfigurer> channelConfigurers) {
Expand All @@ -165,12 +165,13 @@ GrpcChannelFactory shadedNettyGrpcChannelFactory(
return new InProcessOrAlternativeChannelFactory(properties, inProcessChannelFactory, channelFactory);
}

// Then try the normal netty channel factory
// Then try the normal netty channel factory and in process factory
@ConditionalOnMissingBean(GrpcChannelFactory.class)
@ConditionalOnClass(name = {"io.netty.channel.Channel", "io.grpc.netty.NettyChannelBuilder"})
@ConditionalOnClass(name = {"io.netty.channel.Channel", "io.grpc.netty.NettyChannelBuilder",
"io.grpc.inprocess.InProcessChannelBuilder"})
@Bean
@Lazy
GrpcChannelFactory nettyGrpcChannelFactory(
GrpcChannelFactory inProcessOrNettyGrpcChannelFactory(
final GrpcChannelsProperties properties,
final GlobalClientInterceptorRegistry globalClientInterceptorRegistry,
final List<GrpcChannelConfigurer> channelConfigurers) {
Expand All @@ -183,8 +184,38 @@ GrpcChannelFactory nettyGrpcChannelFactory(
return new InProcessOrAlternativeChannelFactory(properties, inProcessChannelFactory, channelFactory);
}

// Then try the shaded netty channel factory
@ConditionalOnMissingBean(GrpcChannelFactory.class)
@ConditionalOnClass(name = {"io.grpc.netty.shaded.io.netty.channel.Channel",
"io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder"})
@Bean
@Lazy
GrpcChannelFactory shadedNettyGrpcChannelFactory(
final GrpcChannelsProperties properties,
final GlobalClientInterceptorRegistry globalClientInterceptorRegistry,
final List<GrpcChannelConfigurer> channelConfigurers) {

log.info("Detected grpc-netty-shaded: Creating ShadedNettyChannelFactory");
return new ShadedNettyChannelFactory(properties, globalClientInterceptorRegistry, channelConfigurers);
}

// Then try the normal netty channel factory
@ConditionalOnMissingBean(GrpcChannelFactory.class)
@ConditionalOnClass(name = {"io.netty.channel.Channel", "io.grpc.netty.NettyChannelBuilder"})
@Bean
@Lazy
GrpcChannelFactory nettyGrpcChannelFactory(
final GrpcChannelsProperties properties,
final GlobalClientInterceptorRegistry globalClientInterceptorRegistry,
final List<GrpcChannelConfigurer> channelConfigurers) {

log.info("Detected grpc-netty: Creating NettyChannelFactory");
return new NettyChannelFactory(properties, globalClientInterceptorRegistry, channelConfigurers);
}

// Finally try the in process channel factory
@ConditionalOnMissingBean(GrpcChannelFactory.class)
@ConditionalOnClass(name = {"io.grpc.inprocess.InProcessChannelBuilder"})
@Bean
@Lazy
GrpcChannelFactory inProcessGrpcChannelFactory(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2016-2024 The gRPC-Spring Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.devh.boot.grpc.client.autoconfigure;

import static java.util.Objects.requireNonNull;

import java.time.Duration;

import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import io.grpc.ClientInterceptor;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.client.channelfactory.GrpcChannelConfigurer;
import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
import net.devh.boot.grpc.client.interceptor.DefaultRequestTimeoutSetupClientInterceptor;

/**
* The default request timeout autoconfiguration for the client.
*
* <p>
* You can disable this config by using:
* </p>
*
* <pre>
* <code>@ImportAutoConfiguration(exclude = GrpcClientDefaultRequestTimeoutAutoConfiguration.class)</code>
* </pre>
*
* @author Sergei Batsura ([email protected])
*/
@Slf4j
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(GrpcClientAutoConfiguration.class)
public class GrpcClientDefaultRequestTimeoutAutoConfiguration {

/**
* Creates a {@link GrpcChannelConfigurer} bean applying the default request timeout from config to each new call
* using a {@link ClientInterceptor}.
*
* @param props The properties for timeout configuration.
* @return The GrpcChannelConfigurer bean with interceptor if timeout is configured.
* @see DefaultRequestTimeoutSetupClientInterceptor
*/
@Bean
GrpcChannelConfigurer timeoutGrpcChannelConfigurer(final GrpcChannelsProperties props) {
requireNonNull(props, "properties");

return (channel, name) -> {
Duration timeout = props.getChannel(name).getDefaultRequestTimeout();
if (timeout != null && timeout.toMillis() > 0L) {
channel.intercept(new DefaultRequestTimeoutSetupClientInterceptor(timeout));
}
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;

import io.grpc.CallOptions;
import io.grpc.LoadBalancerRegistry;
import io.grpc.ManagedChannelBuilder;
import io.grpc.NameResolverProvider;
Expand Down Expand Up @@ -118,6 +119,35 @@ public void setAddress(final String address) {
this.address = address == null ? null : URI.create(address);
}

// --------------------------------------------------
// defaultRequestTimeout
// --------------------------------------------------

private Duration defaultRequestTimeout = null;

/**
* Gets the default request timeout for each new call.
*
* @return The default request timeout or null
* @see #setDefaultRequestTimeout(Duration)
*/
public Duration getDefaultRequestTimeout() {
return this.defaultRequestTimeout;
}

/**
* Set the default request timeout duration for new calls (on a per call basis). By default and if zero value is
* configured, the timeout will not be used. The default request timeout will be ignored, if a deadline has been
* applied manually.
*
* @param defaultRequestTimeout the default request timeout or null.
*
* @see CallOptions#withDeadlineAfter(long, TimeUnit)
*/
public void setDefaultRequestTimeout(Duration defaultRequestTimeout) {
this.defaultRequestTimeout = defaultRequestTimeout;
}

// --------------------------------------------------
// defaultLoadBalancingPolicy
// --------------------------------------------------
Expand Down Expand Up @@ -480,6 +510,9 @@ public void copyDefaultsFrom(final GrpcChannelProperties config) {
if (this.address == null) {
this.address = config.address;
}
if (this.defaultRequestTimeout == null) {
this.defaultRequestTimeout = config.defaultRequestTimeout;
}
if (this.defaultLoadBalancingPolicy == null) {
this.defaultLoadBalancingPolicy = config.defaultLoadBalancingPolicy;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2016-2024 The gRPC-Spring Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.devh.boot.grpc.client.interceptor;

import static java.util.Objects.requireNonNull;

import java.time.Duration;
import java.util.concurrent.TimeUnit;

import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.MethodDescriptor;
import lombok.extern.slf4j.Slf4j;

/**
* A client interceptor configuring the default request timeout / deadline for each call.
*
* @author Sergei Batsura ([email protected])
*/
@Slf4j
public class DefaultRequestTimeoutSetupClientInterceptor implements ClientInterceptor {

private final Duration defaultRequestTimeout;

public DefaultRequestTimeoutSetupClientInterceptor(Duration defaultRequestTimeout) {
this.defaultRequestTimeout = requireNonNull(defaultRequestTimeout, "defaultRequestTimeout");
}

@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
final MethodDescriptor<ReqT, RespT> method,
final CallOptions callOptions,
final Channel next) {

if (callOptions.getDeadline() == null) {
return next.newCall(method,
callOptions.withDeadlineAfter(defaultRequestTimeout.toMillis(), TimeUnit.MILLISECONDS));
} else {
return next.newCall(method, callOptions);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@
"description": "Connection timeout at application startup. If set to a positive duration instructs a client to connect to GRPC-endpoint when GRPC stub is created.",
"defaultValue": 0
},
{
"name": "grpc.client.GLOBAL.defaultRequestTimeout",
"type": "java.time.Duration",
"sourceType": "net.devh.boot.grpc.client.config.GrpcChannelProperties",
"description": "The default timeout is applied to each new call. By default, and if a zero value is configured, the timeout will not be set. The default timeout will be ignored if a deadline has been set manually."
},
{
"name": "grpc.client.GLOBAL.security.authority-override",
"type": "java.lang.String",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ net.devh.boot.grpc.client.autoconfigure.GrpcClientHealthAutoConfiguration
net.devh.boot.grpc.client.autoconfigure.GrpcClientMicrometerTraceAutoConfiguration
net.devh.boot.grpc.client.autoconfigure.GrpcClientSecurityAutoConfiguration
net.devh.boot.grpc.client.autoconfigure.GrpcDiscoveryClientAutoConfiguration
net.devh.boot.grpc.client.autoconfigure.GrpcClientDefaultRequestTimeoutAutoConfiguration
Loading

0 comments on commit ade0a79

Please sign in to comment.