diff --git a/build.gradle b/build.gradle index 779cdcbb73..963e0fc011 100644 --- a/build.gradle +++ b/build.gradle @@ -110,7 +110,7 @@ subprojects { protobufVersion = '3.5.1' protocVersion = '3.5.1-1' protobufNanoVersion = '3.0.0-alpha-5' - opencensusVersion = '0.17.0' + opencensusVersion = '0.18.0' configureProtoCompilation = { String generatedSourcePath = "${projectDir}/src/generated" diff --git a/core/src/main/java/io/grpc/internal/AbstractManagedChannelImplBuilder.java b/core/src/main/java/io/grpc/internal/AbstractManagedChannelImplBuilder.java index bca189f52a..985bc4cb62 100644 --- a/core/src/main/java/io/grpc/internal/AbstractManagedChannelImplBuilder.java +++ b/core/src/main/java/io/grpc/internal/AbstractManagedChannelImplBuilder.java @@ -165,6 +165,7 @@ public abstract class AbstractManagedChannelImplBuilder private boolean statsEnabled = true; private boolean recordStartedRpcs = true; private boolean recordFinishedRpcs = true; + private boolean recordRealTimeMetrics = false; private boolean tracingEnabled = true; @Nullable @@ -383,6 +384,14 @@ public abstract class AbstractManagedChannelImplBuilder recordFinishedRpcs = value; } + /** + * Disable or enable real-time metrics recording. Effective only if {@link #setStatsEnabled} is + * set to true. Disabled by default. + */ + protected void setStatsRecordRealTimeMetrics(boolean value) { + recordRealTimeMetrics = value; + } + /** * Disable or enable tracing features. Enabled by default. * @@ -433,7 +442,8 @@ public abstract class AbstractManagedChannelImplBuilder CensusStatsModule censusStats = this.censusStatsOverride; if (censusStats == null) { censusStats = new CensusStatsModule( - GrpcUtil.STOPWATCH_SUPPLIER, true, recordStartedRpcs, recordFinishedRpcs); + GrpcUtil.STOPWATCH_SUPPLIER, true, recordStartedRpcs, recordFinishedRpcs, + recordRealTimeMetrics); } // First interceptor runs last (see ClientInterceptors.intercept()), so that no // other interceptor can override the tracer factory we set in CallOptions. diff --git a/core/src/main/java/io/grpc/internal/AbstractServerImplBuilder.java b/core/src/main/java/io/grpc/internal/AbstractServerImplBuilder.java index b9396e4f40..25a1c0105e 100644 --- a/core/src/main/java/io/grpc/internal/AbstractServerImplBuilder.java +++ b/core/src/main/java/io/grpc/internal/AbstractServerImplBuilder.java @@ -108,6 +108,7 @@ public abstract class AbstractServerImplBuilder stopwatchSupplier, - boolean propagateTags, boolean recordStartedRpcs, boolean recordFinishedRpcs) { + boolean propagateTags, boolean recordStartedRpcs, boolean recordFinishedRpcs, + boolean recordRealTimeMetrics) { this( Tags.getTagger(), Tags.getTagPropagationComponent().getBinarySerializer(), Stats.getStatsRecorder(), stopwatchSupplier, - propagateTags, recordStartedRpcs, recordFinishedRpcs); + propagateTags, recordStartedRpcs, recordFinishedRpcs, recordRealTimeMetrics); } /** @@ -98,7 +102,8 @@ public final class CensusStatsModule { final Tagger tagger, final TagContextBinarySerializer tagCtxSerializer, StatsRecorder statsRecorder, Supplier stopwatchSupplier, - boolean propagateTags, boolean recordStartedRpcs, boolean recordFinishedRpcs) { + boolean propagateTags, boolean recordStartedRpcs, boolean recordFinishedRpcs, + boolean recordRealTimeMetrics) { this.tagger = checkNotNull(tagger, "tagger"); this.statsRecorder = checkNotNull(statsRecorder, "statsRecorder"); checkNotNull(tagCtxSerializer, "tagCtxSerializer"); @@ -106,6 +111,7 @@ public final class CensusStatsModule { this.propagateTags = propagateTags; this.recordStartedRpcs = recordStartedRpcs; this.recordFinishedRpcs = recordFinishedRpcs; + this.recordRealTimeMetrics = recordRealTimeMetrics; this.statsHeader = Metadata.Key.of("grpc-tags-bin", new Metadata.BinaryMarshaller() { @Override @@ -154,6 +160,20 @@ public final class CensusStatsModule { return new StatsClientInterceptor(); } + private void recordRealTimeMetric(TagContext ctx, MeasureDouble measure, double value) { + if (recordRealTimeMetrics) { + MeasureMap measureMap = statsRecorder.newMeasureMap().put(measure, value); + measureMap.record(ctx); + } + } + + private void recordRealTimeMetric(TagContext ctx, MeasureLong measure, long value) { + if (recordRealTimeMetrics) { + MeasureMap measureMap = statsRecorder.newMeasureMap().put(measure, value); + measureMap.record(ctx); + } + } + private static final class ClientTracer extends ClientStreamTracer { @Nullable private static final AtomicLongFieldUpdater outboundMessageCountUpdater; @@ -209,6 +229,9 @@ public final class CensusStatsModule { inboundUncompressedSizeUpdater = tmpInboundUncompressedSizeUpdater; } + private final CensusStatsModule module; + private final TagContext startCtx; + volatile long outboundMessageCount; volatile long inboundMessageCount; volatile long outboundWireSize; @@ -216,6 +239,11 @@ public final class CensusStatsModule { volatile long outboundUncompressedSize; volatile long inboundUncompressedSize; + ClientTracer(CensusStatsModule module, TagContext startCtx) { + this.module = checkNotNull(module, "module"); + this.startCtx = checkNotNull(startCtx, "startCtx"); + } + @Override @SuppressWarnings("NonAtomicVolatileUpdate") public void outboundWireSize(long bytes) { @@ -224,6 +252,8 @@ public final class CensusStatsModule { } else { outboundWireSize += bytes; } + module.recordRealTimeMetric( + startCtx, RpcMeasureConstants.GRPC_CLIENT_SENT_BYTES_PER_METHOD, bytes); } @Override @@ -234,6 +264,8 @@ public final class CensusStatsModule { } else { inboundWireSize += bytes; } + module.recordRealTimeMetric( + startCtx, RpcMeasureConstants.GRPC_CLIENT_RECEIVED_BYTES_PER_METHOD, bytes); } @Override @@ -264,6 +296,8 @@ public final class CensusStatsModule { } else { inboundMessageCount++; } + module.recordRealTimeMetric( + startCtx, RpcMeasureConstants.GRPC_CLIENT_RECEIVED_MESSAGES_PER_METHOD, 1); } @Override @@ -274,6 +308,8 @@ public final class CensusStatsModule { } else { outboundMessageCount++; } + module.recordRealTimeMetric( + startCtx, RpcMeasureConstants.GRPC_CLIENT_SENT_MESSAGES_PER_METHOD, 1); } } @@ -332,7 +368,7 @@ public final class CensusStatsModule { @Override public ClientStreamTracer newClientStreamTracer(CallOptions callOptions, Metadata headers) { - ClientTracer tracer = new ClientTracer(); + ClientTracer tracer = new ClientTracer(module, startCtx); // TODO(zhangkun83): Once retry or hedging is implemented, a ClientCall may start more than // one streams. We will need to update this file to support them. if (streamTracerUpdater != null) { @@ -378,7 +414,7 @@ public final class CensusStatsModule { long roundtripNanos = stopwatch.elapsed(TimeUnit.NANOSECONDS); ClientTracer tracer = streamTracer; if (tracer == null) { - tracer = BLANK_CLIENT_TRACER; + tracer = new ClientTracer(module, startCtx); } MeasureMap measureMap = module.statsRecorder.newMeasureMap() // TODO(songya): remove the deprecated measure constants once they are completed removed. @@ -502,6 +538,8 @@ public final class CensusStatsModule { } else { outboundWireSize += bytes; } + module.recordRealTimeMetric( + parentCtx, RpcMeasureConstants.GRPC_SERVER_SENT_BYTES_PER_METHOD, bytes); } @Override @@ -512,6 +550,8 @@ public final class CensusStatsModule { } else { inboundWireSize += bytes; } + module.recordRealTimeMetric( + parentCtx, RpcMeasureConstants.GRPC_SERVER_RECEIVED_BYTES_PER_METHOD, bytes); } @Override @@ -542,6 +582,8 @@ public final class CensusStatsModule { } else { inboundMessageCount++; } + module.recordRealTimeMetric( + parentCtx, RpcMeasureConstants.GRPC_SERVER_RECEIVED_MESSAGES_PER_METHOD, 1); } @Override @@ -552,6 +594,8 @@ public final class CensusStatsModule { } else { outboundMessageCount++; } + module.recordRealTimeMetric( + parentCtx, RpcMeasureConstants.GRPC_SERVER_SENT_MESSAGES_PER_METHOD, 1); } /** diff --git a/core/src/test/java/io/grpc/internal/AbstractManagedChannelImplBuilderTest.java b/core/src/test/java/io/grpc/internal/AbstractManagedChannelImplBuilderTest.java index f88797cfa8..d9555c9227 100644 --- a/core/src/test/java/io/grpc/internal/AbstractManagedChannelImplBuilderTest.java +++ b/core/src/test/java/io/grpc/internal/AbstractManagedChannelImplBuilderTest.java @@ -414,7 +414,7 @@ public class AbstractManagedChannelImplBuilderTest { new FakeTagContextBinarySerializer(), new FakeStatsRecorder(), GrpcUtil.STOPWATCH_SUPPLIER, - true, true, true)); + true, true, true, true)); } Builder(SocketAddress directServerAddress, String authority) { @@ -425,7 +425,7 @@ public class AbstractManagedChannelImplBuilderTest { new FakeTagContextBinarySerializer(), new FakeStatsRecorder(), GrpcUtil.STOPWATCH_SUPPLIER, - true, true, true)); + true, true, true, true)); } @Override diff --git a/core/src/test/java/io/grpc/internal/AbstractServerImplBuilderTest.java b/core/src/test/java/io/grpc/internal/AbstractServerImplBuilderTest.java index e0fd106d8d..79b9e4cf31 100644 --- a/core/src/test/java/io/grpc/internal/AbstractServerImplBuilderTest.java +++ b/core/src/test/java/io/grpc/internal/AbstractServerImplBuilderTest.java @@ -91,7 +91,7 @@ public class AbstractServerImplBuilderTest { new FakeTagContextBinarySerializer(), new FakeStatsRecorder(), GrpcUtil.STOPWATCH_SUPPLIER, - true, true, true)); + true, true, true, true)); } @Override diff --git a/core/src/test/java/io/grpc/internal/CensusModulesTest.java b/core/src/test/java/io/grpc/internal/CensusModulesTest.java index 1b14363f6e..8f084590a0 100644 --- a/core/src/test/java/io/grpc/internal/CensusModulesTest.java +++ b/core/src/test/java/io/grpc/internal/CensusModulesTest.java @@ -63,6 +63,8 @@ import io.grpc.internal.testing.StatsTestUtils.FakeTagContextBinarySerializer; import io.grpc.internal.testing.StatsTestUtils.FakeTagger; import io.grpc.internal.testing.StatsTestUtils.MockableSpan; import io.grpc.testing.GrpcServerRule; +import io.opencensus.contrib.grpc.metrics.RpcMeasureConstants; +import io.opencensus.stats.Measure; import io.opencensus.tags.TagContext; import io.opencensus.tags.TagValue; import io.opencensus.trace.BlankSpan; @@ -192,7 +194,7 @@ public class CensusModulesTest { censusStats = new CensusStatsModule( tagger, tagCtxSerializer, statsRecorder, fakeClock.getStopwatchSupplier(), - true, true, true); + true, true, true, false /* real-time */); censusTracing = new CensusTracingModule(tracer, mockTracingPropagationHandler); } @@ -334,30 +336,36 @@ public class CensusModulesTest { } @Test - public void clientBasicStatsDefaultContext_startsAndFinishes() { - subtestClientBasicStatsDefaultContext(true, true); + public void clientBasicStatsDefaultContext_starts_finishes_noRealTime() { + subtestClientBasicStatsDefaultContext(true, true, false); } @Test - public void clientBasicStatsDefaultContext_startsOnly() { - subtestClientBasicStatsDefaultContext(true, false); + public void clientBasicStatsDefaultContext_starts_noFinishes_noRealTime() { + subtestClientBasicStatsDefaultContext(true, false, false); } @Test - public void clientBasicStatsDefaultContext_finishesOnly() { - subtestClientBasicStatsDefaultContext(false, true); + public void clientBasicStatsDefaultContext_noStarts_finishes_noRealTime() { + subtestClientBasicStatsDefaultContext(false, true, false); } @Test - public void clientBasicStatsDefaultContext_neither() { - subtestClientBasicStatsDefaultContext(false, true); + public void clientBasicStatsDefaultContext_noStarts_noFinishes_noRealTime() { + subtestClientBasicStatsDefaultContext(false, false, false); } - private void subtestClientBasicStatsDefaultContext(boolean recordStarts, boolean recordFinishes) { + @Test + public void clientBasicStatsDefaultContext_starts_finishes_realTime() { + subtestClientBasicStatsDefaultContext(true, true, true); + } + + private void subtestClientBasicStatsDefaultContext( + boolean recordStarts, boolean recordFinishes, boolean recordRealTime) { CensusStatsModule localCensusStats = new CensusStatsModule( tagger, tagCtxSerializer, statsRecorder, fakeClock.getStopwatchSupplier(), - true, recordStarts, recordFinishes); + true, recordStarts, recordFinishes, recordRealTime); CensusStatsModule.ClientCallTracer callTracer = localCensusStats.newClientCallTracer( tagger.empty(), method.getFullMethodName()); @@ -381,21 +389,48 @@ public class CensusModulesTest { tracer.outboundHeaders(); fakeClock.forwardTime(100, MILLISECONDS); + tracer.outboundMessage(0); + assertRealTimeMetric( + RpcMeasureConstants.GRPC_CLIENT_SENT_MESSAGES_PER_METHOD, 1, recordRealTime, true); + tracer.outboundWireSize(1028); + assertRealTimeMetric( + RpcMeasureConstants.GRPC_CLIENT_SENT_BYTES_PER_METHOD, 1028, recordRealTime, true); + tracer.outboundUncompressedSize(1128); fakeClock.forwardTime(16, MILLISECONDS); + tracer.inboundMessage(0); + assertRealTimeMetric( + RpcMeasureConstants.GRPC_CLIENT_RECEIVED_MESSAGES_PER_METHOD, 1, recordRealTime, true); + tracer.inboundWireSize(33); + assertRealTimeMetric( + RpcMeasureConstants.GRPC_CLIENT_RECEIVED_BYTES_PER_METHOD, 33, recordRealTime, true); + tracer.inboundUncompressedSize(67); + tracer.outboundMessage(1); + assertRealTimeMetric( + RpcMeasureConstants.GRPC_CLIENT_SENT_MESSAGES_PER_METHOD, 1, recordRealTime, true); + tracer.outboundWireSize(99); + assertRealTimeMetric( + RpcMeasureConstants.GRPC_CLIENT_SENT_BYTES_PER_METHOD, 99, recordRealTime, true); + tracer.outboundUncompressedSize(865); fakeClock.forwardTime(24, MILLISECONDS); tracer.inboundMessage(1); + assertRealTimeMetric( + RpcMeasureConstants.GRPC_CLIENT_RECEIVED_MESSAGES_PER_METHOD, 1, recordRealTime, true); + tracer.inboundWireSize(154); + assertRealTimeMetric( + RpcMeasureConstants.GRPC_CLIENT_RECEIVED_BYTES_PER_METHOD, 154, recordRealTime, true); + tracer.inboundUncompressedSize(552); tracer.streamClosed(Status.OK); callTracer.callEnded(Status.OK); @@ -436,6 +471,24 @@ public class CensusModulesTest { } } + private void assertRealTimeMetric( + Measure measure, long expectedValue, boolean recordRealTimeMetrics, boolean clientSide) { + StatsTestUtils.MetricsRecord record = statsRecorder.pollRecord(); + if (!recordRealTimeMetrics) { + assertNull(record); + return; + } + assertNotNull(record); + if (clientSide) { + assertNoServerContent(record); + } else { + assertNoClientContent(record); + } + TagValue methodTagOld = record.tags.get(DeprecatedCensusConstants.RPC_METHOD); + assertEquals(method.getFullMethodName(), methodTagOld.asString()); + assertEquals(expectedValue, record.getMetricAsLongOrFail(measure)); + } + @Test public void clientBasicTracingDefaultSpan() { CensusTracingModule.ClientCallTracer callTracer = @@ -597,7 +650,7 @@ public class CensusModulesTest { tagCtxSerializer, statsRecorder, fakeClock.getStopwatchSupplier(), - propagate, recordStats, recordStats); + propagate, recordStats, recordStats, recordStats); Metadata headers = new Metadata(); CensusStatsModule.ClientCallTracer callTracer = census.newClientCallTracer(clientCtx, method.getFullMethodName()); @@ -813,30 +866,36 @@ public class CensusModulesTest { } @Test - public void serverBasicStatsNoHeaders_startsAndFinishes() { - subtestServerBasicStatsNoHeaders(true, true); + public void serverBasicStatsNoHeaders_starts_finishes_noRealTime() { + subtestServerBasicStatsNoHeaders(true, true, false); } @Test - public void serverBasicStatsNoHeaders_startsOnly() { - subtestServerBasicStatsNoHeaders(true, false); + public void serverBasicStatsNoHeaders_starts_noFinishes_noRealTime() { + subtestServerBasicStatsNoHeaders(true, false, false); } @Test - public void serverBasicStatsNoHeaders_finishesOnly() { - subtestServerBasicStatsNoHeaders(false, true); + public void serverBasicStatsNoHeaders_noStarts_finishes_noRealTime() { + subtestServerBasicStatsNoHeaders(false, true, false); } @Test - public void serverBasicStatsNoHeaders_neither() { - subtestServerBasicStatsNoHeaders(false, false); + public void serverBasicStatsNoHeaders_noStarts_noFinishes_noRealTime() { + subtestServerBasicStatsNoHeaders(false, false, false); } - private void subtestServerBasicStatsNoHeaders(boolean recordStarts, boolean recordFinishes) { + @Test + public void serverBasicStatsNoHeaders_starts_finishes_realTime() { + subtestServerBasicStatsNoHeaders(true, true, true); + } + + private void subtestServerBasicStatsNoHeaders( + boolean recordStarts, boolean recordFinishes, boolean recordRealTime) { CensusStatsModule localCensusStats = new CensusStatsModule( tagger, tagCtxSerializer, statsRecorder, fakeClock.getStopwatchSupplier(), - true, recordStarts, recordFinishes); + true, recordStarts, recordFinishes, recordRealTime); ServerStreamTracer.Factory tracerFactory = localCensusStats.getServerTracerFactory(); ServerStreamTracer tracer = tracerFactory.newServerStreamTracer(method.getFullMethodName(), new Metadata()); @@ -867,20 +926,44 @@ public class CensusModulesTest { statsCtx); tracer.inboundMessage(0); + assertRealTimeMetric( + RpcMeasureConstants.GRPC_SERVER_RECEIVED_MESSAGES_PER_METHOD, 1, recordRealTime, false); + tracer.inboundWireSize(34); + assertRealTimeMetric( + RpcMeasureConstants.GRPC_SERVER_RECEIVED_BYTES_PER_METHOD, 34, recordRealTime, false); + tracer.inboundUncompressedSize(67); fakeClock.forwardTime(100, MILLISECONDS); tracer.outboundMessage(0); + assertRealTimeMetric( + RpcMeasureConstants.GRPC_SERVER_SENT_MESSAGES_PER_METHOD, 1, recordRealTime, false); + tracer.outboundWireSize(1028); + assertRealTimeMetric( + RpcMeasureConstants.GRPC_SERVER_SENT_BYTES_PER_METHOD, 1028, recordRealTime, false); + tracer.outboundUncompressedSize(1128); fakeClock.forwardTime(16, MILLISECONDS); tracer.inboundMessage(1); + assertRealTimeMetric( + RpcMeasureConstants.GRPC_SERVER_RECEIVED_MESSAGES_PER_METHOD, 1, recordRealTime, false); + tracer.inboundWireSize(154); + assertRealTimeMetric( + RpcMeasureConstants.GRPC_SERVER_RECEIVED_BYTES_PER_METHOD, 154, recordRealTime, false); + tracer.inboundUncompressedSize(552); tracer.outboundMessage(1); + assertRealTimeMetric( + RpcMeasureConstants.GRPC_SERVER_SENT_MESSAGES_PER_METHOD, 1, recordRealTime, false); + tracer.outboundWireSize(99); + assertRealTimeMetric( + RpcMeasureConstants.GRPC_SERVER_SENT_BYTES_PER_METHOD, 99, recordRealTime, false); + tracer.outboundUncompressedSize(865); fakeClock.forwardTime(24, MILLISECONDS); diff --git a/interop-testing/src/main/java/io/grpc/testing/integration/AbstractInteropTest.java b/interop-testing/src/main/java/io/grpc/testing/integration/AbstractInteropTest.java index 17cb821c0f..a50dda5a86 100644 --- a/interop-testing/src/main/java/io/grpc/testing/integration/AbstractInteropTest.java +++ b/interop-testing/src/main/java/io/grpc/testing/integration/AbstractInteropTest.java @@ -228,7 +228,7 @@ public abstract class AbstractInteropTest { tagContextBinarySerializer, serverStatsRecorder, GrpcUtil.STOPWATCH_SUPPLIER, - true, true, true)); + true, true, true, false /* real-time metrics */)); try { server = builder.build().start(); } catch (IOException ex) { @@ -331,7 +331,7 @@ public abstract class AbstractInteropTest { protected final CensusStatsModule createClientCensusStatsModule() { return new CensusStatsModule( tagger, tagContextBinarySerializer, clientStatsRecorder, GrpcUtil.STOPWATCH_SUPPLIER, - true, true, true); + true, true, true, false /* real-time metrics */); } /** diff --git a/netty/src/main/java/io/grpc/netty/InternalNettyChannelBuilder.java b/netty/src/main/java/io/grpc/netty/InternalNettyChannelBuilder.java index bbceb364ee..0bbaed0118 100644 --- a/netty/src/main/java/io/grpc/netty/InternalNettyChannelBuilder.java +++ b/netty/src/main/java/io/grpc/netty/InternalNettyChannelBuilder.java @@ -62,6 +62,10 @@ public final class InternalNettyChannelBuilder { builder.setStatsRecordStartedRpcs(value); } + public static void setStatsRecordRealTimeMetrics(NettyChannelBuilder builder, boolean value) { + builder.setStatsRecordRealTimeMetrics(value); + } + public static ClientTransportFactory buildTransportFactory(NettyChannelBuilder builder) { return builder.buildTransportFactory(); } diff --git a/netty/src/main/java/io/grpc/netty/InternalNettyServerBuilder.java b/netty/src/main/java/io/grpc/netty/InternalNettyServerBuilder.java index 0637b8e7aa..48912f410b 100644 --- a/netty/src/main/java/io/grpc/netty/InternalNettyServerBuilder.java +++ b/netty/src/main/java/io/grpc/netty/InternalNettyServerBuilder.java @@ -33,6 +33,10 @@ public final class InternalNettyServerBuilder { builder.setStatsRecordStartedRpcs(value); } + public static void setStatsRecordRealTimeMetrics(NettyServerBuilder builder, boolean value) { + builder.setStatsRecordRealTimeMetrics(value); + } + public static void setTracingEnabled(NettyServerBuilder builder, boolean value) { builder.setTracingEnabled(value); } diff --git a/netty/src/main/java/io/grpc/netty/NettyChannelBuilder.java b/netty/src/main/java/io/grpc/netty/NettyChannelBuilder.java index a9c44e62ea..569568d406 100644 --- a/netty/src/main/java/io/grpc/netty/NettyChannelBuilder.java +++ b/netty/src/main/java/io/grpc/netty/NettyChannelBuilder.java @@ -471,6 +471,11 @@ public final class NettyChannelBuilder super.setStatsRecordStartedRpcs(value); } + @Override + protected void setStatsRecordRealTimeMetrics(boolean value) { + super.setStatsRecordRealTimeMetrics(value); + } + @VisibleForTesting NettyChannelBuilder setTransportTracerFactory(TransportTracer.Factory transportTracerFactory) { this.transportTracerFactory = transportTracerFactory; diff --git a/netty/src/main/java/io/grpc/netty/NettyServerBuilder.java b/netty/src/main/java/io/grpc/netty/NettyServerBuilder.java index cae6dadfc0..0a408fb9f4 100644 --- a/netty/src/main/java/io/grpc/netty/NettyServerBuilder.java +++ b/netty/src/main/java/io/grpc/netty/NettyServerBuilder.java @@ -228,6 +228,11 @@ public final class NettyServerBuilder extends AbstractServerImplBuilder