mirror of https://github.com/grpc/grpc-java.git
core: support default method config in service config (#6987)
References: service config spec change: grpc/grpc-proto#75 c-core implementation grpc/grpc#22232
This commit is contained in:
parent
d605faafc3
commit
e78d1c95ef
|
|
@ -37,6 +37,8 @@ import javax.annotation.Nullable;
|
|||
*/
|
||||
final class ManagedChannelServiceConfig {
|
||||
|
||||
@Nullable
|
||||
private final MethodInfo defaultMethodConfig;
|
||||
private final Map<String, MethodInfo> serviceMethodMap;
|
||||
private final Map<String, MethodInfo> serviceMap;
|
||||
@Nullable
|
||||
|
|
@ -47,11 +49,13 @@ final class ManagedChannelServiceConfig {
|
|||
private final Map<String, ?> healthCheckingConfig;
|
||||
|
||||
ManagedChannelServiceConfig(
|
||||
@Nullable MethodInfo defaultMethodConfig,
|
||||
Map<String, MethodInfo> serviceMethodMap,
|
||||
Map<String, MethodInfo> serviceMap,
|
||||
@Nullable Throttle retryThrottling,
|
||||
@Nullable Object loadBalancingConfig,
|
||||
@Nullable Map<String, ?> healthCheckingConfig) {
|
||||
this.defaultMethodConfig = defaultMethodConfig;
|
||||
this.serviceMethodMap = Collections.unmodifiableMap(new HashMap<>(serviceMethodMap));
|
||||
this.serviceMap = Collections.unmodifiableMap(new HashMap<>(serviceMap));
|
||||
this.retryThrottling = retryThrottling;
|
||||
|
|
@ -66,6 +70,7 @@ final class ManagedChannelServiceConfig {
|
|||
static ManagedChannelServiceConfig empty() {
|
||||
return
|
||||
new ManagedChannelServiceConfig(
|
||||
null,
|
||||
new HashMap<String, MethodInfo>(),
|
||||
new HashMap<String, MethodInfo>(),
|
||||
/* retryThrottling= */ null,
|
||||
|
|
@ -100,6 +105,7 @@ final class ManagedChannelServiceConfig {
|
|||
// this is surprising, but possible.
|
||||
return
|
||||
new ManagedChannelServiceConfig(
|
||||
null,
|
||||
serviceMethodMap,
|
||||
serviceMap,
|
||||
retryThrottling,
|
||||
|
|
@ -107,6 +113,7 @@ final class ManagedChannelServiceConfig {
|
|||
healthCheckingConfig);
|
||||
}
|
||||
|
||||
MethodInfo defaultMethodConfig = null;
|
||||
for (Map<String, ?> methodConfig : methodConfigs) {
|
||||
MethodInfo info = new MethodInfo(
|
||||
methodConfig, retryEnabled, maxRetryAttemptsLimit, maxHedgedAttemptsLimit);
|
||||
|
|
@ -114,13 +121,21 @@ final class ManagedChannelServiceConfig {
|
|||
List<Map<String, ?>> nameList =
|
||||
ServiceConfigUtil.getNameListFromMethodConfig(methodConfig);
|
||||
|
||||
checkArgument(
|
||||
nameList != null && !nameList.isEmpty(), "no names in method config %s", methodConfig);
|
||||
if (nameList == null || nameList.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
for (Map<String, ?> name : nameList) {
|
||||
String serviceName = ServiceConfigUtil.getServiceFromName(name);
|
||||
checkArgument(!Strings.isNullOrEmpty(serviceName), "missing service name");
|
||||
String methodName = ServiceConfigUtil.getMethodFromName(name);
|
||||
if (Strings.isNullOrEmpty(methodName)) {
|
||||
if (Strings.isNullOrEmpty(serviceName)) {
|
||||
checkArgument(
|
||||
Strings.isNullOrEmpty(methodName), "missing service name for method %s", methodName);
|
||||
checkArgument(
|
||||
defaultMethodConfig == null,
|
||||
"Duplicate default method config in service config %s",
|
||||
serviceConfig);
|
||||
defaultMethodConfig = info;
|
||||
} else if (Strings.isNullOrEmpty(methodName)) {
|
||||
// Service scoped config
|
||||
checkArgument(
|
||||
!serviceMap.containsKey(serviceName), "Duplicate service %s", serviceName);
|
||||
|
|
@ -139,6 +154,7 @@ final class ManagedChannelServiceConfig {
|
|||
|
||||
return
|
||||
new ManagedChannelServiceConfig(
|
||||
defaultMethodConfig,
|
||||
serviceMethodMap,
|
||||
serviceMap,
|
||||
retryThrottling,
|
||||
|
|
@ -165,6 +181,11 @@ final class ManagedChannelServiceConfig {
|
|||
return serviceMethodMap;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
MethodInfo getDefaultMethodConfig() {
|
||||
return defaultMethodConfig;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
Object getLoadBalancingConfig() {
|
||||
|
|
|
|||
|
|
@ -174,14 +174,18 @@ final class ServiceConfigInterceptor implements ClientInterceptor {
|
|||
@CheckForNull
|
||||
private MethodInfo getMethodInfo(MethodDescriptor<?, ?> method) {
|
||||
ManagedChannelServiceConfig mcsc = managedChannelServiceConfig.get();
|
||||
MethodInfo info = null;
|
||||
if (mcsc != null) {
|
||||
info = mcsc.getServiceMethodMap().get(method.getFullMethodName());
|
||||
if (mcsc == null) {
|
||||
return null;
|
||||
}
|
||||
if (info == null && mcsc != null) {
|
||||
MethodInfo info;
|
||||
info = mcsc.getServiceMethodMap().get(method.getFullMethodName());
|
||||
if (info == null) {
|
||||
String serviceName = method.getServiceName();
|
||||
info = mcsc.getServiceMap().get(serviceName);
|
||||
}
|
||||
if (info == null) {
|
||||
info = mcsc.getDefaultMethodConfig();
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,15 +18,22 @@ package io.grpc.internal;
|
|||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class ManagedChannelServiceConfigTest {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void managedChannelServiceConfig_shouldParseHealthCheckingConfig() throws Exception {
|
||||
Map<String, ?> rawServiceConfig =
|
||||
|
|
@ -52,6 +59,83 @@ public class ManagedChannelServiceConfigTest {
|
|||
assertThat(mcsc.getHealthCheckingConfig()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createManagedChannelServiceConfig_failsOnDuplicateMethod() {
|
||||
Map<String, ?> name1 = ImmutableMap.of("service", "service", "method", "method");
|
||||
Map<String, ?> name2 = ImmutableMap.of("service", "service", "method", "method");
|
||||
Map<String, ?> methodConfig = ImmutableMap.of("name", ImmutableList.of(name1, name2));
|
||||
Map<String, ?> serviceConfig = ImmutableMap.of("methodConfig", ImmutableList.of(methodConfig));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Duplicate method");
|
||||
|
||||
ManagedChannelServiceConfig.fromServiceConfig(serviceConfig, true, 3, 4, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createManagedChannelServiceConfig_failsOnDuplicateService() {
|
||||
Map<String, ?> name1 = ImmutableMap.of("service", "service");
|
||||
Map<String, ?> name2 = ImmutableMap.of("service", "service");
|
||||
Map<String, ?> methodConfig = ImmutableMap.of("name", ImmutableList.of(name1, name2));
|
||||
Map<String, ?> serviceConfig = ImmutableMap.of("methodConfig", ImmutableList.of(methodConfig));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Duplicate service");
|
||||
|
||||
ManagedChannelServiceConfig.fromServiceConfig(serviceConfig, true, 3, 4, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createManagedChannelServiceConfig_failsOnDuplicateServiceMultipleConfig() {
|
||||
Map<String, ?> name1 = ImmutableMap.of("service", "service");
|
||||
Map<String, ?> name2 = ImmutableMap.of("service", "service");
|
||||
Map<String, ?> methodConfig1 = ImmutableMap.of("name", ImmutableList.of(name1));
|
||||
Map<String, ?> methodConfig2 = ImmutableMap.of("name", ImmutableList.of(name2));
|
||||
Map<String, ?> serviceConfig =
|
||||
ImmutableMap.of("methodConfig", ImmutableList.of(methodConfig1, methodConfig2));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Duplicate service");
|
||||
|
||||
ManagedChannelServiceConfig.fromServiceConfig(serviceConfig, true, 3, 4, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createManagedChannelServiceConfig_failsOnMethodNameWithEmptyServiceName() {
|
||||
Map<String, ?> name = ImmutableMap.of("service", "", "method", "method1");
|
||||
Map<String, ?> methodConfig = ImmutableMap.of("name", ImmutableList.of(name));
|
||||
Map<String, ?> serviceConfig = ImmutableMap.of("methodConfig", ImmutableList.of(methodConfig));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("missing service name for method method1");
|
||||
|
||||
ManagedChannelServiceConfig.fromServiceConfig(serviceConfig, true, 3, 4, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createManagedChannelServiceConfig_failsOnMethodNameWithoutServiceName() {
|
||||
Map<String, ?> name = ImmutableMap.of("method", "method1");
|
||||
Map<String, ?> methodConfig = ImmutableMap.of("name", ImmutableList.of(name));
|
||||
Map<String, ?> serviceConfig = ImmutableMap.of("methodConfig", ImmutableList.of(methodConfig));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("missing service name for method method1");
|
||||
|
||||
ManagedChannelServiceConfig.fromServiceConfig(serviceConfig, true, 3, 4, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createManagedChannelServiceConfig_failsOnMissingServiceName() {
|
||||
Map<String, ?> name = ImmutableMap.of("method", "method");
|
||||
Map<String, ?> methodConfig = ImmutableMap.of("name", ImmutableList.of(name));
|
||||
Map<String, ?> serviceConfig = ImmutableMap.of("methodConfig", ImmutableList.of(methodConfig));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("missing service");
|
||||
|
||||
ManagedChannelServiceConfig.fromServiceConfig(serviceConfig, true, 3, 4, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Object> parseConfig(String json) throws Exception {
|
||||
return (Map<String, Object>) JsonParser.parse(json);
|
||||
|
|
|
|||
|
|
@ -304,81 +304,61 @@ public class ServiceConfigInterceptorTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void handleUpdate_failsOnMissingServiceName() {
|
||||
JsonObj name = new JsonObj("method", "method");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name));
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("missing service");
|
||||
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUpdate_failsOnDuplicateMethod() {
|
||||
JsonObj name1 = new JsonObj("service", "service", "method", "method");
|
||||
JsonObj name2 = new JsonObj("service", "service", "method", "method");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name1, name2));
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Duplicate method");
|
||||
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUpdate_failsOnEmptyName() {
|
||||
public void handleUpdate_onEmptyName() {
|
||||
JsonObj methodConfig = new JsonObj();
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("no names in method config");
|
||||
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getDefaultMethodConfig()).isNull();
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMap()).isEmpty();
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMethodMap()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUpdate_failsOnDuplicateService() {
|
||||
JsonObj name1 = new JsonObj("service", "service");
|
||||
JsonObj name2 = new JsonObj("service", "service");
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name1, name2));
|
||||
public void handleUpdate_onDefaultMethodConfig() {
|
||||
JsonObj name = new JsonObj();
|
||||
JsonObj methodConfig = new JsonObj("name", new JsonList(name));
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Duplicate service");
|
||||
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUpdate_failsOnDuplicateServiceMultipleConfig() {
|
||||
JsonObj name1 = new JsonObj("service", "service");
|
||||
JsonObj name2 = new JsonObj("service", "service");
|
||||
JsonObj methodConfig1 = new JsonObj("name", new JsonList(name1));
|
||||
JsonObj methodConfig2 = new JsonObj("name", new JsonList(name2));
|
||||
JsonObj serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig1, methodConfig2));
|
||||
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Duplicate service");
|
||||
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getDefaultMethodConfig())
|
||||
.isEqualTo(new MethodInfo(methodConfig, false, 1, 1));
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMap()).isEmpty();
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMethodMap()).isEmpty();
|
||||
|
||||
name = new JsonObj("method", "");
|
||||
methodConfig = new JsonObj("name", new JsonList(name));
|
||||
serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
parsedServiceConfig = createManagedChannelServiceConfig(serviceConfig);
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getDefaultMethodConfig())
|
||||
.isEqualTo(new MethodInfo(methodConfig, false, 1, 1));
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMap()).isEmpty();
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMethodMap()).isEmpty();
|
||||
|
||||
name = new JsonObj("service", "");
|
||||
methodConfig = new JsonObj("name", new JsonList(name));
|
||||
serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
parsedServiceConfig = createManagedChannelServiceConfig(serviceConfig);
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getDefaultMethodConfig())
|
||||
.isEqualTo(new MethodInfo(methodConfig, false, 1, 1));
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMap()).isEmpty();
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMethodMap()).isEmpty();
|
||||
|
||||
name = new JsonObj("service", "", "method", "");
|
||||
methodConfig = new JsonObj("name", new JsonList(name));
|
||||
serviceConfig = new JsonObj("methodConfig", new JsonList(methodConfig));
|
||||
parsedServiceConfig = createManagedChannelServiceConfig(serviceConfig);
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getDefaultMethodConfig())
|
||||
.isEqualTo(new MethodInfo(methodConfig, false, 1, 1));
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMap()).isEmpty();
|
||||
assertThat(interceptor.managedChannelServiceConfig.get().getServiceMethodMap()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -425,6 +405,61 @@ public class ServiceConfigInterceptorTest {
|
|||
"service2", new MethodInfo(methodConfig, false, 1, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interceptCall_matchNames() {
|
||||
JsonObj name0 = new JsonObj();
|
||||
JsonObj name1 = new JsonObj("service", "service");
|
||||
JsonObj name2 = new JsonObj("service", "service", "method", "method");
|
||||
JsonObj methodConfig0 = new JsonObj(
|
||||
"name", new JsonList(name0), "maxRequestMessageBytes", 5d);
|
||||
JsonObj methodConfig1 = new JsonObj(
|
||||
"name", new JsonList(name1), "maxRequestMessageBytes", 6d);
|
||||
JsonObj methodConfig2 = new JsonObj(
|
||||
"name", new JsonList(name2), "maxRequestMessageBytes", 7d);
|
||||
JsonObj serviceConfig =
|
||||
new JsonObj("methodConfig", new JsonList(methodConfig0, methodConfig1, methodConfig2));
|
||||
ManagedChannelServiceConfig parsedServiceConfig =
|
||||
createManagedChannelServiceConfig(serviceConfig);
|
||||
|
||||
interceptor.handleUpdate(parsedServiceConfig);
|
||||
|
||||
String fullMethodName =
|
||||
MethodDescriptor.generateFullMethodName("service", "method");
|
||||
MethodDescriptor<Void, Void> methodDescriptor =
|
||||
MethodDescriptor.newBuilder(new NoopMarshaller(), new NoopMarshaller())
|
||||
.setType(MethodType.UNARY)
|
||||
.setFullMethodName(fullMethodName)
|
||||
.build();
|
||||
interceptor.interceptCall(
|
||||
methodDescriptor, CallOptions.DEFAULT, channel);
|
||||
verify(channel).newCall(eq(methodDescriptor), callOptionsCap.capture());
|
||||
assertThat(callOptionsCap.getValue().getMaxOutboundMessageSize()).isEqualTo(7);
|
||||
|
||||
fullMethodName =
|
||||
MethodDescriptor.generateFullMethodName("service", "method2");
|
||||
methodDescriptor =
|
||||
MethodDescriptor.newBuilder(new NoopMarshaller(), new NoopMarshaller())
|
||||
.setType(MethodType.UNARY)
|
||||
.setFullMethodName(fullMethodName)
|
||||
.build();
|
||||
interceptor.interceptCall(
|
||||
methodDescriptor, CallOptions.DEFAULT, channel);
|
||||
verify(channel).newCall(eq(methodDescriptor), callOptionsCap.capture());
|
||||
assertThat(callOptionsCap.getValue().getMaxOutboundMessageSize()).isEqualTo(6);
|
||||
|
||||
fullMethodName =
|
||||
MethodDescriptor.generateFullMethodName("service2", "method");
|
||||
methodDescriptor =
|
||||
MethodDescriptor.newBuilder(new NoopMarshaller(), new NoopMarshaller())
|
||||
.setType(MethodType.UNARY)
|
||||
.setFullMethodName(fullMethodName)
|
||||
.build();
|
||||
interceptor.interceptCall(
|
||||
methodDescriptor, CallOptions.DEFAULT, channel);
|
||||
verify(channel).newCall(eq(methodDescriptor), callOptionsCap.capture());
|
||||
assertThat(callOptionsCap.getValue().getMaxOutboundMessageSize()).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodInfo_validateDeadline() {
|
||||
JsonObj name = new JsonObj("service", "service");
|
||||
|
|
|
|||
|
|
@ -39,12 +39,14 @@ import org.junit.runners.JUnit4;
|
|||
public class ServiceConfigStateTest {
|
||||
|
||||
private final ManagedChannelServiceConfig serviceConfig1 = new ManagedChannelServiceConfig(
|
||||
null,
|
||||
Collections.<String, MethodInfo>emptyMap(),
|
||||
Collections.<String, MethodInfo>emptyMap(),
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
private final ManagedChannelServiceConfig serviceConfig2 = new ManagedChannelServiceConfig(
|
||||
null,
|
||||
Collections.<String, MethodInfo>emptyMap(),
|
||||
Collections.<String, MethodInfo>emptyMap(),
|
||||
null,
|
||||
|
|
@ -428,6 +430,7 @@ public class ServiceConfigStateTest {
|
|||
public void lookup_default_onPresent_onPresent() {
|
||||
ServiceConfigState scs = new ServiceConfigState(serviceConfig1, true);
|
||||
ManagedChannelServiceConfig serviceConfig3 = new ManagedChannelServiceConfig(
|
||||
null,
|
||||
Collections.<String, MethodInfo>emptyMap(),
|
||||
Collections.<String, MethodInfo>emptyMap(),
|
||||
null,
|
||||
|
|
|
|||
Loading…
Reference in New Issue