Skip to content

Commit

Permalink
grpclb: add serviceName config to grpclb policy config (#6563)
Browse files Browse the repository at this point in the history
  • Loading branch information
creamsoup committed Feb 11, 2020
1 parent 45bb403 commit 774f276
Show file tree
Hide file tree
Showing 6 changed files with 468 additions and 198 deletions.
83 changes: 83 additions & 0 deletions grpclb/src/main/java/io/grpc/grpclb/GrpclbConfig.java
@@ -0,0 +1,83 @@
/*
* Copyright 2019 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.grpclb;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import io.grpc.grpclb.GrpclbState.Mode;
import javax.annotation.Nullable;

final class GrpclbConfig {

private final Mode mode;
@Nullable
private final String serviceName;

private GrpclbConfig(Mode mode, @Nullable String serviceName) {
this.mode = checkNotNull(mode, "mode");
this.serviceName = serviceName;
}

static GrpclbConfig create(Mode mode) {
return create(mode, null);
}

static GrpclbConfig create(Mode mode, @Nullable String serviceName) {
return new GrpclbConfig(mode, serviceName);
}

Mode getMode() {
return mode;
}

/**
* If specified, it overrides the name of the sevice name to be sent to the balancer. if not, the
* target to be sent to the balancer will continue to be obtained from the target URI passed
* to the gRPC client channel.
*/
@Nullable
String getServiceName() {
return serviceName;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
GrpclbConfig that = (GrpclbConfig) o;
return mode == that.mode && Objects.equal(serviceName, that.serviceName);
}

@Override
public int hashCode() {
return Objects.hashCode(mode, serviceName);
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("mode", mode)
.add("serviceName", serviceName)
.toString();
}
}
96 changes: 12 additions & 84 deletions grpclb/src/main/java/io/grpc/grpclb/GrpclbLoadBalancer.java
Expand Up @@ -22,23 +22,17 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Stopwatch;
import io.grpc.Attributes;
import io.grpc.ChannelLogger;
import io.grpc.ChannelLogger.ChannelLogLevel;
import io.grpc.ConnectivityStateInfo;
import io.grpc.EquivalentAddressGroup;
import io.grpc.LoadBalancer;
import io.grpc.Status;
import io.grpc.grpclb.GrpclbState.Mode;
import io.grpc.internal.BackoffPolicy;
import io.grpc.internal.ServiceConfigUtil;
import io.grpc.internal.ServiceConfigUtil.LbConfig;
import io.grpc.internal.TimeProvider;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;

