io.grpc.internal.ServiceConfigUtil Maven / Gradle / Ivy
/*
* Copyright 2018 The gRPC Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.grpc.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Verify.verify;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.VerifyException;
import io.grpc.LoadBalancerProvider;
import io.grpc.LoadBalancerRegistry;
import io.grpc.NameResolver.ConfigOrError;
import io.grpc.Status;
import io.grpc.internal.RetriableStream.Throttle;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
/**
* Helper utility to work with service configs.
*
* This class contains helper methods to parse service config JSON values into Java types.
*/
public final class ServiceConfigUtil {
private ServiceConfigUtil() {}
/**
* Fetches the health-checked service config from service config. {@code null} if can't find one.
*/
@Nullable
public static Map getHealthCheckedService(@Nullable Map serviceConfig) {
if (serviceConfig == null) {
return null;
}
/* schema as follows
{
"healthCheckConfig": {
// Service name to use in the health-checking request.
"serviceName": string
}
}
*/
return JsonUtil.getObject(serviceConfig, "healthCheckConfig");
}
/**
* Fetches the health-checked service name from health-checked service config. {@code null} if
* can't find one.
*/
@Nullable
public static String getHealthCheckedServiceName(
@Nullable Map healthCheckedServiceConfig) {
if (healthCheckedServiceConfig == null) {
return null;
}
return JsonUtil.getString(healthCheckedServiceConfig, "serviceName");
}
@Nullable
static Throttle getThrottlePolicy(@Nullable Map serviceConfig) {
if (serviceConfig == null) {
return null;
}
/* schema as follows
{
"retryThrottling": {
// The number of tokens starts at maxTokens. The token_count will always be
// between 0 and maxTokens.
//
// This field is required and must be greater than zero.
"maxTokens": number,
// The amount of tokens to add on each successful RPC. Typically this will
// be some number between 0 and 1, e.g., 0.1.
//
// This field is required and must be greater than zero. Up to 3 decimal
// places are supported.
"tokenRatio": number
}
}
*/
Map throttling = JsonUtil.getObject(serviceConfig, "retryThrottling");
if (throttling == null) {
return null;
}
// TODO(dapengzhang0): check if this is null.
float maxTokens = JsonUtil.getNumberAsDouble(throttling, "maxTokens").floatValue();
float tokenRatio = JsonUtil.getNumberAsDouble(throttling, "tokenRatio").floatValue();
checkState(maxTokens > 0f, "maxToken should be greater than zero");
checkState(tokenRatio > 0f, "tokenRatio should be greater than zero");
return new Throttle(maxTokens, tokenRatio);
}
@Nullable
static Integer getMaxAttemptsFromRetryPolicy(Map retryPolicy) {
return JsonUtil.getNumberAsInteger(retryPolicy, "maxAttempts");
}
@Nullable
static Long getInitialBackoffNanosFromRetryPolicy(Map retryPolicy) {
return JsonUtil.getStringAsDuration(retryPolicy, "initialBackoff");
}
@Nullable
static Long getMaxBackoffNanosFromRetryPolicy(Map retryPolicy) {
return JsonUtil.getStringAsDuration(retryPolicy, "maxBackoff");
}
@Nullable
static Double getBackoffMultiplierFromRetryPolicy(Map retryPolicy) {
return JsonUtil.getNumberAsDouble(retryPolicy, "backoffMultiplier");
}
@Nullable
static Long getPerAttemptRecvTimeoutNanosFromRetryPolicy(Map retryPolicy) {
return JsonUtil.getStringAsDuration(retryPolicy, "perAttemptRecvTimeout");
}
private static Set getListOfStatusCodesAsSet(Map obj, String key) {
List> statuses = JsonUtil.getList(obj, key);
if (statuses == null) {
return null;
}
return getStatusCodesFromList(statuses);
}
private static Set getStatusCodesFromList(List> statuses) {
EnumSet codes = EnumSet.noneOf(Status.Code.class);
for (Object status : statuses) {
Status.Code code;
if (status instanceof Double) {
Double statusD = (Double) status;
int codeValue = statusD.intValue();
verify((double) codeValue == statusD, "Status code %s is not integral", status);
code = Status.fromCodeValue(codeValue).getCode();
verify(code.value() == statusD.intValue(), "Status code %s is not valid", status);
} else if (status instanceof String) {
try {
code = Status.Code.valueOf((String) status);
} catch (IllegalArgumentException iae) {
throw new VerifyException("Status code " + status + " is not valid", iae);
}
} else {
throw new VerifyException(
"Can not convert status code " + status + " to Status.Code, because its type is "
+ status.getClass());
}
codes.add(code);
}
return Collections.unmodifiableSet(codes);
}
static Set getRetryableStatusCodesFromRetryPolicy(Map retryPolicy) {
String retryableStatusCodesKey = "retryableStatusCodes";
Set codes = getListOfStatusCodesAsSet(retryPolicy, retryableStatusCodesKey);
verify(codes != null, "%s is required in retry policy", retryableStatusCodesKey);
verify(!codes.contains(Status.Code.OK), "%s must not contain OK", retryableStatusCodesKey);
return codes;
}
@Nullable
static Integer getMaxAttemptsFromHedgingPolicy(Map hedgingPolicy) {
return JsonUtil.getNumberAsInteger(hedgingPolicy, "maxAttempts");
}
@Nullable
static Long getHedgingDelayNanosFromHedgingPolicy(Map hedgingPolicy) {
return JsonUtil.getStringAsDuration(hedgingPolicy, "hedgingDelay");
}
static Set getNonFatalStatusCodesFromHedgingPolicy(Map hedgingPolicy) {
String nonFatalStatusCodesKey = "nonFatalStatusCodes";
Set codes = getListOfStatusCodesAsSet(hedgingPolicy, nonFatalStatusCodesKey);
if (codes == null) {
return Collections.unmodifiableSet(EnumSet.noneOf(Status.Code.class));
}
verify(!codes.contains(Status.Code.OK), "%s must not contain OK", nonFatalStatusCodesKey);
return codes;
}
@Nullable
static String getServiceFromName(Map name) {
return JsonUtil.getString(name, "service");
}
@Nullable
static String getMethodFromName(Map name) {
return JsonUtil.getString(name, "method");
}
@Nullable
static Map getRetryPolicyFromMethodConfig(Map methodConfig) {
return JsonUtil.getObject(methodConfig, "retryPolicy");
}
@Nullable
static Map getHedgingPolicyFromMethodConfig(Map methodConfig) {
return JsonUtil.getObject(methodConfig, "hedgingPolicy");
}
@Nullable
static List