Skip to content

Commit 0a5324d

Browse files
authored
Merge branch 'master' into upgrade-grpc
2 parents 679c4e5 + cb029de commit 0a5324d

File tree

12 files changed

+341
-15
lines changed

12 files changed

+341
-15
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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ buildscript {
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

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

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/test/java/net/devh/boot/grpc/client/metrics/MetricsClientStreamTracersTest.java

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import io.micrometer.core.instrument.distribution.HistogramSnapshot;
3838
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
3939
import net.devh.boot.grpc.client.metrics.MetricsClientStreamTracers.CallAttemptsTracerFactory;
40+
import net.devh.boot.grpc.common.util.Constants;
4041

4142
/**
4243
* Tests for {@link MetricsClientStreamTracers}.
@@ -61,6 +62,10 @@ class MetricsClientStreamTracersTest {
6162
private static final String GRPC_METHOD_TAG_KEY = "grpc.method";
6263
private static final String GRPC_STATUS_TAG_KEY = "grpc.status";
6364
private static final String FULL_METHOD_NAME = "package1.service1/method1";
65+
private static final String INSTRUMENTATION_SOURCE_TAG_KEY = "instrumentation_source";
66+
private static final String INSTRUMENTATION_SOURCE_TAG_VALUE = Constants.LIBRARY_NAME;
67+
private static final String INSTRUMENTATION_VERSION_TAG_KEY = "instrumentation_version";
68+
private static final String INSTRUMENTATION_VERSION_TAG_VALUE = Constants.VERSION;
6469

6570
private static class StringInputStream extends InputStream {
6671
final String string;
@@ -125,6 +130,8 @@ void clientBasicMetrics() {
125130

126131
assertThat(meterRegistry.get(CLIENT_ATTEMPT_STARTED)
127132
.tag(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME)
133+
.tag(INSTRUMENTATION_SOURCE_TAG_KEY, INSTRUMENTATION_SOURCE_TAG_VALUE)
134+
.tag(INSTRUMENTATION_VERSION_TAG_KEY, INSTRUMENTATION_VERSION_TAG_VALUE)
128135
.counter()
129136
.count()).isEqualTo(1);
130137

@@ -146,11 +153,16 @@ void clientBasicMetrics() {
146153

147154
assertThat(meterRegistry.get(CLIENT_ATTEMPT_STARTED)
148155
.tag(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME)
156+
.tag(INSTRUMENTATION_SOURCE_TAG_KEY, INSTRUMENTATION_SOURCE_TAG_VALUE)
157+
.tag(INSTRUMENTATION_VERSION_TAG_KEY, INSTRUMENTATION_VERSION_TAG_VALUE)
149158
.counter()
150159
.count()).isEqualTo(1);
151160

152161
Tags expectedTags =
153-
Tags.of(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME, GRPC_STATUS_TAG_KEY, Status.Code.OK.toString());
162+
Tags.of(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME,
163+
GRPC_STATUS_TAG_KEY, Status.Code.OK.toString(),
164+
INSTRUMENTATION_SOURCE_TAG_KEY, INSTRUMENTATION_SOURCE_TAG_VALUE,
165+
INSTRUMENTATION_VERSION_TAG_KEY, INSTRUMENTATION_VERSION_TAG_VALUE);
154166

155167
HistogramSnapshot attemptDurationSnapshot = meterRegistry.get(CLIENT_ATTEMPT_DURATION)
156168
.tags(expectedTags)
@@ -200,6 +212,8 @@ void recordAttemptMetrics() {
200212

201213
assertThat(meterRegistry.get(CLIENT_ATTEMPT_STARTED)
202214
.tag(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME)
215+
.tag(INSTRUMENTATION_SOURCE_TAG_KEY, INSTRUMENTATION_SOURCE_TAG_VALUE)
216+
.tag(INSTRUMENTATION_VERSION_TAG_KEY, INSTRUMENTATION_VERSION_TAG_VALUE)
203217
.counter()
204218
.count()).isEqualTo(1);
205219

@@ -213,10 +227,15 @@ void recordAttemptMetrics() {
213227
tracer.streamClosed(Status.UNAVAILABLE);
214228

215229
Tags expectedUnailableStatusTags =
216-
Tags.of(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME, GRPC_STATUS_TAG_KEY, Status.Code.UNAVAILABLE.toString());
230+
Tags.of(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME,
231+
GRPC_STATUS_TAG_KEY, Status.Code.UNAVAILABLE.toString(),
232+
INSTRUMENTATION_SOURCE_TAG_KEY, INSTRUMENTATION_SOURCE_TAG_VALUE,
233+
INSTRUMENTATION_VERSION_TAG_KEY, INSTRUMENTATION_VERSION_TAG_VALUE);
217234

218235
assertThat(meterRegistry.get(CLIENT_ATTEMPT_STARTED)
219236
.tag(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME)
237+
.tag(INSTRUMENTATION_SOURCE_TAG_KEY, INSTRUMENTATION_SOURCE_TAG_VALUE)
238+
.tag(INSTRUMENTATION_VERSION_TAG_KEY, INSTRUMENTATION_VERSION_TAG_VALUE)
220239
.counter()
221240
.count()).isEqualTo(1);
222241
assertThat(meterRegistry.get(CLIENT_ATTEMPT_DURATION)
@@ -248,10 +267,15 @@ void recordAttemptMetrics() {
248267
tracer.streamClosed(Status.NOT_FOUND);
249268

250269
Tags expectedNotFoundStatusTags =
251-
Tags.of(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME, GRPC_STATUS_TAG_KEY, Status.Code.NOT_FOUND.toString());
270+
Tags.of(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME,
271+
GRPC_STATUS_TAG_KEY, Status.Code.NOT_FOUND.toString(),
272+
INSTRUMENTATION_SOURCE_TAG_KEY, INSTRUMENTATION_SOURCE_TAG_VALUE,
273+
INSTRUMENTATION_VERSION_TAG_KEY, INSTRUMENTATION_VERSION_TAG_VALUE);
252274

253275
assertThat(meterRegistry.get(CLIENT_ATTEMPT_STARTED)
254276
.tag(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME)
277+
.tag(INSTRUMENTATION_SOURCE_TAG_KEY, INSTRUMENTATION_SOURCE_TAG_VALUE)
278+
.tag(INSTRUMENTATION_VERSION_TAG_KEY, INSTRUMENTATION_VERSION_TAG_VALUE)
255279
.counter()
256280
.count()).isEqualTo(2);
257281

@@ -290,6 +314,8 @@ void recordAttemptMetrics() {
290314

291315
assertThat(meterRegistry.get(CLIENT_ATTEMPT_STARTED)
292316
.tag(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME)
317+
.tag(INSTRUMENTATION_SOURCE_TAG_KEY, INSTRUMENTATION_SOURCE_TAG_VALUE)
318+
.tag(INSTRUMENTATION_VERSION_TAG_KEY, INSTRUMENTATION_VERSION_TAG_VALUE)
293319
.counter()
294320
.count()).isEqualTo(3);
295321

@@ -342,10 +368,15 @@ void recordAttemptMetrics() {
342368
callAttemptsTracerFactory.callEnded(Status.OK);
343369

344370
Tags expectedOKStatusTags =
345-
Tags.of(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME, GRPC_STATUS_TAG_KEY, Status.Code.OK.toString());
371+
Tags.of(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME,
372+
GRPC_STATUS_TAG_KEY, Status.Code.OK.toString(),
373+
INSTRUMENTATION_SOURCE_TAG_KEY, INSTRUMENTATION_SOURCE_TAG_VALUE,
374+
INSTRUMENTATION_VERSION_TAG_KEY, INSTRUMENTATION_VERSION_TAG_VALUE);
346375

347376
assertThat(meterRegistry.get(CLIENT_ATTEMPT_STARTED)
348377
.tag(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME)
378+
.tag(INSTRUMENTATION_SOURCE_TAG_KEY, INSTRUMENTATION_SOURCE_TAG_VALUE)
379+
.tag(INSTRUMENTATION_VERSION_TAG_KEY, INSTRUMENTATION_VERSION_TAG_VALUE)
349380
.counter()
350381
.count()).isEqualTo(4);
351382
assertThat(meterRegistry.get(CLIENT_ATTEMPT_DURATION)
@@ -388,8 +419,10 @@ void clientStreamNeverCreatedStillRecordMetrics() {
388419
callAttemptsTracerFactory.callEnded(status);
389420

390421
Tags expectedDeadlineExceededStatusTags =
391-
Tags.of(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME, GRPC_STATUS_TAG_KEY,
392-
Status.Code.DEADLINE_EXCEEDED.toString());
422+
Tags.of(GRPC_METHOD_TAG_KEY, FULL_METHOD_NAME,
423+
GRPC_STATUS_TAG_KEY, Status.Code.DEADLINE_EXCEEDED.toString(),
424+
INSTRUMENTATION_SOURCE_TAG_KEY, INSTRUMENTATION_SOURCE_TAG_VALUE,
425+
INSTRUMENTATION_VERSION_TAG_KEY, INSTRUMENTATION_VERSION_TAG_VALUE);
393426

394427
HistogramSnapshot attemptDurationSnapshot = meterRegistry.get(CLIENT_ATTEMPT_DURATION)
395428
.tags(expectedDeadlineExceededStatusTags)

0 commit comments

Comments
 (0)