mirror of https://github.com/grpc/grpc-java.git
core: For Android, ignores DNS cache
There is a known issue that causes DNS lookup issue when network siwtchover on android. This issue is tracked separately in #4962. This change simply disables DNS cache to avoid the issue on Android.
This commit is contained in:
parent
0a7fa14042
commit
b7dc501bbe
|
|
@ -98,6 +98,8 @@ final class DnsNameResolver extends NameResolver {
|
||||||
* <p>Default value is -1 (cache forever) if security manager is installed. If security manager is
|
* <p>Default value is -1 (cache forever) if security manager is installed. If security manager is
|
||||||
* not installed, the ttl value is {@code null} which falls back to {@link
|
* not installed, the ttl value is {@code null} which falls back to {@link
|
||||||
* #DEFAULT_NETWORK_CACHE_TTL_SECONDS gRPC default value}.
|
* #DEFAULT_NETWORK_CACHE_TTL_SECONDS gRPC default value}.
|
||||||
|
*
|
||||||
|
* <p>For android, gRPC doesn't attempt to cache; this property value will be ignored.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final String NETWORKADDRESS_CACHE_TTL_PROPERTY = "networkaddress.cache.ttl";
|
static final String NETWORKADDRESS_CACHE_TTL_PROPERTY = "networkaddress.cache.ttl";
|
||||||
|
|
@ -143,7 +145,7 @@ final class DnsNameResolver extends NameResolver {
|
||||||
|
|
||||||
DnsNameResolver(@Nullable String nsAuthority, String name, Attributes params,
|
DnsNameResolver(@Nullable String nsAuthority, String name, Attributes params,
|
||||||
Resource<ExecutorService> executorResource, ProxyDetector proxyDetector,
|
Resource<ExecutorService> executorResource, ProxyDetector proxyDetector,
|
||||||
Stopwatch stopwatch) {
|
Stopwatch stopwatch, boolean isAndroid) {
|
||||||
// TODO: if a DNS server is provided as nsAuthority, use it.
|
// TODO: if a DNS server is provided as nsAuthority, use it.
|
||||||
// https://www.captechconsulting.com/blogs/accessing-the-dusty-corners-of-dns-with-java
|
// https://www.captechconsulting.com/blogs/accessing-the-dusty-corners-of-dns-with-java
|
||||||
this.executorResource = executorResource;
|
this.executorResource = executorResource;
|
||||||
|
|
@ -166,7 +168,7 @@ final class DnsNameResolver extends NameResolver {
|
||||||
port = nameUri.getPort();
|
port = nameUri.getPort();
|
||||||
}
|
}
|
||||||
this.proxyDetector = proxyDetector;
|
this.proxyDetector = proxyDetector;
|
||||||
this.resolveRunnable = new Resolve(this, stopwatch);
|
this.resolveRunnable = new Resolve(this, stopwatch, getNetworkAddressCacheTtlNanos(isAndroid));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -196,10 +198,10 @@ final class DnsNameResolver extends NameResolver {
|
||||||
private final long cacheTtlNanos;
|
private final long cacheTtlNanos;
|
||||||
private ResolutionResults cachedResolutionResults = null;
|
private ResolutionResults cachedResolutionResults = null;
|
||||||
|
|
||||||
Resolve(DnsNameResolver resolver, Stopwatch stopwatch) {
|
Resolve(DnsNameResolver resolver, Stopwatch stopwatch, long cacheTtlNanos) {
|
||||||
this.resolver = resolver;
|
this.resolver = resolver;
|
||||||
this.stopwatch = Preconditions.checkNotNull(stopwatch, "stopwatch");
|
this.stopwatch = Preconditions.checkNotNull(stopwatch, "stopwatch");
|
||||||
this.cacheTtlNanos = getNetworkAddressCacheTtlNanos();
|
this.cacheTtlNanos = cacheTtlNanos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -230,23 +232,6 @@ final class DnsNameResolver extends NameResolver {
|
||||||
|| (cacheTtlNanos > 0 && stopwatch.elapsed(TimeUnit.NANOSECONDS) > cacheTtlNanos);
|
|| (cacheTtlNanos > 0 && stopwatch.elapsed(TimeUnit.NANOSECONDS) > cacheTtlNanos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns value of network address cache ttl property. */
|
|
||||||
private static long getNetworkAddressCacheTtlNanos() {
|
|
||||||
String cacheTtlPropertyValue = System.getProperty(NETWORKADDRESS_CACHE_TTL_PROPERTY);
|
|
||||||
long cacheTtl = DEFAULT_NETWORK_CACHE_TTL_SECONDS;
|
|
||||||
if (cacheTtlPropertyValue != null) {
|
|
||||||
try {
|
|
||||||
cacheTtl = Long.parseLong(cacheTtlPropertyValue);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
logger.log(
|
|
||||||
Level.WARNING,
|
|
||||||
"Property({0}) valid is not valid number format({1}), fall back to default({2})",
|
|
||||||
new Object[] {NETWORKADDRESS_CACHE_TTL_PROPERTY, cacheTtlPropertyValue, cacheTtl});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cacheTtl > 0 ? TimeUnit.SECONDS.toNanos(cacheTtl) : cacheTtl;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void resolveInternal(Listener savedListener) {
|
void resolveInternal(Listener savedListener) {
|
||||||
InetSocketAddress destination =
|
InetSocketAddress destination =
|
||||||
|
|
@ -483,6 +468,31 @@ final class DnsNameResolver extends NameResolver {
|
||||||
ServiceConfigUtil.getList(serviceConfigChoice, SERVICE_CONFIG_CHOICE_CLIENT_HOSTNAME_KEY));
|
ServiceConfigUtil.getList(serviceConfigChoice, SERVICE_CONFIG_CHOICE_CLIENT_HOSTNAME_KEY));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns value of network address cache ttl property if not Android environment. For android,
|
||||||
|
* DnsNameResolver does not cache the dns lookup result.
|
||||||
|
*/
|
||||||
|
private static long getNetworkAddressCacheTtlNanos(boolean isAndroid) {
|
||||||
|
if (isAndroid) {
|
||||||
|
// on Android, ignore dns cache.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
String cacheTtlPropertyValue = System.getProperty(NETWORKADDRESS_CACHE_TTL_PROPERTY);
|
||||||
|
long cacheTtl = DEFAULT_NETWORK_CACHE_TTL_SECONDS;
|
||||||
|
if (cacheTtlPropertyValue != null) {
|
||||||
|
try {
|
||||||
|
cacheTtl = Long.parseLong(cacheTtlPropertyValue);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
logger.log(
|
||||||
|
Level.WARNING,
|
||||||
|
"Property({0}) valid is not valid number format({1}), fall back to default({2})",
|
||||||
|
new Object[] {NETWORKADDRESS_CACHE_TTL_PROPERTY, cacheTtlPropertyValue, cacheTtl});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cacheTtl > 0 ? TimeUnit.SECONDS.toNanos(cacheTtl) : cacheTtl;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if a given Service Config choice applies, and if so, returns it.
|
* Determines if a given Service Config choice applies, and if so, returns it.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package io.grpc.internal;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.base.Stopwatch;
|
import com.google.common.base.Stopwatch;
|
||||||
import io.grpc.Attributes;
|
import io.grpc.Attributes;
|
||||||
|
import io.grpc.InternalServiceProviders;
|
||||||
import io.grpc.NameResolverProvider;
|
import io.grpc.NameResolverProvider;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
|
|
@ -54,7 +55,8 @@ public final class DnsNameResolverProvider extends NameResolverProvider {
|
||||||
params,
|
params,
|
||||||
GrpcUtil.SHARED_CHANNEL_EXECUTOR,
|
GrpcUtil.SHARED_CHANNEL_EXECUTOR,
|
||||||
GrpcUtil.getDefaultProxyDetector(),
|
GrpcUtil.getDefaultProxyDetector(),
|
||||||
Stopwatch.createUnstarted());
|
Stopwatch.createUnstarted(),
|
||||||
|
InternalServiceProviders.isAndroid(getClass().getClassLoader()));
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -118,18 +118,34 @@ public class DnsNameResolverTest {
|
||||||
return newResolver(name, port, GrpcUtil.NOOP_PROXY_DETECTOR, Stopwatch.createUnstarted());
|
return newResolver(name, port, GrpcUtil.NOOP_PROXY_DETECTOR, Stopwatch.createUnstarted());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DnsNameResolver newResolver(String name, int port, boolean isAndroid) {
|
||||||
|
return
|
||||||
|
newResolver(
|
||||||
|
name, port, GrpcUtil.NOOP_PROXY_DETECTOR, Stopwatch.createUnstarted(), isAndroid);
|
||||||
|
}
|
||||||
|
|
||||||
private DnsNameResolver newResolver(
|
private DnsNameResolver newResolver(
|
||||||
String name,
|
String name,
|
||||||
int port,
|
int port,
|
||||||
ProxyDetector proxyDetector,
|
ProxyDetector proxyDetector,
|
||||||
Stopwatch stopwatch) {
|
Stopwatch stopwatch) {
|
||||||
|
return newResolver(name, port, proxyDetector, stopwatch, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DnsNameResolver newResolver(
|
||||||
|
String name,
|
||||||
|
int port,
|
||||||
|
ProxyDetector proxyDetector,
|
||||||
|
Stopwatch stopwatch,
|
||||||
|
boolean isAndroid) {
|
||||||
DnsNameResolver dnsResolver = new DnsNameResolver(
|
DnsNameResolver dnsResolver = new DnsNameResolver(
|
||||||
null,
|
null,
|
||||||
name,
|
name,
|
||||||
Attributes.newBuilder().set(NameResolver.Factory.PARAMS_DEFAULT_PORT, port).build(),
|
Attributes.newBuilder().set(NameResolver.Factory.PARAMS_DEFAULT_PORT, port).build(),
|
||||||
fakeExecutorResource,
|
fakeExecutorResource,
|
||||||
proxyDetector,
|
proxyDetector,
|
||||||
stopwatch);
|
stopwatch,
|
||||||
|
isAndroid);
|
||||||
return dnsResolver;
|
return dnsResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,14 +216,30 @@ public class DnsNameResolverTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolve_androidIgnoresPropertyValue() throws Exception {
|
||||||
|
System.setProperty(DnsNameResolver.NETWORKADDRESS_CACHE_TTL_PROPERTY, Long.toString(2));
|
||||||
|
resolveNeverCache(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolve_androidIgnoresPropertyValueCacheForever() throws Exception {
|
||||||
|
System.setProperty(DnsNameResolver.NETWORKADDRESS_CACHE_TTL_PROPERTY, Long.toString(-1));
|
||||||
|
resolveNeverCache(true);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolve_neverCache() throws Exception {
|
public void resolve_neverCache() throws Exception {
|
||||||
System.setProperty(DnsNameResolver.NETWORKADDRESS_CACHE_TTL_PROPERTY, "0");
|
System.setProperty(DnsNameResolver.NETWORKADDRESS_CACHE_TTL_PROPERTY, "0");
|
||||||
|
resolveNeverCache(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resolveNeverCache(boolean isAndroid) throws Exception {
|
||||||
final List<InetAddress> answer1 = createAddressList(2);
|
final List<InetAddress> answer1 = createAddressList(2);
|
||||||
final List<InetAddress> answer2 = createAddressList(1);
|
final List<InetAddress> answer2 = createAddressList(1);
|
||||||
String name = "foo.googleapis.com";
|
String name = "foo.googleapis.com";
|
||||||
|
|
||||||
DnsNameResolver resolver = newResolver(name, 81);
|
DnsNameResolver resolver = newResolver(name, 81, isAndroid);
|
||||||
AddressResolver mockResolver = mock(AddressResolver.class);
|
AddressResolver mockResolver = mock(AddressResolver.class);
|
||||||
when(mockResolver.resolveAddress(Matchers.anyString())).thenReturn(answer1).thenReturn(answer2);
|
when(mockResolver.resolveAddress(Matchers.anyString())).thenReturn(answer1).thenReturn(answer2);
|
||||||
resolver.setAddressResolver(mockResolver);
|
resolver.setAddressResolver(mockResolver);
|
||||||
|
|
@ -241,7 +273,7 @@ public class DnsNameResolverTest {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
new DnsNameResolver.Resolve(nrf, Stopwatch.createUnstarted()).resolveInternal(mockListener);
|
new DnsNameResolver.Resolve(nrf, Stopwatch.createUnstarted(), 0).resolveInternal(mockListener);
|
||||||
|
|
||||||
ArgumentCaptor<Status> ac = ArgumentCaptor.forClass(Status.class);
|
ArgumentCaptor<Status> ac = ArgumentCaptor.forClass(Status.class);
|
||||||
verify(mockListener).onError(ac.capture());
|
verify(mockListener).onError(ac.capture());
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue