Skip to content

Commit 03092a1

Browse files
committed
gRPC: allow to customize server and channel
- fix #3912
1 parent f18e69e commit 03092a1

2 files changed

Lines changed: 83 additions & 1 deletion

File tree

docs/asciidoc/modules/gRPC.adoc

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,52 @@ import io.jooby.kt.Kooby
4444
<1> Enable HTTP/2 on your server.
4545
<2> Install the module and explicitly register your services.
4646

47+
=== Configuration
48+
49+
You can customize the underlying `InProcessServerBuilder` and `InProcessChannelBuilder` used by the module to apply advanced gRPC configurations. This is particularly useful for registering global interceptors (like OpenTelemetry traces), adjusting payload limits, or tweaking executor settings.
50+
51+
Use the `withServer` and `withChannel` callbacks to hook directly into the builders before the server starts:
52+
53+
[source, java, role="primary"]
54+
.Java
55+
----
56+
import io.jooby.Jooby;
57+
import io.jooby.grpc.GrpcModule;
58+
59+
{
60+
install(new GrpcModule(new GreeterService())
61+
.withServer(server -> { // <1>
62+
server.maxInboundMessageSize(1024 * 1024 * 20); // 20MB limit
63+
})
64+
.withChannel(channel -> { // <2>
65+
channel.intercept(new MyCustomClientInterceptor());
66+
})
67+
);
68+
}
69+
----
70+
71+
[source, kotlin, role="secondary"]
72+
.Kotlin
73+
----
74+
import io.jooby.grpc.GrpcModule
75+
import io.jooby.kt.Kooby
76+
77+
{
78+
install(GrpcModule(GreeterService())
79+
.withServer { server -> // <1>
80+
server.maxInboundMessageSize(1024 * 1024 * 20) // 20MB limit
81+
}
82+
.withChannel { channel -> // <2>
83+
channel.intercept(MyCustomClientInterceptor())
84+
}
85+
)
86+
}
87+
----
88+
<1> Customize the internal gRPC server (e.g., adjust max message sizes, add server-side interceptors).
89+
<2> Customize the internal loopback channel (e.g., add client-side interceptors for context propagation).
90+
91+
NOTE: **Size Limits:** By default, Jooby automatically sets the gRPC server's `maxInboundMessageSize` and `maxInboundMetadataSize` to match your web server's `server.maxRequestSize` property (which defaults to `10mb`). If you manually increase these limits on the gRPC server builder, you **must** also increase `server.maxRequestSize`. If an incoming gRPC payload or metadata exceeds the configured web server limit, the request will be rejected before it ever reaches the gRPC layer.
92+
4793
=== Dependency Injection
4894

4995
If your gRPC services require external dependencies (like database repositories), you can register the service classes instead of pre-instantiated objects. The module will automatically provision them using your active Dependency Injection framework (e.g., Guice, Spring).

modules/jooby-grpc/src/main/java/io/jooby/grpc/GrpcModule.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@
7373
public class GrpcModule implements Extension {
7474
private final List<BindableService> services = new ArrayList<>();
7575
private final List<Class<? extends BindableService>> serviceClasses = new ArrayList<>();
76+
private SneakyThrows.Consumer<InProcessServerBuilder> serverCustomizer;
77+
private SneakyThrows.Consumer<InProcessChannelBuilder> channelCustomizer;
7678

7779
static {
7880
// Optionally remove existing handlers attached to the j.u.l root logger
@@ -111,6 +113,30 @@ public final GrpcModule bind(Class<? extends BindableService>... serviceClasses)
111113
return this;
112114
}
113115

116+
/**
117+
* Customizes the in-process gRPC server using the provided server customizer. This method accepts
118+
* a consumer that applies custom settings to an {@code InProcessServerBuilder} instance.
119+
*
120+
* @param serverCustomizer a consumer to customize the {@code InProcessServerBuilder}.
121+
* @return this {@code GrpcModule} instance for method chaining.
122+
*/
123+
public GrpcModule withServer(SneakyThrows.Consumer<InProcessServerBuilder> serverCustomizer) {
124+
this.serverCustomizer = serverCustomizer;
125+
return this;
126+
}
127+
128+
/**
129+
* Configures the gRPC channel using a consumer that applies custom settings to an {@code
130+
* InProcessChannelBuilder} instance.
131+
*
132+
* @param channelConsumer a consumer to customize the {@code InProcessChannelBuilder}.
133+
* @return this {@code GrpcModule} instance for method chaining.
134+
*/
135+
public GrpcModule withChannel(SneakyThrows.Consumer<InProcessChannelBuilder> channelConsumer) {
136+
this.channelCustomizer = channelConsumer;
137+
return this;
138+
}
139+
114140
/**
115141
* Installs the gRPC extension into the Jooby application. *
116142
*
@@ -142,12 +168,22 @@ public void install(Jooby app) throws Exception {
142168
var service = app.require(serviceClass);
143169
bindService(app, builder, registry, service);
144170
}
171+
// Sync both
172+
builder.maxInboundMessageSize(app.getServerOptions().getMaxRequestSize());
173+
builder.maxInboundMetadataSize(app.getServerOptions().getMaxRequestSize());
174+
if (serverCustomizer != null) {
175+
serverCustomizer.accept(builder);
176+
}
145177
var grpcServer = builder.build().start();
146178

147179
// KEEP .directExecutor() here!
148180
// This ensures that when the background gRPC worker finishes, it instantly pushes
149181
// the response back to Undertow/Netty without wasting time on another thread hop.
150-
var channel = InProcessChannelBuilder.forName(serverName).directExecutor().build();
182+
var channelBuilder = InProcessChannelBuilder.forName(serverName).directExecutor();
183+
if (channelCustomizer != null) {
184+
channelCustomizer.accept(channelBuilder);
185+
}
186+
var channel = channelBuilder.build();
151187
processor.setChannel(channel);
152188

153189
app.onStop(channel::shutdownNow);

0 commit comments

Comments
 (0)