xds: modify XdsClient interface and define data to be used by gRPC client from xDS responses (#6390)

This change completes the definition of XdsClient interface and fill the content of data types passing from XdsClient to each resource watcher.
This commit is contained in:
Chengyuan Zhang 2019-11-05 23:59:04 -08:00 committed by GitHub
parent a408d086bd
commit 794517a7c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 270 additions and 22 deletions

View File

@ -16,16 +16,25 @@
package io.grpc.xds;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.grpc.Status;
import io.grpc.xds.EnvoyProtoData.DropOverload;
import io.grpc.xds.EnvoyProtoData.Locality;
import io.grpc.xds.EnvoyProtoData.LocalityLbEndpoints;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/**
* An {@link XdsClient} instance encapsulates all of the logic for communicating with the xDS
* server. It may create multiple RPC streams (or a single ADS stream) for a series of xDS
* protocols (e.g., LDS, RDS, VHDS, CDS and EDS) over a single channel. Watch-based interfaces
* are provided for each set of data needed by gRPC.
*
* <p>This class should only be instantiated by the xDS resolver but can be passed to load
* balancing policies.
*/
abstract class XdsClient {
@ -35,9 +44,12 @@ abstract class XdsClient {
* traffic mirroring, retry or hedging, default timeouts and load balancing policy that will
* be used to generate a service config.
*/
// TODO(chengyuanzhang): content TBD, most information comes from VirtualHost proto.
static final class ConfigUpdate {
private String clusterName;
private final String clusterName;
private ConfigUpdate(String clusterName) {
this.clusterName = clusterName;
}
String getClusterName() {
return clusterName;
@ -48,19 +60,20 @@ abstract class XdsClient {
}
static final class Builder {
private ConfigUpdate base;
private String clusterName;
// Use ConfigUpdate.newBuilder().
private Builder() {
base = new ConfigUpdate();
}
Builder setClusterName(String clusterName) {
base.clusterName = clusterName;
this.clusterName = clusterName;
return this;
}
ConfigUpdate build() {
return base;
Preconditions.checkState(clusterName != null, "clusterName is not set");
return new ConfigUpdate(clusterName);
}
}
}
@ -70,9 +83,110 @@ abstract class XdsClient {
* The results include configurations for a single upstream cluster, such as endpoint discovery
* type, load balancing policy, connection timeout and etc.
*/
// TODO(zdapeng): content TBD.
static final class ClusterUpdate {
private final String clusterName;
private final String edsServiceName;
private final String lbPolicy;
private final boolean enableLrs;
private final String lrsServerName;
private ClusterUpdate(String clusterName, String edsServiceName, String lbPolicy,
boolean enableLrs, @Nullable String lrsServerName) {
this.clusterName = clusterName;
this.edsServiceName = edsServiceName;
this.lbPolicy = lbPolicy;
this.enableLrs = enableLrs;
this.lrsServerName = lrsServerName;
}
String getClusterName() {
return clusterName;
}
/**
* Returns the resource name for EDS requests.
*/
String getEdsServiceName() {
return edsServiceName;
}
/**
* Returns the policy of balancing loads to endpoints. Only "round_robin" is supported
* as of now.
*/
String getLbPolicy() {
return lbPolicy;
}
/**
* Returns true if LRS is enabled.
*/
boolean isEnableLrs() {
return enableLrs;
}
/**
* Returns the server name to send client load reports to if LRS is enabled. {@code null} if
* {@link #isEnableLrs()} returns {@code false}.
*/
@Nullable
String getLrsServerName() {
return lrsServerName;
}
static Builder newBuilder() {
return new Builder();
}
static final class Builder {
private String clusterName;
private String edsServiceName;
private String lbPolicy;
private boolean enableLrs;
@Nullable
private String lrsServerName;
// Use ClusterUpdate.newBuilder().
private Builder() {
}
Builder setClusterName(String clusterName) {
this.clusterName = clusterName;
return this;
}
Builder setEdsServiceName(String edsServiceName) {
this.edsServiceName = edsServiceName;
return this;
}
Builder setLbPolicy(String lbPolicy) {
this.lbPolicy = lbPolicy;
return this;
}
Builder setEnableLrs(boolean enableLrs) {
this.enableLrs = enableLrs;
return this;
}
Builder setLrsServerName(String lrsServerName) {
this.lrsServerName = lrsServerName;
return this;
}
ClusterUpdate build() {
Preconditions.checkState(clusterName != null, "clusterName is not set");
Preconditions.checkState(lbPolicy != null, "lbPolicy is not set");
Preconditions.checkState(
(enableLrs && lrsServerName != null) || (!enableLrs && lrsServerName == null),
"lrsServerName is not set while LRS is enabled "
+ "OR lrsServerName is set while LRS is not enabled");
return
new ClusterUpdate(clusterName, edsServiceName == null ? clusterName : edsServiceName,
lbPolicy, enableLrs, lrsServerName);
}
}
}
/**
@ -81,9 +195,75 @@ abstract class XdsClient {
* configurations for traffic control such as drop overloads, inter-cluster load balancing
* policy and etc.
*/
// TODO(zdapeng): content TBD.
static final class EndpointUpdate {
private final String clusterName;
private final Map<Locality, LocalityLbEndpoints> localityLbEndpointsMap;
private final List<DropOverload> dropPolicies;
private EndpointUpdate(
String clusterName,
Map<Locality, LocalityLbEndpoints> localityLbEndpoints,
List<DropOverload> dropPolicies) {
this.clusterName = clusterName;
this.localityLbEndpointsMap = localityLbEndpoints;
this.dropPolicies = dropPolicies;
}
static Builder newBuilder() {
return new Builder();
}
String getClusterName() {
return clusterName;
}
/**
* Returns a map of localities with endpoints load balancing information in each locality.
*/
Map<Locality, LocalityLbEndpoints> getLocalityLbEndpointsMap() {
return Collections.unmodifiableMap(localityLbEndpointsMap);
}
/**
* Returns a list of drop policies to be applied to outgoing requests.
*/
List<DropOverload> getDropPolicies() {
return Collections.unmodifiableList(dropPolicies);
}
static final class Builder {
private String clusterName;
private Map<Locality, LocalityLbEndpoints> localityLbEndpointsMap = new LinkedHashMap<>();
private List<DropOverload> dropPolicies = new ArrayList<>();
// Use EndpointUpdate.newBuilder().
private Builder() {
}
Builder setClusterName(String clusterName) {
this.clusterName = clusterName;
return this;
}
Builder addLocalityLbEndpoints(Locality locality, LocalityLbEndpoints info) {
localityLbEndpointsMap.put(locality, info);
return this;
}
Builder addDropPolicy(DropOverload policy) {
dropPolicies.add(policy);
return this;
}
EndpointUpdate build() {
Preconditions.checkState(clusterName != null, "clusterName is not set");
return
new EndpointUpdate(
clusterName,
ImmutableMap.copyOf(localityLbEndpointsMap),
ImmutableList.copyOf(dropPolicies));
}
}
}
/**
@ -120,16 +300,33 @@ abstract class XdsClient {
}
/**
* Starts resource discovery with xDS protocol. This should be the first method to be called in
* this class. It should only be called once.
*/
abstract void start();
/**
* Stops resource discovery. No method in this class should be called after this point.
* Shutdown this {@link XdsClient} and release resources.
*/
abstract void shutdown();
/**
* Registers a watcher to receive {@link ConfigUpdate} for service with the given hostname and
* port.
*
* <p>Unlike watchers for cluster data and endpoint data, at any point of time at most one config
* watcher is allowed.
*
* @param hostName the host name part of the "xds:" URI for the server name that the gRPC client
* targets for. Must NOT contain port.
* @param port the port part of the "xds:" URI for the server name that the gRPC client targets
* for. -1 if not specified.
* @param watcher the {@link ConfigWatcher} to receive {@link ConfigUpdate}.
*/
void watchConfigData(String hostName, int port, ConfigWatcher watcher) {
}
/**
* Unregisters the existing config watcher. The previously registered config watcher will no
* longer receive {@link ConfigUpdate}. Noop if no config watcher has been registered.
*/
void cancelConfigDataWatch() {
}
/**
* Registers a data watcher for the given cluster.
*/
@ -137,9 +334,10 @@ abstract class XdsClient {
}
/**
* Unregisters the given cluster watcher.
* Unregisters the given cluster watcher, which was registered to receive updates for the
* given cluster.
*/
void cancelClusterDataWatch(ClusterWatcher watcher) {
void cancelClusterDataWatch(String clusterName, ClusterWatcher watcher) {
}
/**
@ -149,8 +347,9 @@ abstract class XdsClient {
}
/**
* Unregisters the given endpoints watcher.
* Unregisters the given endpoints watcher, which was registered to receive updates for
* endpoints information in the given cluster.
*/
void cancelEndpointDataWatch(EndpointWatcher watcher) {
void cancelEndpointDataWatch(String clusterName, EndpointWatcher watcher) {
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.xds;
import static com.google.common.truth.Truth.assertThat;
import io.grpc.xds.XdsClient.ClusterUpdate;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Unit tests for {@link XdsClient}.
*/
@RunWith(JUnit4.class)
public class XdsClientTest {
@Test
public void buildClusterUpdate_defaultToClusterNameWhenEdsServiceNameNotSet() {
ClusterUpdate clusterUpdate1 =
ClusterUpdate.newBuilder()
.setClusterName("foo.googleapis.com")
.setEdsServiceName("bar.googleapis.com")
.setLbPolicy("round_robin")
.build();
assertThat(clusterUpdate1.getEdsServiceName()).isEqualTo("bar.googleapis.com");
ClusterUpdate clusterUpdate2 =
ClusterUpdate.newBuilder()
.setClusterName("foo.googleapis.com")
.setLbPolicy("round_robin")
.build();
assertThat(clusterUpdate2.getEdsServiceName()).isEqualTo("foo.googleapis.com");
}
}