Skip to content

Commit fe94442

Browse files
author
Alexander Furer
committed
fixes #82
1 parent a492780 commit fe94442

File tree

7 files changed

+231
-17
lines changed

7 files changed

+231
-17
lines changed

README.adoc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,29 @@ Default value is `0` (means don't wait).
9494
shutdownGrace: 30
9595
----
9696

97+
* link:grpc-spring-boot-starter/src/main/java/org/lognet/springboot/grpc/autoconfigure/GRpcServerProperties.java[Netty-specific server properties] can be specified under `grpc.netty-server` prefix. +
98+
By configuring one of the `grpc.netty-server.xxxx` values you are implicitly setting transport to be Netty-based.
99+
100+
[source,yaml]
101+
----
102+
grpc:
103+
netty-server:
104+
keep-alive-time: 30s <1>
105+
max-inbound-message-size: 10MB <2>
106+
primary-listen-address: 10.10.15.23:0 <3>
107+
additional-listen-addresses:
108+
- 192.168.0.100:6767 <4>
109+
110+
111+
----
112+
<1> `Duration` type properties can be configured with string value format described https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/DurationStyle.java[here].
113+
<2> `DataSize` type properties can be configured with string value described https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/unit/DataSize.html#parse-java.lang.CharSequence-[here]
114+
<3> Exposed on external network IP with custom port.
115+
`SocketAddress` type properties string value format:
116+
* `host:port` (if `port` value is less than 1, uses random value)
117+
* `host:` (uses default grpc port, `6565` )
118+
<4> Exposed on internal network IP as well with predefined port `6767`.
119+
97120
The starter supports also the `in-process server`, which should be used for testing purposes :
98121

99122
[source,yaml]
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.lognet.springboot.grpc;
2+
3+
import io.grpc.examples.GreeterGrpc;
4+
import org.junit.Assert;
5+
import org.junit.runner.RunWith;
6+
import org.lognet.springboot.grpc.demo.DemoApp;
7+
import org.springframework.boot.test.context.SpringBootTest;
8+
import org.springframework.test.context.ActiveProfiles;
9+
import org.springframework.test.context.junit4.SpringRunner;
10+
11+
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE;
12+
13+
@RunWith(SpringRunner.class)
14+
@SpringBootTest(classes = {DemoApp.class}, webEnvironment = NONE)
15+
@ActiveProfiles("netty-test1")
16+
public class NettyTransportConfigTest1 extends GrpcServerTestBase {
17+
18+
@Override
19+
protected GreeterGrpc.GreeterFutureStub beforeGreeting(GreeterGrpc.GreeterFutureStub stub) {
20+
Assert.assertEquals(getPort(),gRpcServerProperties.getNettyServer().getPrimaryListenAddress().getPort());
21+
return stub;
22+
23+
}
24+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.lognet.springboot.grpc;
2+
3+
import io.grpc.examples.GreeterGrpc;
4+
import org.junit.Assert;
5+
import org.junit.runner.RunWith;
6+
import org.lognet.springboot.grpc.autoconfigure.GRpcServerProperties;
7+
import org.lognet.springboot.grpc.demo.DemoApp;
8+
import org.springframework.boot.test.context.SpringBootTest;
9+
import org.springframework.test.context.ActiveProfiles;
10+
import org.springframework.test.context.junit4.SpringRunner;
11+
12+
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE;
13+
14+
@RunWith(SpringRunner.class)
15+
@SpringBootTest(classes = {DemoApp.class}, webEnvironment = NONE)
16+
@ActiveProfiles("netty-test2")
17+
public class NettyTransportConfigTest2 extends GrpcServerTestBase {
18+
19+
@Override
20+
protected GreeterGrpc.GreeterFutureStub beforeGreeting(GreeterGrpc.GreeterFutureStub stub) {
21+
Assert.assertEquals(getPort(), GRpcServerProperties.DEFAULT_GRPC_PORT);
22+
return stub;
23+
24+
}
25+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
grpc:
2+
netty-server:
3+
keep-alive-time: 30s
4+
max-inbound-message-size: 10MB
5+
primary-listen-address: 10.10.15.23:6767
6+
additional-listen-addresses:
7+
- 192.168.0.100:6767
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
grpc:
2+
netty-server:
3+
primary-listen-address: "localhost:"

grpc-spring-boot-starter/src/main/java/org/lognet/springboot/grpc/autoconfigure/GRpcAutoConfiguration.java

Lines changed: 97 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.grpc.ServerBuilder;
44
import io.grpc.inprocess.InProcessServerBuilder;
5+
import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder;
56
import io.grpc.services.HealthStatusManager;
67
import lombok.extern.slf4j.Slf4j;
78
import org.lognet.springboot.grpc.GRpcServerBuilderConfigurer;
@@ -15,11 +16,16 @@
1516
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
1617
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
1718
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
19+
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
1820
import org.springframework.boot.context.properties.EnableConfigurationProperties;
1921
import org.springframework.context.annotation.Bean;
22+
import org.springframework.core.convert.converter.Converter;
23+
import org.springframework.util.SocketUtils;
2024

2125
import java.io.IOException;
26+
import java.net.InetSocketAddress;
2227
import java.util.Optional;
28+
import java.util.concurrent.TimeUnit;
2329
import java.util.function.Consumer;
2430

2531
/**
@@ -28,7 +34,7 @@
2834

2935
@AutoConfigureOrder
3036
@ConditionalOnBean(annotation = GRpcService.class)
31-
@EnableConfigurationProperties (GRpcServerProperties.class)
37+
@EnableConfigurationProperties(GRpcServerProperties.class)
3238
@Slf4j
3339
public class GRpcAutoConfiguration {
3440

@@ -38,12 +44,63 @@ public class GRpcAutoConfiguration {
3844
@Bean
3945
@ConditionalOnProperty(value = "grpc.enabled", havingValue = "true", matchIfMissing = true)
4046
public GRpcServerRunner grpcServerRunner(@Qualifier("grpcInternalConfigurator") Consumer<ServerBuilder<?>> configurator) {
41-
return new GRpcServerRunner(configurator, ServerBuilder.forPort(grpcServerProperties.getRunningPort()));
47+
ServerBuilder<?> serverBuilder = Optional.ofNullable(grpcServerProperties.getNettyServer())
48+
.<ServerBuilder<?>> map(n->{
49+
final NettyServerBuilder builder = Optional.ofNullable(n.getPrimaryListenAddress())
50+
.map(NettyServerBuilder::forAddress)
51+
.orElse(NettyServerBuilder.forPort(grpcServerProperties.getRunningPort()));
52+
53+
Optional.ofNullable(n.getAdditionalListenAddresses())
54+
.ifPresent(l->l.forEach(builder::addListenAddress));
55+
56+
Optional.ofNullable(n.getFlowControlWindow())
57+
.ifPresent(builder::flowControlWindow);
58+
59+
Optional.ofNullable(n.getInitialFlowControlWindow())
60+
.ifPresent(builder::initialFlowControlWindow);
61+
62+
Optional.ofNullable(n.getKeepAliveTime())
63+
.ifPresent(t->builder.keepAliveTime(t.toMillis(), TimeUnit.MILLISECONDS));
64+
65+
Optional.ofNullable(n.getKeepAliveTimeout())
66+
.ifPresent(t->builder.keepAliveTimeout(t.toMillis(), TimeUnit.MILLISECONDS));
67+
68+
Optional.ofNullable(n.getPermitKeepAliveTime())
69+
.ifPresent(t->builder.permitKeepAliveTime(t.toMillis(), TimeUnit.MILLISECONDS));
70+
71+
72+
Optional.ofNullable(n.getMaxConnectionAge())
73+
.ifPresent(t->builder.maxConnectionAge(t.toMillis(), TimeUnit.MILLISECONDS));
74+
75+
Optional.ofNullable(n.getMaxConnectionAgeGrace())
76+
.ifPresent(t->builder.maxConnectionAgeGrace(t.toMillis(), TimeUnit.MILLISECONDS));
77+
78+
Optional.ofNullable(n.getMaxConnectionIdle())
79+
.ifPresent(t->builder.maxConnectionIdle(t.toMillis(), TimeUnit.MILLISECONDS));
80+
81+
Optional.ofNullable(n.getMaxConcurrentCallsPerConnection())
82+
.ifPresent(builder::maxConcurrentCallsPerConnection);
83+
84+
Optional.ofNullable(n.getPermitKeepAliveWithoutCalls())
85+
.ifPresent(builder::permitKeepAliveWithoutCalls);
86+
87+
Optional.ofNullable(n.getMaxInboundMessageSize())
88+
.ifPresent(s->builder.maxInboundMessageSize((int)s.toBytes()));
89+
90+
Optional.ofNullable(n.getMaxInboundMetadataSize())
91+
.ifPresent(s->builder.maxInboundMetadataSize((int)s.toBytes()));
92+
93+
94+
return builder;
95+
96+
})
97+
.orElse(ServerBuilder.forPort(grpcServerProperties.getRunningPort()));
98+
return new GRpcServerRunner(configurator, serverBuilder);
4299
}
43100

44101
@Bean
45102
@ConditionalOnExpression("#{environment.getProperty('grpc.inProcessServerName','')!=''}")
46-
public GRpcServerRunner grpcInprocessServerRunner(@Qualifier("grpcInternalConfigurator") Consumer<ServerBuilder<?>> configurator){
103+
public GRpcServerRunner grpcInprocessServerRunner(@Qualifier("grpcInternalConfigurator") Consumer<ServerBuilder<?>> configurator) {
47104

48105
return new GRpcServerRunner(configurator, InProcessServerBuilder.forName(grpcServerProperties.getInProcessServerName()));
49106
}
@@ -54,22 +111,22 @@ public HealthStatusManager healthStatusManager() {
54111
}
55112

56113
@Bean
57-
@ConditionalOnMissingBean( GRpcServerBuilderConfigurer.class)
58-
public GRpcServerBuilderConfigurer serverBuilderConfigurer(){
114+
@ConditionalOnMissingBean(GRpcServerBuilderConfigurer.class)
115+
public GRpcServerBuilderConfigurer serverBuilderConfigurer() {
59116
return new GRpcServerBuilderConfigurer();
60117
}
61118

62119
@Bean(name = "grpcInternalConfigurator")
63-
public Consumer<ServerBuilder<?>> configurator(GRpcServerBuilderConfigurer configurer){
120+
public Consumer<ServerBuilder<?>> configurator(GRpcServerBuilderConfigurer configurer) {
64121
return serverBuilder -> {
65-
if(grpcServerProperties.isEnabled()){
122+
if (grpcServerProperties.isEnabled()) {
66123
Optional.ofNullable(grpcServerProperties.getSecurity())
67-
.ifPresent(s->{
124+
.ifPresent(s -> {
68125
boolean setupSecurity = Optional.ofNullable(s.getCertChain()).isPresent();
69-
if(setupSecurity != Optional.ofNullable(s.getPrivateKey()).isPresent() ){
70-
throw new BeanCreationException("Both gRPC TLS 'certChain' and 'privateKey' should be configured. One of them is null. ");
126+
if (setupSecurity != Optional.ofNullable(s.getPrivateKey()).isPresent()) {
127+
throw new BeanCreationException("Both gRPC TLS 'certChain' and 'privateKey' should be configured. One of them is null. ");
71128
}
72-
if(setupSecurity) {
129+
if (setupSecurity) {
73130
try {
74131
serverBuilder.useTransportSecurity(s.getCertChain().getInputStream(),
75132
s.getPrivateKey().getInputStream()
@@ -84,4 +141,33 @@ public Consumer<ServerBuilder<?>> configurator(GRpcServerBuilderConfigurer confi
84141
};
85142
}
86143

144+
@Bean
145+
@ConfigurationPropertiesBinding
146+
public static Converter<String, InetSocketAddress> socketAddressConverter() {
147+
return new Converter<String, InetSocketAddress>() {
148+
@Override
149+
public InetSocketAddress convert(String source) {
150+
final String[] chunks = source.split(":");
151+
int port;
152+
switch (chunks.length) {
153+
case 1:
154+
port = GRpcServerProperties.DEFAULT_GRPC_PORT;
155+
break;
156+
case 2:
157+
port = Integer.parseInt(chunks[1]);
158+
if(port<1){
159+
port = SocketUtils.findAvailableTcpPort();
160+
}
161+
break;
162+
default:
163+
throw new IllegalArgumentException(source +" can't be converted to socket address");
164+
165+
}
166+
167+
return new InetSocketAddress(chunks[0], port);
168+
}
169+
};
170+
}
171+
172+
87173
}

grpc-spring-boot-starter/src/main/java/org/lognet/springboot/grpc/autoconfigure/GRpcServerProperties.java

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,31 @@
66
import org.springframework.boot.context.properties.ConfigurationProperties;
77
import org.springframework.core.io.Resource;
88
import org.springframework.util.SocketUtils;
9+
import org.springframework.util.unit.DataSize;
910

11+
import javax.annotation.PostConstruct;
12+
import java.net.InetSocketAddress;
13+
import java.time.Duration;
14+
import java.util.List;
1015
import java.util.Optional;
1116

1217
/**
1318
* Created by alexf on 26-Jan-16.
1419
*/
1520

1621
@ConfigurationProperties("grpc")
17-
@Getter @Setter
22+
@Getter
23+
@Setter
1824
public class GRpcServerProperties {
1925
public static final int DEFAULT_GRPC_PORT = 6565;
2026
/**
2127
* gRPC server port
22-
*
2328
*/
2429
private Integer port = null;
2530

26-
private SecurityProperties security ;
31+
private SecurityProperties security;
32+
33+
private NettyServerProperties nettyServer;
2734

2835
@Setter(AccessLevel.NONE)
2936
@Getter(AccessLevel.NONE)
@@ -38,7 +45,6 @@ public class GRpcServerProperties {
3845
/**
3946
* In process server name.
4047
* If the value is not empty, the embedded in-process server will be created and started.
41-
*
4248
*/
4349
private String inProcessServerName;
4450

@@ -68,9 +74,49 @@ public Integer getRunningPort() {
6874

6975
}
7076

71-
@Getter @Setter
72-
public static class SecurityProperties{
77+
@Getter
78+
@Setter
79+
public static class SecurityProperties {
7380
private Resource certChain;
7481
private Resource privateKey;
7582
}
83+
84+
@Getter
85+
@Setter
86+
public static class NettyServerProperties {
87+
private Integer flowControlWindow;
88+
private Integer initialFlowControlWindow;
89+
90+
private Integer maxConcurrentCallsPerConnection;
91+
92+
private Duration keepAliveTime;
93+
private Duration keepAliveTimeout;
94+
95+
private Duration maxConnectionAge;
96+
private Duration maxConnectionAgeGrace;
97+
private Duration maxConnectionIdle;
98+
private Duration permitKeepAliveTime;
99+
100+
private DataSize maxInboundMessageSize;
101+
private DataSize maxInboundMetadataSize;
102+
103+
private Boolean permitKeepAliveWithoutCalls;
104+
/**
105+
* grpc listen address. <P>If configured, takes precedence over {@code grpc.port} property value.</p>
106+
* Supported format:
107+
* <li>{@code host:port} (if port is less than 1, uses random value)
108+
* <li>{@code host:} (uses default grpc port, 6565 )
109+
*/
110+
private InetSocketAddress primaryListenAddress;
111+
112+
private List<InetSocketAddress> additionalListenAddresses;
113+
}
114+
115+
@PostConstruct
116+
public void init(){
117+
Optional.ofNullable(nettyServer)
118+
.map(NettyServerProperties::getPrimaryListenAddress)
119+
.ifPresent(a->port = a.getPort());
120+
121+
}
76122
}

0 commit comments

Comments
 (0)