/**
Expand All @@ -48,16 +42,16 @@
* or round-robin balancer.
*/
class GrpclbLoadBalancer extends LoadBalancer {
private static final Mode DEFAULT_MODE = Mode.ROUND_ROBIN;
private static final Logger logger = Logger.getLogger(GrpclbLoadBalancer.class.getName());

private static final GrpclbConfig DEFAULT_CONFIG = GrpclbConfig.create(Mode.ROUND_ROBIN);

private final Helper helper;
private final TimeProvider time;
private final Stopwatch stopwatch;
private final SubchannelPool subchannelPool;
private final BackoffPolicy.Provider backoffPolicyProvider;

private Mode mode = Mode.ROUND_ROBIN;
private GrpclbConfig config = DEFAULT_CONFIG;

// All mutable states in this class are mutated ONLY from Channel Executor
@Nullable
Expand Down Expand Up @@ -88,7 +82,6 @@ public void handleSubchannelState(Subchannel subchannel, ConnectivityStateInfo n
}

@Override
@SuppressWarnings("deprecation") // TODO(creamsoup) migrate to use parsed service config
public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
Attributes attributes = resolvedAddresses.getAttributes();
List<EquivalentAddressGroup> newLbAddresses = attributes.get(GrpclbConstants.ATTR_LB_ADDRS);
Expand All @@ -114,11 +107,13 @@ public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
newLbAddressGroups = Collections.unmodifiableList(newLbAddressGroups);
List<EquivalentAddressGroup> newBackendServers =
Collections.unmodifiableList(resolvedAddresses.getAddresses());
Map<String, ?> rawLbConfigValue = attributes.get(ATTR_LOAD_BALANCING_CONFIG);
Mode newMode = retrieveModeFromLbConfig(rawLbConfigValue, helper.getChannelLogger());
if (!mode.equals(newMode)) {
mode = newMode;
helper.getChannelLogger().log(ChannelLogLevel.INFO, "Mode: " + newMode);
GrpclbConfig newConfig = (GrpclbConfig) resolvedAddresses.getLoadBalancingPolicyConfig();
if (newConfig == null) {
newConfig = DEFAULT_CONFIG;
}
if (!config.equals(newConfig)) {
config = newConfig;
helper.getChannelLogger().log(ChannelLogLevel.INFO, "Config: " + newConfig);
recreateStates();
}
grpclbState.handleAddresses(newLbAddressGroups, newBackendServers);
Expand All @@ -131,40 +126,6 @@ public void requestConnection() {
}
}

@VisibleForTesting
static Mode retrieveModeFromLbConfig(
@Nullable Map<String, ?> rawLbConfigValue, ChannelLogger channelLogger) {
try {
if (rawLbConfigValue == null) {
return DEFAULT_MODE;
}
List<?> rawChildPolicies = getList(rawLbConfigValue, "childPolicy");
if (rawChildPolicies == null) {
return DEFAULT_MODE;
}
List<LbConfig> childPolicies =
ServiceConfigUtil.unwrapLoadBalancingConfigList(checkObjectList(rawChildPolicies));
for (LbConfig childPolicy : childPolicies) {
String childPolicyName = childPolicy.getPolicyName();
switch (childPolicyName) {
case "round_robin":
return Mode.ROUND_ROBIN;
case "pick_first":
return Mode.PICK_FIRST;
default:
channelLogger.log(
ChannelLogLevel.DEBUG,
"grpclb ignoring unsupported child policy " + childPolicyName);
}
}
} catch (RuntimeException e) {
channelLogger.log(ChannelLogLevel.WARNING, "Bad grpclb config, using " + DEFAULT_MODE);
logger.log(
Level.WARNING, "Bad grpclb config: " + rawLbConfigValue + ", using " + DEFAULT_MODE, e);
}
return DEFAULT_MODE;
}

private void resetStates() {
if (grpclbState != null) {
grpclbState.shutdown();
Expand All @@ -175,8 +136,8 @@ private void resetStates() {
private void recreateStates() {
resetStates();
checkState(grpclbState == null, "Should've been cleared");
grpclbState = new GrpclbState(mode, helper, subchannelPool, time, stopwatch,
backoffPolicyProvider);
grpclbState =
new GrpclbState(config, helper, subchannelPool, time, stopwatch, backoffPolicyProvider);
}

@Override
Expand All @@ -201,37 +162,4 @@ public boolean canHandleEmptyAddressListFromNameResolution() {
GrpclbState getGrpclbState() {
return grpclbState;
}

// TODO(carl-mastrangelo): delete getList and checkObjectList once apply is complete for SVCCFG.
/**
* Gets a list from an object for the given key. Copy of
* {@link io.grpc.internal.ServiceConfigUtil#getList}.
*/
@Nullable
private static List<?> getList(Map<String, ?> obj, String key) {
assert key != null;
if (!obj.containsKey(key)) {
return null;
}
Object value = obj.get(key);
if (!(value instanceof List)) {
throw new ClassCastException(
String.format("value '%s' for key '%s' in %s is not List", value, key, obj));
}
return (List<?>) value;
}

/**
* Copy of {@link io.grpc.internal.ServiceConfigUtil#checkObjectList}.
*/
@SuppressWarnings("unchecked")
private static List<Map<String, ?>> checkObjectList(List<?> rawList) {
for (int i = 0; i < rawList.size(); i++) {
if (!(rawList.get(i) instanceof Map)) {
throw new ClassCastException(
String.format("value %s for idx %d in %s is not object", rawList.get(i), i, rawList));
}
}
return (List<Map<String, ?>>) rawList;
}
}
68 changes: 25 additions & 43 deletions grpclb/src/main/java/io/grpc/grpclb/GrpclbLoadBalancerProvider.java
Expand Up @@ -24,12 +24,13 @@
import io.grpc.Status;
import io.grpc.grpclb.GrpclbState.Mode;
import io.grpc.internal.ExponentialBackoffPolicy;
import io.grpc.internal.JsonUtil;
import io.grpc.internal.ServiceConfigUtil;
import io.grpc.internal.ServiceConfigUtil.LbConfig;
import io.grpc.internal.TimeProvider;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;

/**
* The provider for the "grpclb" balancing policy. This class should not be directly referenced in
Expand All @@ -38,6 +39,7 @@
*/
@Internal
public final class GrpclbLoadBalancerProvider extends LoadBalancerProvider {

private static final Mode DEFAULT_MODE = Mode.ROUND_ROBIN;

@Override
Expand Down Expand Up @@ -78,57 +80,37 @@ public ConfigOrError parseLoadBalancingPolicyConfig(
ConfigOrError parseLoadBalancingConfigPolicyInternal(
Map<String, ?> rawLoadBalancingPolicyConfig) {
if (rawLoadBalancingPolicyConfig == null) {
return ConfigOrError.fromConfig(DEFAULT_MODE);
return ConfigOrError.fromConfig(GrpclbConfig.create(DEFAULT_MODE));
}
String serviceName = JsonUtil.getString(rawLoadBalancingPolicyConfig, "serviceName");
List<?> rawChildPolicies = JsonUtil.getList(rawLoadBalancingPolicyConfig, "childPolicy");
List<LbConfig> childPolicies = null;
if (rawChildPolicies != null) {
childPolicies =
ServiceConfigUtil
.unwrapLoadBalancingConfigList(JsonUtil.checkObjectList(rawChildPolicies));
}
List<?> rawChildPolicies = getList(rawLoadBalancingPolicyConfig, "childPolicy");
if (rawChildPolicies == null) {
return ConfigOrError.fromConfig(DEFAULT_MODE);

if (childPolicies == null || childPolicies.isEmpty()) {
return ConfigOrError.fromConfig(GrpclbConfig.create(DEFAULT_MODE, serviceName));
}
List<LbConfig> childPolicies =
ServiceConfigUtil.unwrapLoadBalancingConfigList(checkObjectList(rawChildPolicies));

List<String> policiesTried = new ArrayList<>();
for (LbConfig childPolicy : childPolicies) {
String childPolicyName = childPolicy.getPolicyName();
switch (childPolicyName) {
case "round_robin":
return ConfigOrError.fromConfig(Mode.ROUND_ROBIN);
return ConfigOrError.fromConfig(GrpclbConfig.create(Mode.ROUND_ROBIN, serviceName));
case "pick_first":
return ConfigOrError.fromConfig(Mode.PICK_FIRST);
return ConfigOrError.fromConfig(GrpclbConfig.create(Mode.PICK_FIRST, serviceName));
default:
// TODO(zhangkun83): maybe log?
}
}
return ConfigOrError.fromConfig(DEFAULT_MODE);
}

/**
* Gets a list from an object for the given key. Copy of
* {@link io.grpc.internal.ServiceConfigUtil#getList}.
*/
@Nullable
private static List<?> getList(Map<String, ?> obj, String key) {
assert key != null;
if (!obj.containsKey(key)) {
return null;
}
Object value = obj.get(key);
if (!(value instanceof List)) {
throw new ClassCastException(
String.format("value '%s' for key '%s' in %s is not List", value, key, obj));
}
return (List<?>) value;
}

/**
* Copy of {@link io.grpc.internal.ServiceConfigUtil#checkObjectList}.
*/
@SuppressWarnings("unchecked")
private static List<Map<String, ?>> checkObjectList(List<?> rawList) {
for (int i = 0; i < rawList.size(); i++) {
if (!(rawList.get(i) instanceof Map)) {
throw new ClassCastException(
String.format("value %s for idx %d in %s is not object", rawList.get(i), i, rawList));
policiesTried.add(childPolicyName);
}
}
return (List<Map<String, ?>>) rawList;
return ConfigOrError.fromError(
Status
.INVALID_ARGUMENT
.withDescription(
"None of " + policiesTried + " specified child policies are available."));
}
}

0 comments on commit 774f276

Please sign in to comment.