@@ -25,6 +25,7 @@ import io.grpc.ServerBuilder;
|
||||
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.grpc.server.DefaultGrpcServerFactory;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
|
||||
/**
|
||||
* Helper class used to map {@link GrpcServerProperties} to
|
||||
@@ -46,10 +47,39 @@ class DefaultServerFactoryPropertyMapper<T extends ServerBuilder<T>> {
|
||||
* @param serverBuilder the builder
|
||||
*/
|
||||
void customizeServerBuilder(T serverBuilder) {
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
GrpcServerProperties.KeepAlive keepAlive = this.properties.getKeepAlive();
|
||||
map.from(keepAlive.getTime()).to(durationProperty(serverBuilder::keepAliveTime));
|
||||
map.from(keepAlive.getTimeout()).to(durationProperty(serverBuilder::keepAliveTimeout));
|
||||
PropertyMapper mapper = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
customizeKeepAlive(serverBuilder, mapper);
|
||||
customizeInboundLimits(serverBuilder, mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the keep-alive properties to the server factory's server builder.
|
||||
* @param serverBuilder the builder
|
||||
* @param mapper the property mapper
|
||||
*/
|
||||
void customizeKeepAlive(T serverBuilder, PropertyMapper mapper) {
|
||||
GrpcServerProperties.KeepAlive keepAliveProps = this.properties.getKeepAlive();
|
||||
mapper.from(keepAliveProps.getTime()).to(durationProperty(serverBuilder::keepAliveTime));
|
||||
mapper.from(keepAliveProps.getTimeout()).to(durationProperty(serverBuilder::keepAliveTimeout));
|
||||
mapper.from(keepAliveProps.getMaxIdle()).to(durationProperty(serverBuilder::maxConnectionIdle));
|
||||
mapper.from(keepAliveProps.getMaxAge()).to(durationProperty(serverBuilder::maxConnectionAge));
|
||||
mapper.from(keepAliveProps.getMaxAgeGrace()).to(durationProperty(serverBuilder::maxConnectionAgeGrace));
|
||||
mapper.from(keepAliveProps.getPermitTime()).to(durationProperty(serverBuilder::permitKeepAliveTime));
|
||||
mapper.from(keepAliveProps.isPermitWithoutCalls()).to(serverBuilder::permitKeepAliveWithoutCalls);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the inbound limits properties to the server factory's server builder.
|
||||
* @param serverBuilder the builder
|
||||
* @param mapper the property mapper
|
||||
*/
|
||||
void customizeInboundLimits(T serverBuilder, PropertyMapper mapper) {
|
||||
mapper.from(properties.getMaxInboundMessageSize())
|
||||
.asInt(DataSize::toBytes)
|
||||
.to(serverBuilder::maxInboundMessageSize);
|
||||
mapper.from(properties.getMaxInboundMetadataSize())
|
||||
.asInt(DataSize::toBytes)
|
||||
.to(serverBuilder::maxInboundMetadataSize);
|
||||
}
|
||||
|
||||
Consumer<Duration> durationProperty(BiConsumer<Long, TimeUnit> setter) {
|
||||
|
||||
@@ -19,7 +19,10 @@ import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.convert.DataSizeUnit;
|
||||
import org.springframework.boot.convert.DurationUnit;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
import org.springframework.util.unit.DataUnit;
|
||||
|
||||
@ConfigurationProperties(prefix = "spring.grpc.server")
|
||||
public class GrpcServerProperties {
|
||||
@@ -29,16 +32,6 @@ public class GrpcServerProperties {
|
||||
*/
|
||||
public static final String ANY_IP_ADDRESS = "*";
|
||||
|
||||
/**
|
||||
* Server should listen to any IPv4 address.
|
||||
*/
|
||||
public static final String ANY_IPv4_ADDRESS = "0.0.0.0";
|
||||
|
||||
/**
|
||||
* Server should listen to any IPv6 address.
|
||||
*/
|
||||
public static final String ANY_IPv6_ADDRESS = "::";
|
||||
|
||||
/**
|
||||
* Server address to bind to. The default is any IP address ('*').
|
||||
*/
|
||||
@@ -46,6 +39,7 @@ public class GrpcServerProperties {
|
||||
|
||||
/**
|
||||
* Server port to listen on. When the value is 0, a random available port is selected.
|
||||
* The default is 9090.
|
||||
*/
|
||||
private int port = 9090;
|
||||
|
||||
@@ -57,10 +51,22 @@ public class GrpcServerProperties {
|
||||
@DurationUnit(ChronoUnit.SECONDS)
|
||||
private Duration shutdownGracePeriod = Duration.of(30, ChronoUnit.SECONDS);
|
||||
|
||||
/**
|
||||
* Maximum message size allowed to be received by the server (default 4MiB).
|
||||
*/
|
||||
@DataSizeUnit(DataUnit.BYTES)
|
||||
private DataSize maxInboundMessageSize = DataSize.ofBytes(4 * 1024 * 1024);
|
||||
|
||||
/**
|
||||
* Maximum metadata size allowed to be received by the server (default 8KiB).
|
||||
*/
|
||||
@DataSizeUnit(DataUnit.BYTES)
|
||||
private DataSize maxInboundMetadataSize = DataSize.ofBytes(8192);
|
||||
|
||||
private final KeepAlive keepAlive = new KeepAlive();
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
return this.address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
@@ -68,7 +74,7 @@ public class GrpcServerProperties {
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
return this.port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
@@ -76,13 +82,29 @@ public class GrpcServerProperties {
|
||||
}
|
||||
|
||||
public Duration getShutdownGracePeriod() {
|
||||
return shutdownGracePeriod;
|
||||
return this.shutdownGracePeriod;
|
||||
}
|
||||
|
||||
public void setShutdownGracePeriod(Duration shutdownGracePeriod) {
|
||||
this.shutdownGracePeriod = shutdownGracePeriod;
|
||||
}
|
||||
|
||||
public DataSize getMaxInboundMessageSize() {
|
||||
return this.maxInboundMessageSize;
|
||||
}
|
||||
|
||||
public void setMaxInboundMessageSize(DataSize maxInboundMessageSize) {
|
||||
this.maxInboundMessageSize = maxInboundMessageSize;
|
||||
}
|
||||
|
||||
public DataSize getMaxInboundMetadataSize() {
|
||||
return this.maxInboundMetadataSize;
|
||||
}
|
||||
|
||||
public void setMaxInboundMetadataSize(DataSize maxInboundMetadataSize) {
|
||||
this.maxInboundMetadataSize = maxInboundMetadataSize;
|
||||
}
|
||||
|
||||
public KeepAlive getKeepAlive() {
|
||||
return this.keepAlive;
|
||||
}
|
||||
@@ -103,8 +125,40 @@ public class GrpcServerProperties {
|
||||
@DurationUnit(ChronoUnit.SECONDS)
|
||||
private Duration timeout = Duration.of(20, ChronoUnit.SECONDS);
|
||||
|
||||
/**
|
||||
* Maximum time a connection can remain idle before being gracefully terminated
|
||||
* (default infinite).
|
||||
*/
|
||||
@DurationUnit(ChronoUnit.SECONDS)
|
||||
private Duration maxIdle = null;
|
||||
|
||||
/**
|
||||
* Maximum time a connection may exist before being gracefully terminated (default
|
||||
* infinite).
|
||||
*/
|
||||
@DurationUnit(ChronoUnit.SECONDS)
|
||||
private Duration maxAge = null;
|
||||
|
||||
/**
|
||||
* Maximum time for graceful connection termination (default infinite).
|
||||
*/
|
||||
@DurationUnit(ChronoUnit.SECONDS)
|
||||
private Duration maxAgeGrace = null;
|
||||
|
||||
/**
|
||||
* Maximum keep-alive time clients are permitted to configure (default 5m).
|
||||
*/
|
||||
@DurationUnit(ChronoUnit.SECONDS)
|
||||
private Duration permitTime = Duration.of(5, ChronoUnit.MINUTES);
|
||||
|
||||
/**
|
||||
* Whether clients are permitted to send keep alive pings when there are no
|
||||
* outstanding RPCs on the connection (default false).
|
||||
*/
|
||||
private boolean permitWithoutCalls = false;
|
||||
|
||||
public Duration getTime() {
|
||||
return time;
|
||||
return this.time;
|
||||
}
|
||||
|
||||
public void setTime(Duration time) {
|
||||
@@ -112,13 +166,53 @@ public class GrpcServerProperties {
|
||||
}
|
||||
|
||||
public Duration getTimeout() {
|
||||
return timeout;
|
||||
return this.timeout;
|
||||
}
|
||||
|
||||
public void setTimeout(Duration timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public Duration getMaxIdle() {
|
||||
return this.maxIdle;
|
||||
}
|
||||
|
||||
public void setMaxIdle(Duration maxIdle) {
|
||||
this.maxIdle = maxIdle;
|
||||
}
|
||||
|
||||
public Duration getMaxAge() {
|
||||
return this.maxAge;
|
||||
}
|
||||
|
||||
public void setMaxAge(Duration maxAge) {
|
||||
this.maxAge = maxAge;
|
||||
}
|
||||
|
||||
public Duration getMaxAgeGrace() {
|
||||
return this.maxAgeGrace;
|
||||
}
|
||||
|
||||
public void setMaxAgeGrace(Duration maxAgeGrace) {
|
||||
this.maxAgeGrace = maxAgeGrace;
|
||||
}
|
||||
|
||||
public Duration getPermitTime() {
|
||||
return this.permitTime;
|
||||
}
|
||||
|
||||
public void setPermitTime(Duration permitTime) {
|
||||
this.permitTime = permitTime;
|
||||
}
|
||||
|
||||
public boolean isPermitWithoutCalls() {
|
||||
return this.permitWithoutCalls;
|
||||
}
|
||||
|
||||
public void setPermitWithoutCalls(boolean permitWithoutCalls) {
|
||||
this.permitWithoutCalls = permitWithoutCalls;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -49,10 +50,11 @@ class GrpcServerPropertiesTests {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("spring.grpc.server.address", "my-server-ip");
|
||||
map.put("spring.grpc.server.port", "3130");
|
||||
map.put("spring.grpc.server.shutdown-grace-period", "15s");
|
||||
map.put("spring.grpc.server.shutdown-grace-period", "15");
|
||||
GrpcServerProperties properties = bindProperties(map);
|
||||
assertThat(properties.getAddress()).isEqualTo("my-server-ip");
|
||||
assertThat(properties.getPort()).isEqualTo(3130);
|
||||
assertThat(properties.getShutdownGracePeriod()).isEqualTo(Duration.ofSeconds(15));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -65,9 +67,62 @@ class GrpcServerPropertiesTests {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("spring.grpc.server.keep-alive.time", "45m");
|
||||
map.put("spring.grpc.server.keep-alive.timeout", "40s");
|
||||
map.put("spring.grpc.server.keep-alive.max-idle", "1h");
|
||||
map.put("spring.grpc.server.keep-alive.max-age", "3h");
|
||||
map.put("spring.grpc.server.keep-alive.max-age-grace", "21s");
|
||||
map.put("spring.grpc.server.keep-alive.permit-time", "33s");
|
||||
map.put("spring.grpc.server.keep-alive.permit-without-calls", "true");
|
||||
GrpcServerProperties.KeepAlive properties = bindProperties(map).getKeepAlive();
|
||||
assertThatPropertiesSetAsExpected(properties);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindWithoutUnitsSpecified() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("spring.grpc.server.keep-alive.time", "2700");
|
||||
map.put("spring.grpc.server.keep-alive.timeout", "40");
|
||||
map.put("spring.grpc.server.keep-alive.max-idle", "3600");
|
||||
map.put("spring.grpc.server.keep-alive.max-age", "10800");
|
||||
map.put("spring.grpc.server.keep-alive.max-age-grace", "21");
|
||||
map.put("spring.grpc.server.keep-alive.permit-time", "33");
|
||||
map.put("spring.grpc.server.keep-alive.permit-without-calls", "true");
|
||||
GrpcServerProperties.KeepAlive properties = bindProperties(map).getKeepAlive();
|
||||
assertThatPropertiesSetAsExpected(properties);
|
||||
}
|
||||
|
||||
private void assertThatPropertiesSetAsExpected(GrpcServerProperties.KeepAlive properties) {
|
||||
assertThat(properties.getTime()).isEqualTo(Duration.ofMinutes(45));
|
||||
assertThat(properties.getTimeout()).isEqualTo(Duration.ofSeconds(40));
|
||||
assertThat(properties.getMaxIdle()).isEqualTo(Duration.ofHours(1));
|
||||
assertThat(properties.getMaxAge()).isEqualTo(Duration.ofHours(3));
|
||||
assertThat(properties.getMaxAgeGrace()).isEqualTo(Duration.ofSeconds(21));
|
||||
assertThat(properties.getPermitTime()).isEqualTo(Duration.ofSeconds(33));
|
||||
assertThat(properties.isPermitWithoutCalls()).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class InboundLimitsProperties {
|
||||
|
||||
@Test
|
||||
void bind() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("spring.grpc.server.max-inbound-message-size", "20MB");
|
||||
map.put("spring.grpc.server.max-inbound-metadata-size", "1MB");
|
||||
GrpcServerProperties properties = bindProperties(map);
|
||||
assertThat(properties.getMaxInboundMessageSize()).isEqualTo(DataSize.ofMegabytes(20));
|
||||
assertThat(properties.getMaxInboundMetadataSize()).isEqualTo(DataSize.ofMegabytes(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindWithoutUnits() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("spring.grpc.server.max-inbound-message-size", "1048576");
|
||||
map.put("spring.grpc.server.max-inbound-metadata-size", "1024");
|
||||
GrpcServerProperties properties = bindProperties(map);
|
||||
assertThat(properties.getMaxInboundMessageSize()).isEqualTo(DataSize.ofMegabytes(1));
|
||||
assertThat(properties.getMaxInboundMetadataSize()).isEqualTo(DataSize.ofKilobytes(1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ import java.util.function.Supplier;
|
||||
import io.grpc.ServerBuilder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.util.unit.DataSize;
|
||||
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@@ -59,11 +61,25 @@ class ServerFactoryPropertyMappersTests {
|
||||
GrpcServerProperties properties = new GrpcServerProperties();
|
||||
properties.getKeepAlive().setTime(Duration.ofHours(1));
|
||||
properties.getKeepAlive().setTimeout(Duration.ofSeconds(10));
|
||||
properties.getKeepAlive().setMaxIdle(Duration.ofHours(2));
|
||||
properties.getKeepAlive().setMaxAge(Duration.ofHours(3));
|
||||
properties.getKeepAlive().setMaxAgeGrace(Duration.ofSeconds(45));
|
||||
properties.getKeepAlive().setPermitTime(Duration.ofMinutes(7));
|
||||
properties.getKeepAlive().setPermitWithoutCalls(true);
|
||||
properties.setMaxInboundMessageSize(DataSize.ofMegabytes(333));
|
||||
properties.setMaxInboundMetadataSize(DataSize.ofKilobytes(111));
|
||||
X mapper = mapperFactory.apply(properties);
|
||||
T builder = mockBuilderToCustomize.get();
|
||||
mapper.customizeServerBuilder(builder);
|
||||
then(builder).should().keepAliveTime(Duration.ofHours(1).toNanos(), TimeUnit.NANOSECONDS);
|
||||
then(builder).should().keepAliveTimeout(Duration.ofSeconds(10).toNanos(), TimeUnit.NANOSECONDS);
|
||||
then(builder).should().maxConnectionIdle(Duration.ofHours(2).toNanos(), TimeUnit.NANOSECONDS);
|
||||
then(builder).should().maxConnectionAge(Duration.ofHours(3).toNanos(), TimeUnit.NANOSECONDS);
|
||||
then(builder).should().maxConnectionAgeGrace(Duration.ofSeconds(45).toNanos(), TimeUnit.NANOSECONDS);
|
||||
then(builder).should().permitKeepAliveTime(Duration.ofMinutes(7).toNanos(), TimeUnit.NANOSECONDS);
|
||||
then(builder).should().permitKeepAliveWithoutCalls(true);
|
||||
then(builder).should().maxInboundMessageSize(Math.toIntExact(DataSize.ofMegabytes(333).toBytes()));
|
||||
then(builder).should().maxInboundMetadataSize(Math.toIntExact(DataSize.ofKilobytes(111).toBytes()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user