Skip to content

Commit a653e17

Browse files
authored
Merge branch 'master' into renovate/gradle-gradle-build-action-3.x
2 parents f7ec0f1 + bbe8def commit a653e17

File tree

19 files changed

+387
-63
lines changed

19 files changed

+387
-63
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ establishing a direct line of communication. Your feedback is highly appreciated
1717

1818
README: [English](README.md) | [中文](README-zh-CN.md)
1919

20-
**Documentation:** [English](https://yidongnan.github.io/grpc-spring-boot-starter/en/) | [中文](https://yidongnan.github.io/grpc-spring-boot-starter/zh-CN/)
20+
**Documentation:** [English](https://grpc-ecosystem.github.io/grpc-spring/en/) | [中文](https://grpc-ecosystem.github.io/grpc-spring/zh-CN/)
2121

2222
## Features
2323

build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@ buildscript {
1111
projectVersion = '3.0.0.RELEASE'
1212

1313
// https://github.com/grpc/grpc-java/releases
14-
grpcVersion = '1.60.1'
14+
grpcVersion = '1.63.0'
1515

1616
// https://github.com/google/guava/releases
17-
guavaVersion = '33.0.0-jre'
17+
guavaVersion = '33.1.0-jre'
1818
// https://github.com/protocolbuffers/protobuf/releases
1919
protobufVersion = '3.25.3'
2020
protobufGradlePluginVersion = '0.9.4'
2121

2222
// https://github.com/spring-projects/spring-boot/releases
23-
springBootVersion = '3.2.3'
23+
springBootVersion = '3.2.4'
2424
// https://github.com/spring-cloud/spring-cloud-release/releases
2525
springCloudVersion = '2023.0.0'
2626
// https://github.com/alibaba/spring-cloud-alibaba/releases
@@ -61,7 +61,7 @@ if (hasProperty('buildScan')) {
6161
wrapper {
6262
// Update using:
6363
// ./gradlew wrapper --gradle-version=8.4 --distribution-type=bin
64-
gradleVersion = '8.6'
64+
gradleVersion = '8.7'
6565
}
6666

6767
def buildTimeAndDate = OffsetDateTime.now()

docs/en/client/configuration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ There are a number of supported schemes, that you can use to determine the targe
6969
- `discovery` (Prio 6): \
7070
(Optional) Uses spring-cloud's `DiscoveryClient` to lookup appropriate targets. The connections will be refreshed
7171
automatically during `HeartbeatEvent`s. Uses the `gRPC_port` metadata to determine the port, otherwise uses the
72-
service port. \
72+
service port. Uses the `gRPC_service_config` metadata to determine [service config](https://grpc.github.io/grpc/core/md_doc_service_config.html). \
7373
Example: `discovery:///service-name`
7474
- `self` (Prio 0): \
7575
The self address or scheme is a keyword that is available, if you also use `grpc-server-spring-boot-starter` and

gradle/wrapper/gradle-wrapper.jar

-9 Bytes
Binary file not shown.

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

grpc-client-spring-boot-starter/src/main/java/net/devh/boot/grpc/client/channelfactory/AbstractChannelFactory.java

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@ protected void configure(final T builder, final String name) {
181181
configureKeepAlive(builder, name);
182182
configureSecurity(builder, name);
183183
configureLimits(builder, name);
184-
configureCompression(builder, name);
185184
configureUserAgent(builder, name);
186185
for (final GrpcChannelConfigurer channelConfigurer : this.channelConfigurers) {
187186
channelConfigurer.accept(builder, name);
@@ -234,7 +233,7 @@ protected boolean isNonNullAndNonBlank(final String value) {
234233
}
235234

236235
/**
237-
* Configures limits such as max message sizes that should be used by the channel.
236+
* Configures limits such as max message or metadata sizes that should be used by the channel.
238237
*
239238
* @param builder The channel builder to configure.
240239
* @param name The name of the client to configure.
@@ -245,18 +244,9 @@ protected void configureLimits(final T builder, final String name) {
245244
if (maxInboundMessageSize != null) {
246245
builder.maxInboundMessageSize((int) maxInboundMessageSize.toBytes());
247246
}
248-
}
249-
250-
/**
251-
* Configures the compression options that should be used by the channel.
252-
*
253-
* @param builder The channel builder to configure.
254-
* @param name The name of the client to configure.
255-
*/
256-
protected void configureCompression(final T builder, final String name) {
257-
final GrpcChannelProperties properties = getPropertiesFor(name);
258-
if (properties.isFullStreamDecompression()) {
259-
builder.enableFullStreamDecompression();
247+
final DataSize maxInboundMetadataSize = properties.getMaxInboundMetadataSize();
248+
if (maxInboundMetadataSize != null) {
249+
builder.maxInboundMetadataSize((int) maxInboundMetadataSize.toBytes());
260250
}
261251
}
262252

grpc-client-spring-boot-starter/src/main/java/net/devh/boot/grpc/client/config/GrpcChannelProperties.java

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -333,35 +333,43 @@ public void setMaxInboundMessageSize(final DataSize maxInboundMessageSize) {
333333
}
334334
}
335335

336-
// --------------------------------------------------
337-
338-
private Boolean fullStreamDecompression;
339-
private static final boolean DEFAULT_FULL_STREAM_DECOMPRESSION = false;
336+
@DataSizeUnit(DataUnit.BYTES)
337+
private DataSize maxInboundMetadataSize = null;
340338

341339
/**
342-
* Gets whether full-stream decompression of inbound streams should be enabled.
340+
* Sets the maximum size of metadata in bytes allowed to be received. If not set ({@code null}) then it will default
341+
* to gRPC's default. The default is implementation-dependent, but is not generally less than 8 KiB and may be
342+
* unlimited. If set to {@code -1} then it will use the highest possible limit (not recommended). Integer.MAX_VALUE
343+
* disables the enforcement.
343344
*
344-
* @return True, if full-stream decompression of inbound streams should be enabled. False otherwise.
345+
* @return The maximum size of metadata in bytes allowed to be received or null if the default should be used.
345346
*
346-
* @see #setFullStreamDecompression(Boolean)
347+
* @see ManagedChannelBuilder#maxInboundMetadataSize(int) (int)
347348
*/
348-
public boolean isFullStreamDecompression() {
349-
return this.fullStreamDecompression == null ? DEFAULT_FULL_STREAM_DECOMPRESSION : this.fullStreamDecompression;
349+
public DataSize getMaxInboundMetadataSize() {
350+
return maxInboundMetadataSize;
350351
}
351352

352353
/**
353-
* Sets whether full-stream decompression of inbound streams should be enabled. This will cause the channel's
354-
* outbound headers to advertise support for GZIP compressed streams, and gRPC servers which support the feature may
355-
* respond with a GZIP compressed stream.
354+
* Sets the maximum size of metadata in bytes allowed to be received. If not set ({@code null}) then it will
355+
* default.The default is implementation-dependent, but is not generally less than 8 KiB and may be unlimited. If
356+
* set to {@code -1} then it will use the highest possible limit (not recommended). Integer.MAX_VALUE disables the
357+
* enforcement.
356358
*
357-
* @param fullStreamDecompression Whether full stream decompression should be enabled or null to use the fallback.
359+
* @param maxInboundMetadataSize The new maximum size of metadata in bytes allowed to be received. {@code -1} for
360+
* max possible. Null to use the gRPC's default.
358361
*
359-
* @see ManagedChannelBuilder#enableFullStreamDecompression()
362+
* @see ManagedChannelBuilder#maxInboundMetadataSize(int) (int)
360363
*/
361-
public void setFullStreamDecompression(final Boolean fullStreamDecompression) {
362-
this.fullStreamDecompression = fullStreamDecompression;
364+
public void setMaxInboundMetadataSize(DataSize maxInboundMetadataSize) {
365+
if (maxInboundMetadataSize == null || maxInboundMetadataSize.toBytes() >= 0) {
366+
this.maxInboundMetadataSize = maxInboundMetadataSize;
367+
} else if (maxInboundMetadataSize.toBytes() == -1) {
368+
this.maxInboundMetadataSize = DataSize.ofBytes(Integer.MAX_VALUE);
369+
} else {
370+
throw new IllegalArgumentException("Unsupported maxInboundMetadataSize: " + maxInboundMetadataSize);
371+
}
363372
}
364-
365373
// --------------------------------------------------
366374

367375
private NegotiationType negotiationType;
@@ -493,9 +501,6 @@ public void copyDefaultsFrom(final GrpcChannelProperties config) {
493501
if (this.maxInboundMessageSize == null) {
494502
this.maxInboundMessageSize = config.maxInboundMessageSize;
495503
}
496-
if (this.fullStreamDecompression == null) {
497-
this.fullStreamDecompression = config.fullStreamDecompression;
498-
}
499504
if (this.negotiationType == null) {
500505
this.negotiationType = config.negotiationType;
501506
}

grpc-client-spring-boot-starter/src/main/java/net/devh/boot/grpc/client/metrics/MetricsClientStreamTracers.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import io.grpc.Status;
3636
import io.grpc.Status.Code;
3737
import io.micrometer.core.instrument.Tags;
38+
import net.devh.boot.grpc.common.util.Constants;
3839

3940
/**
4041
* Provides factories for {@link io.grpc.StreamTracer} that records metrics.
@@ -47,6 +48,8 @@
4748
final class MetricsClientStreamTracers {
4849
private static final Supplier<Stopwatch> STOPWATCH_SUPPLIER = Stopwatch::createUnstarted;
4950
private final Supplier<Stopwatch> stopwatchSupplier;
51+
private static final String INSTRUMENTATION_SOURCE_TAG_KEY = "instrumentation_source";
52+
private static final String INSTRUMENTATION_VERSION_TAG_KEY = "instrumentation_version";
5053

5154
MetricsClientStreamTracers() {
5255
this(STOPWATCH_SUPPLIER);
@@ -127,7 +130,10 @@ public void streamClosed(Status status) {
127130

128131
void recordFinishedAttempt() {
129132
Tags attemptMetricTags =
130-
Tags.of("grpc.method", fullMethodName, "grpc.status", statusCode.toString());
133+
Tags.of("grpc.method", fullMethodName,
134+
"grpc.status", statusCode.toString(),
135+
INSTRUMENTATION_SOURCE_TAG_KEY, Constants.LIBRARY_NAME,
136+
INSTRUMENTATION_VERSION_TAG_KEY, Constants.VERSION);
131137
this.metricsClientMeters.getClientAttemptDuration()
132138
.withTags(attemptMetricTags)
133139
.record(attemptNanos, TimeUnit.NANOSECONDS);
@@ -168,7 +174,9 @@ static final class CallAttemptsTracerFactory extends ClientStreamTracer.Factory
168174

169175
// Record here in case newClientStreamTracer() would never be called.
170176
this.metricsClientMeters.getAttemptCounter()
171-
.withTags(Tags.of("grpc.method", fullMethodName))
177+
.withTags(Tags.of("grpc.method", fullMethodName,
178+
INSTRUMENTATION_SOURCE_TAG_KEY, Constants.LIBRARY_NAME,
179+
INSTRUMENTATION_VERSION_TAG_KEY, Constants.VERSION))
172180
.increment();
173181
}
174182

@@ -188,7 +196,9 @@ public ClientStreamTracer newClientStreamTracer(StreamInfo info, Metadata metada
188196
// attempt, as first attempt cannot be a transparent retry.
189197
if (attemptsPerCall.get() > 0) {
190198
this.metricsClientMeters.getAttemptCounter()
191-
.withTags((Tags.of("grpc.method", fullMethodName)))
199+
.withTags((Tags.of("grpc.method", fullMethodName,
200+
INSTRUMENTATION_SOURCE_TAG_KEY, Constants.LIBRARY_NAME,
201+
INSTRUMENTATION_VERSION_TAG_KEY, Constants.VERSION)))
192202
.increment();
193203
}
194204
if (!info.isTransparentRetry()) {
@@ -248,7 +258,10 @@ void recordFinishedCall() {
248258
}
249259
callLatencyNanos = clientCallStopWatch.elapsed(TimeUnit.NANOSECONDS);
250260
Tags clientCallMetricTags =
251-
Tags.of("grpc.method", this.fullMethodName, "grpc.status", status.getCode().toString());
261+
Tags.of("grpc.method", this.fullMethodName,
262+
"grpc.status", status.getCode().toString(),
263+
INSTRUMENTATION_SOURCE_TAG_KEY, Constants.LIBRARY_NAME,
264+
INSTRUMENTATION_VERSION_TAG_KEY, Constants.VERSION);
252265
this.metricsClientMeters.getClientCallDuration()
253266
.withTags(clientCallMetricTags)
254267
.record(callLatencyNanos, TimeUnit.NANOSECONDS);

grpc-client-spring-boot-starter/src/main/java/net/devh/boot/grpc/client/nameresolver/DiscoveryClientNameResolver.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import static net.devh.boot.grpc.client.nameresolver.DiscoveryClientResolverFactory.DISCOVERY_INSTANCE_ID_KEY;
2323
import static net.devh.boot.grpc.client.nameresolver.DiscoveryClientResolverFactory.DISCOVERY_SERVICE_NAME_KEY;
2424
import static net.devh.boot.grpc.common.util.GrpcUtils.CLOUD_DISCOVERY_METADATA_PORT;
25+
import static net.devh.boot.grpc.common.util.GrpcUtils.CLOUD_DISCOVERY_METADATA_SERVICE_CONFIG;
2526

2627
import java.net.InetSocketAddress;
2728
import java.util.List;
@@ -35,6 +36,8 @@
3536
import org.springframework.util.CollectionUtils;
3637

3738
import com.google.common.collect.Lists;
39+
import com.google.gson.Gson;
40+
import com.google.gson.JsonSyntaxException;
3841

3942
import io.grpc.Attributes;
4043
import io.grpc.Attributes.Builder;
@@ -58,13 +61,15 @@ public class DiscoveryClientNameResolver extends NameResolver {
5861
@Deprecated
5962
private static final String LEGACY_CLOUD_DISCOVERY_METADATA_PORT = "gRPC.port";
6063
private static final List<ServiceInstance> KEEP_PREVIOUS = null;
64+
private static final Gson GSON = new Gson();
6165

6266
private final String name;
6367
private final DiscoveryClient client;
6468
private final SynchronizationContext syncContext;
6569
private final Consumer<DiscoveryClientNameResolver> shutdownHook;
6670
private final SharedResourceHolder.Resource<Executor> executorResource;
6771
private final boolean usingExecutorResource;
72+
private final ServiceConfigParser serviceConfigParser;
6873

6974
// The field must be accessed from syncContext, although the methods on an Listener2 can be called
7075
// from any thread.
@@ -93,6 +98,7 @@ public DiscoveryClientNameResolver(final String name, final DiscoveryClient clie
9398
this.executor = args.getOffloadExecutor();
9499
this.usingExecutorResource = this.executor == null;
95100
this.executorResource = executorResource;
101+
this.serviceConfigParser = args.getServiceConfigParser();
96102
}
97103

98104
/**
@@ -187,6 +193,55 @@ protected int getGrpcPort(final ServiceInstance instance) {
187193
}
188194
}
189195

196+
/**
197+
* Extracts and parse gRPC service config from the given service instances.
198+
*
199+
* @param instances The list of instances to extract the service config from.
200+
* @return Parsed gRPC service config or null.
201+
*/
202+
private ConfigOrError resolveServiceConfig(List<ServiceInstance> instances) {
203+
final String serviceConfig = getServiceConfig(instances);
204+
if (serviceConfig == null) {
205+
return null;
206+
}
207+
log.debug("Found service config for {}", getName());
208+
if (log.isTraceEnabled()) {
209+
// This is to avoid blowing log into several lines if newlines present in service config string.
210+
final String logStr = serviceConfig.replace("\r", "\\r").replace("\n", "\\n");
211+
log.trace("Service config for {}: {}", getName(), logStr);
212+
}
213+
try {
214+
@SuppressWarnings("unchecked")
215+
Map<String, ?> parsedServiceConfig = GSON.fromJson(serviceConfig, Map.class);
216+
return serviceConfigParser.parseServiceConfig(parsedServiceConfig);
217+
} catch (JsonSyntaxException e) {
218+
return ConfigOrError.fromError(
219+
Status.UNKNOWN
220+
.withDescription("Failed to parse grpc service config")
221+
.withCause(e));
222+
}
223+
}
224+
225+
/**
226+
* Extracts the gRPC service config string from the given service instances.
227+
*
228+
* @param instances The list of instances to extract the service config from.
229+
* @return The gRPC service config or null.
230+
*/
231+
protected String getServiceConfig(final List<ServiceInstance> instances) {
232+
for (final ServiceInstance inst : instances) {
233+
final Map<String, String> metadata = inst.getMetadata();
234+
if (metadata == null || metadata.isEmpty()) {
235+
continue;
236+
}
237+
final String metaValue = metadata.get(CLOUD_DISCOVERY_METADATA_SERVICE_CONFIG);
238+
if (metaValue != null && !metaValue.isEmpty()) {
239+
return metaValue;
240+
}
241+
}
242+
return null;
243+
}
244+
190245
/**
191246
* Gets the attributes from the service instance for later use in a load balancer. Can be overwritten to convert
192247
* custom attributes.
@@ -318,6 +373,7 @@ private List<ServiceInstance> resolveInternal() {
318373
log.debug("Ready to update server list for {}", getName());
319374
this.savedListener.onResult(ResolutionResult.newBuilder()
320375
.setAddresses(toTargets(newInstanceList))
376+
.setServiceConfig(resolveServiceConfig(newInstanceList))
321377
.build());
322378
log.info("Done updating server list for {}", getName());
323379
return newInstanceList;

grpc-client-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,6 @@
5353
"description": "Whether keepAlive should be enabled.",
5454
"defaultValue": false
5555
},
56-
{
57-
"name": "grpc.client.GLOBAL.full-stream-decompression",
58-
"type": "java.lang.Boolean",
59-
"sourceType": "net.devh.boot.grpc.client.config.GrpcChannelProperties",
60-
"description": "Whether full-stream decompression of inbound streams should be enabled.",
61-
"defaultValue": false
62-
},
6356
{
6457
"name": "grpc.client.GLOBAL.keep-alive-time",
6558
"type": "java.time.Duration",
@@ -94,6 +87,12 @@
9487
"sourceType": "net.devh.boot.grpc.client.config.GrpcChannelProperties",
9588
"description": "The maximum message size allowed to be received by the channel.\nIf not set (null) then it will default to gRPC's default.\nIf set to -1 then it will use the highest possible limit (not recommended)."
9689
},
90+
{
91+
"name": "grpc.client.GLOBAL.max-inbound-metadata-size",
92+
"type": "org.springframework.util.unit.DataSize",
93+
"sourceType": "net.devh.boot.grpc.client.config.GrpcChannelProperties",
94+
"description": "the maximum size of metadata in bytes allowed to be received. \nIf not set (null) then it will default to gRPC's default. \nIf set to {@code -1} then it will use the highest possible limit (not recommended)."
95+
},
9796
{
9897
"name": "grpc.client.GLOBAL.negotiation-type",
9998
"type": "net.devh.boot.grpc.client.config.NegotiationType",
@@ -158,4 +157,4 @@
158157
"description": "The path to the trusted certificate collection.\nIf not set (null) it will use the system's default collection (Default).\nThis collection will be used to verify server certificates."
159158
}
160159
]
161-
}
160+
}

0 commit comments

Comments
 (0)