mirror of https://github.com/grpc/grpc-java.git
NameResolverRegistry and better-defined target string.
- NameResolverRegistry contains all the official NameResolvers. Users can also add custom NameResolvers to it. It looks up NameResolver by try-and-fail. It is the default NameResolver.Factory for builders. DnsNameResolver. - Pass target as Strings instead of URIs from the channel builder to ManagedChannelImpl. A target string is not necessarily a valid URI, in which case ManagedChannelImpl will add "dns:///" to the beginning of the target and use it as URI. - DnsNameResolver will require scheme "dns" to be present. It no longer allows scheme-absent URIs.
This commit is contained in:
parent
6dc5e8075b
commit
efac679abc
|
|
@ -47,7 +47,16 @@ import javax.annotation.Nullable;
|
|||
/**
|
||||
* A factory for DNS-based {@link NameResolver}s.
|
||||
*
|
||||
* <p>The format of the target URI is {@code "[dns://[<DNS_server_address>]/]<name>"}.
|
||||
* <p>It resolves a target URI whose scheme is {@code "dns"}. The (optional) authority of the target
|
||||
* URI is reserved for the address of alternative DNS server (not implemented yet). The path of the
|
||||
* target URI, exluding the leading slash {@code '/'}, is treated as the host name to be resolved by
|
||||
* DNS. Example target URIs:
|
||||
* <ul>
|
||||
* <li>{@code "dns:///foo.googleapis.com:8080"} (using default DNS)</li>
|
||||
* <li>{@code "dns://8.8.8.8/foo.googleapis.com:8080"} (using alternative DNS (not implemented
|
||||
* yet))</li>
|
||||
* <li>{@code "dns:///foo.googleapis.com"} (without port)</li>
|
||||
* </ul>
|
||||
*/
|
||||
@ExperimentalApi
|
||||
public final class DnsNameResolverFactory extends NameResolver.Factory {
|
||||
|
|
@ -56,10 +65,7 @@ public final class DnsNameResolverFactory extends NameResolver.Factory {
|
|||
|
||||
@Override
|
||||
public NameResolver newNameResolver(URI targetUri) {
|
||||
String scheme = targetUri.getScheme();
|
||||
if (scheme == null) {
|
||||
return new DnsNameResolver(null, targetUri.toString());
|
||||
} else if (scheme.equals("dns")) {
|
||||
if ("dns".equals(targetUri.getScheme())) {
|
||||
String targetPath = Preconditions.checkNotNull(targetUri.getPath(), "targetPath");
|
||||
Preconditions.checkArgument(targetPath.startsWith("/"),
|
||||
"the path component (%s) of the target (%s) must start with '/'", targetPath, targetUri);
|
||||
|
|
|
|||
|
|
@ -44,6 +44,31 @@ public abstract class ManagedChannelBuilder<T extends ManagedChannelBuilder<T>>
|
|||
return ManagedChannelProvider.provider().builderForAddress(name, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a channel with a target string, which can be either a valid {@link
|
||||
* NameResolver}-compliant URI, or a HOST:PORT string.
|
||||
*
|
||||
* <p>Example {@code NameResolver}-compliant URIs:
|
||||
* <ul>
|
||||
* <li>{@code "dns:///foo.googleapis.com:8080"}</li>
|
||||
* <li>{@code "dns:///foo.googleapis.com"}</li>
|
||||
* <li>{@code "dns://8.8.8.8/foo.googleapis.com:8080"}</li>
|
||||
* <li>{@code "dns://8.8.8.8/foo.googleapis.com"}</li>
|
||||
* <li>{@code "zookeeper://zk.example.com:9900/example_service"}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Example HOST:PORT strings, which will be converted to {@code NameResolver}-compliant URIs by
|
||||
* prepending {@code "dns:///"}.
|
||||
* <ul>
|
||||
* <li>{@code "localhost"}</li>
|
||||
* <li>{@code "127.0.0.1"}</li>
|
||||
* <li>{@code "localhost:8080"}</li>
|
||||
* <li>{@code "foo.googleapis.com:8080"}</li>
|
||||
* <li>{@code "127.0.0.1:8080"}</li>
|
||||
* <li>{@code "[2001:db8:85a3:8d3:1319:8a2e:370:7348]"}</li>
|
||||
* <li>{@code "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443"}</li>
|
||||
* </ul>
|
||||
*/
|
||||
@ExperimentalApi
|
||||
public static ManagedChannelBuilder<?> forTarget(String target) {
|
||||
return ManagedChannelProvider.provider().builderForTarget(target);
|
||||
|
|
|
|||
|
|
@ -41,10 +41,12 @@ import javax.annotation.concurrent.ThreadSafe;
|
|||
* A pluggable component that resolves a target URI (which is broken down into 3 parts as described
|
||||
* below) and return addresses to the caller.
|
||||
*
|
||||
* <p>The format of the target URI is {@code "[<scheme>:]<scheme-specific-string>"}
|
||||
* <p>The target URI is a {@link URI} instance. A {@code NameResolver} uses the scheme to determine
|
||||
* whether it can resolve a given URI, and uses the components after the scheme for actual
|
||||
* resolution.
|
||||
*
|
||||
* <p>{@code NameResolver} has no knowledge of load-balancing. The addresses of a target may be
|
||||
* changed over time, thus the caller registers a {@link Listener} to receive continuous updates.
|
||||
* <p>The addresses and attributes of a target may be changed over time, thus the caller registers a
|
||||
* {@link Listener} to receive continuous updates.
|
||||
*/
|
||||
@ExperimentalApi
|
||||
@ThreadSafe
|
||||
|
|
@ -71,7 +73,10 @@ public abstract class NameResolver {
|
|||
public abstract static class Factory {
|
||||
/**
|
||||
* Creates a {@link NameResolver} for the given target URI, or {@code null} if the given URI
|
||||
* cannot be resolved by this factory.
|
||||
* cannot be resolved by this factory. The decision should be solely based on the scheme of the
|
||||
* URI.
|
||||
*
|
||||
* @param targetUri the target URI to be resolved, whose scheme must not be {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public abstract NameResolver newNameResolver(URI targetUri);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright 2015, Google Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package io.grpc;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
/**
|
||||
* A registry that holds various {@link NameResolver.Factory}s and dispatches target URI to the
|
||||
* first one that can handle it.
|
||||
*/
|
||||
@ExperimentalApi
|
||||
@ThreadSafe
|
||||
public final class NameResolverRegistry extends NameResolver.Factory {
|
||||
private static final NameResolverRegistry defaultRegistry = new NameResolverRegistry();
|
||||
|
||||
private final CopyOnWriteArrayList<NameResolver.Factory> registry =
|
||||
new CopyOnWriteArrayList<NameResolver.Factory>();
|
||||
|
||||
private NameResolverRegistry() {
|
||||
// To prevent instantiation
|
||||
}
|
||||
|
||||
static {
|
||||
defaultRegistry.register(DnsNameResolverFactory.getInstance());
|
||||
}
|
||||
|
||||
public static NameResolverRegistry getDefaultRegistry() {
|
||||
return defaultRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a {@link NameResolver.Factory}.
|
||||
*/
|
||||
public void register(NameResolver.Factory factory) {
|
||||
registry.add(0, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link NameResolver} created by the first factory that can handle the given target
|
||||
* URI, or {@code null} if no one can handle it.
|
||||
*
|
||||
* <p>The factory that was registered later has higher priority.
|
||||
*/
|
||||
@Override
|
||||
public NameResolver newNameResolver(URI targetUri) {
|
||||
for (NameResolver.Factory factory : registry) {
|
||||
NameResolver resolver = factory.newNameResolver(targetUri);
|
||||
if (resolver != null) {
|
||||
return resolver;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -35,11 +35,11 @@ import com.google.common.base.Preconditions;
|
|||
|
||||
import io.grpc.Attributes;
|
||||
import io.grpc.ClientInterceptor;
|
||||
import io.grpc.DnsNameResolverFactory;
|
||||
import io.grpc.Internal;
|
||||
import io.grpc.LoadBalancer;
|
||||
import io.grpc.ManagedChannelBuilder;
|
||||
import io.grpc.NameResolver;
|
||||
import io.grpc.NameResolverRegistry;
|
||||
import io.grpc.ResolvedServerInfo;
|
||||
import io.grpc.SimpleLoadBalancerFactory;
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ public abstract class AbstractManagedChannelImplBuilder
|
|||
private Executor executor;
|
||||
private final List<ClientInterceptor> interceptors = new ArrayList<ClientInterceptor>();
|
||||
|
||||
private final URI target;
|
||||
private final String target;
|
||||
|
||||
@Nullable
|
||||
private final SocketAddress directServerAddress;
|
||||
|
|
@ -82,13 +82,13 @@ public abstract class AbstractManagedChannelImplBuilder
|
|||
@Nullable
|
||||
private LoadBalancer.Factory loadBalancerFactory;
|
||||
|
||||
protected AbstractManagedChannelImplBuilder(URI target) {
|
||||
protected AbstractManagedChannelImplBuilder(String target) {
|
||||
this.target = Preconditions.checkNotNull(target);
|
||||
this.directServerAddress = null;
|
||||
}
|
||||
|
||||
protected AbstractManagedChannelImplBuilder(SocketAddress directServerAddress, String authority) {
|
||||
this.target = URI.create("direct-address:///" + directServerAddress);
|
||||
this.target = "directaddress:///" + directServerAddress;
|
||||
this.directServerAddress = directServerAddress;
|
||||
this.nameResolverFactory = new DirectAddressNameResolverFactory(directServerAddress, authority);
|
||||
}
|
||||
|
|
@ -163,9 +163,10 @@ public abstract class AbstractManagedChannelImplBuilder
|
|||
target,
|
||||
// TODO(carl-mastrangelo): Allow clients to pass this in
|
||||
new ExponentialBackoffPolicy.Provider(),
|
||||
// TODO(zhangkun83): use a NameResolver registry for the "nameResolverFactory == null" case
|
||||
nameResolverFactory == null ? DnsNameResolverFactory.getInstance() : nameResolverFactory,
|
||||
loadBalancerFactory == null ? SimpleLoadBalancerFactory.getInstance() : loadBalancerFactory,
|
||||
nameResolverFactory == null ? NameResolverRegistry.getDefaultRegistry()
|
||||
: nameResolverFactory,
|
||||
loadBalancerFactory == null ? SimpleLoadBalancerFactory.getInstance()
|
||||
: loadBalancerFactory,
|
||||
transportFactory, executor, userAgent, interceptors);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ import io.grpc.internal.ClientCallImpl.ClientTransportProvider;
|
|||
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
|
@ -131,7 +132,7 @@ public final class ManagedChannelImpl extends ManagedChannel {
|
|||
}
|
||||
};
|
||||
|
||||
ManagedChannelImpl(URI targetUri, BackoffPolicy.Provider backoffPolicyProvider,
|
||||
ManagedChannelImpl(String target, BackoffPolicy.Provider backoffPolicyProvider,
|
||||
NameResolver.Factory nameResolverFactory, LoadBalancer.Factory loadBalancerFactory,
|
||||
ClientTransportFactory transportFactory, @Nullable Executor executor,
|
||||
@Nullable String userAgent, List<ClientInterceptor> interceptors) {
|
||||
|
|
@ -143,9 +144,38 @@ public final class ManagedChannelImpl extends ManagedChannel {
|
|||
this.executor = executor;
|
||||
}
|
||||
this.backoffPolicyProvider = backoffPolicyProvider;
|
||||
this.nameResolver = nameResolverFactory.newNameResolver(targetUri);
|
||||
Preconditions.checkArgument(this.nameResolver != null,
|
||||
"The given NameResolverFactory cannot resolve %s", targetUri);
|
||||
|
||||
// Finding a NameResolver. Try using the target string as the URI. If that fails, try prepending
|
||||
// "dns:///".
|
||||
NameResolver nameResolver = null;
|
||||
URI targetUri;
|
||||
StringBuilder uriSyntaxErrors = new StringBuilder();
|
||||
try {
|
||||
targetUri = new URI(target);
|
||||
nameResolver = nameResolverFactory.newNameResolver(targetUri);
|
||||
// For "localhost:8080" this would likely return null, because "localhost" is parsed as the
|
||||
// scheme. Will fall into the next branch and try "dns:///localhost:8080".
|
||||
} catch (URISyntaxException e) {
|
||||
// "foo.googleapis.com:8080" will trigger this exception, because "foo.googleapis.com" is an
|
||||
// invalid scheme. Just fall through and will try "dns:///foo.googleapis.com:8080"
|
||||
uriSyntaxErrors.append(e.getMessage());
|
||||
}
|
||||
if (nameResolver == null) {
|
||||
try {
|
||||
targetUri = new URI("dns:///" + target);
|
||||
nameResolver = nameResolverFactory.newNameResolver(targetUri);
|
||||
} catch (URISyntaxException e) {
|
||||
if (uriSyntaxErrors.length() > 0) {
|
||||
uriSyntaxErrors.append("; ");
|
||||
}
|
||||
uriSyntaxErrors.append(e.getMessage());
|
||||
}
|
||||
}
|
||||
Preconditions.checkArgument(nameResolver != null,
|
||||
"cannot find a NameResolver for %s%s", target,
|
||||
uriSyntaxErrors.length() > 0 ? " (" + uriSyntaxErrors.toString() + ")" : "");
|
||||
this.nameResolver = nameResolver;
|
||||
|
||||
this.loadBalancer = loadBalancerFactory.newLoadBalancer(nameResolver.getServiceAuthority(), tm);
|
||||
this.transportFactory = transportFactory;
|
||||
this.userAgent = userAgent;
|
||||
|
|
|
|||
|
|
@ -94,8 +94,8 @@ public class ManagedChannelImplTest {
|
|||
new StringMarshaller(), new IntegerMarshaller());
|
||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
private final String serviceName = "fake.example.com";
|
||||
private final URI target = URI.create("//" + serviceName);
|
||||
private final String authority = serviceName;
|
||||
private final String target = "fake://" + serviceName;
|
||||
private final SocketAddress socketAddress = new SocketAddress() {};
|
||||
private final ResolvedServerInfo server = new ResolvedServerInfo(socketAddress, Attributes.EMPTY);
|
||||
|
||||
|
|
@ -328,12 +328,11 @@ public class ManagedChannelImplTest {
|
|||
|
||||
@Override
|
||||
public NameResolver newNameResolver(final URI targetUri) {
|
||||
assertEquals(null, targetUri.getScheme());
|
||||
assertEquals("fake", targetUri.getScheme());
|
||||
assertEquals(serviceName, targetUri.getAuthority());
|
||||
return new NameResolver() {
|
||||
@Override public String getServiceAuthority() {
|
||||
assertNotNull(targetUri.toString() + " has authority", targetUri.getAuthority());
|
||||
return targetUri.getAuthority();
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
@Override public void start(final Listener listener) {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ import io.netty.handler.ssl.SslContext;
|
|||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URI;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
|
@ -89,14 +88,14 @@ public class NettyChannelBuilder extends AbstractManagedChannelImplBuilder<Netty
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a new builder with the given target URI that will be resolved by
|
||||
* Creates a new builder with the given target string that will be resolved by
|
||||
* {@link io.grpc.NameResolver}.
|
||||
*/
|
||||
public static NettyChannelBuilder forTarget(String targetUri) {
|
||||
return new NettyChannelBuilder(URI.create(targetUri));
|
||||
public static NettyChannelBuilder forTarget(String target) {
|
||||
return new NettyChannelBuilder(target);
|
||||
}
|
||||
|
||||
private NettyChannelBuilder(URI target) {
|
||||
private NettyChannelBuilder(String target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ import io.grpc.internal.SharedResourceHolder.Resource;
|
|||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
|
@ -103,11 +102,11 @@ public class OkHttpChannelBuilder extends
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a new builder for the given target URI that will be resolved by
|
||||
* Creates a new builder for the given target that will be resolved by
|
||||
* {@link io.grpc.NameResolver}.
|
||||
*/
|
||||
public static OkHttpChannelBuilder forTarget(String targetUri) {
|
||||
return new OkHttpChannelBuilder(URI.create(targetUri));
|
||||
public static OkHttpChannelBuilder forTarget(String target) {
|
||||
return new OkHttpChannelBuilder(target);
|
||||
}
|
||||
|
||||
private Executor transportExecutor;
|
||||
|
|
@ -118,10 +117,10 @@ public class OkHttpChannelBuilder extends
|
|||
private int maxMessageSize = DEFAULT_MAX_MESSAGE_SIZE;
|
||||
|
||||
protected OkHttpChannelBuilder(String host, int port) {
|
||||
this(URI.create("dns:///" + GrpcUtil.authorityFromHostAndPort(host, port)));
|
||||
this(GrpcUtil.authorityFromHostAndPort(host, port));
|
||||
}
|
||||
|
||||
private OkHttpChannelBuilder(URI target) {
|
||||
private OkHttpChannelBuilder(String target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue