Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
Expand All @@ -47,6 +48,7 @@
import org.apache.pulsar.client.impl.conf.ClientConfigurationData;
import org.apache.pulsar.common.allocator.PulsarByteBufAllocator;
import org.apache.pulsar.common.util.FutureUtil;
import org.apache.pulsar.common.util.netty.DnsResolverUtil;
import org.apache.pulsar.common.util.netty.EventLoopUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -62,13 +64,20 @@ public class ConnectionPool implements AutoCloseable {
private final boolean isSniProxy;

protected final DnsNameResolver dnsResolver;
private final boolean shouldCloseDnsResolver;

public ConnectionPool(ClientConfigurationData conf, EventLoopGroup eventLoopGroup) throws PulsarClientException {
this(conf, eventLoopGroup, () -> new ClientCnx(conf, eventLoopGroup));
}

public ConnectionPool(ClientConfigurationData conf, EventLoopGroup eventLoopGroup,
Supplier<ClientCnx> clientCnxSupplier) throws PulsarClientException {
Supplier<ClientCnx> clientCnxSupplier) throws PulsarClientException {
this(conf, eventLoopGroup, clientCnxSupplier, Optional.empty());
}

public ConnectionPool(ClientConfigurationData conf, EventLoopGroup eventLoopGroup,
Supplier<ClientCnx> clientCnxSupplier, Optional<DnsNameResolver> dnsNameResolver)
throws PulsarClientException {
this.eventLoopGroup = eventLoopGroup;
this.clientConfig = conf;
this.maxConnectionsPerHosts = conf.getConnectionsPerBroker();
Expand All @@ -91,14 +100,21 @@ public ConnectionPool(ClientConfigurationData conf, EventLoopGroup eventLoopGrou
log.error("Failed to create channel initializer");
throw new PulsarClientException(e);
}

this.shouldCloseDnsResolver = !dnsNameResolver.isPresent();
this.dnsResolver = dnsNameResolver.orElseGet(() -> createDnsNameResolver(conf, eventLoopGroup));
}

private static DnsNameResolver createDnsNameResolver(ClientConfigurationData conf, EventLoopGroup eventLoopGroup) {
DnsNameResolverBuilder dnsNameResolverBuilder = new DnsNameResolverBuilder(eventLoopGroup.next())
.traceEnabled(true).channelType(EventLoopUtil.getDatagramChannelClass(eventLoopGroup));
if (conf.getDnsLookupBindAddress() != null) {
InetSocketAddress addr = new InetSocketAddress(conf.getDnsLookupBindAddress(),
conf.getDnsLookupBindPort());
dnsNameResolverBuilder.localAddress(addr);
}
this.dnsResolver = dnsNameResolverBuilder.build();
DnsResolverUtil.applyJdkDnsCacheSettings(dnsNameResolverBuilder);
return dnsNameResolverBuilder.build();
}

private static final Random random = new Random();
Expand Down Expand Up @@ -320,7 +336,9 @@ public void releaseConnection(ClientCnx cnx) {
@Override
public void close() throws Exception {
closeAllConnections();
dnsResolver.close();
if (shouldCloseDnsResolver) {
dnsResolver.close();
}
}

private void cleanupConnection(InetSocketAddress address, int connectionKey,
Expand Down
5 changes: 5 additions & 0 deletions pulsar-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@
<artifactId>netty-handler</artifactId>
</dependency>

<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-resolver-dns</artifactId>
</dependency>

<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.pulsar.common.util.netty;

import io.netty.resolver.dns.DnsNameResolverBuilder;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class DnsResolverUtil {
private static final int MIN_TTL = 0;
private static final int TTL;
private static final int NEGATIVE_TTL;

// default TTL value when JDK setting is "forever" (-1)
private static final int DEFAULT_TTL = 60;

// default negative TTL value when JDK setting is "forever" (-1)
private static final int DEFAULT_NEGATIVE_TTL = 10;

static {
int ttl = DEFAULT_TTL;
int negativeTtl = DEFAULT_NEGATIVE_TTL;
try {
// use reflection to call sun.net.InetAddressCachePolicy's get and getNegative methods for getting
// effective JDK settings for DNS caching
Class<?> inetAddressCachePolicyClass = Class.forName("sun.net.InetAddressCachePolicy");
Method getTTLMethod = inetAddressCachePolicyClass.getMethod("get");
ttl = (Integer) getTTLMethod.invoke(null);
Method getNegativeTTLMethod = inetAddressCachePolicyClass.getMethod("getNegative");
negativeTtl = (Integer) getNegativeTTLMethod.invoke(null);
} catch (NoSuchMethodException | ClassNotFoundException | InvocationTargetException
| IllegalAccessException e) {
log.warn("Cannot get DNS TTL settings from sun.net.InetAddressCachePolicy class", e);
}
TTL = useDefaultTTLWhenSetToForever(ttl, DEFAULT_TTL);
NEGATIVE_TTL = useDefaultTTLWhenSetToForever(negativeTtl, DEFAULT_NEGATIVE_TTL);
}

private static int useDefaultTTLWhenSetToForever(int ttl, int defaultTtl) {
return ttl < 0 ? defaultTtl : ttl;
}

private DnsResolverUtil() {
// utility class with static methods, prevent instantiation
}

/**
* Configure Netty's {@link DnsNameResolverBuilder}'s ttl and negativeTtl to match the JDK's DNS caching settings.
* If the JDK setting for TTL is forever (-1), the TTL will be set to 60 seconds.
*
* @param dnsNameResolverBuilder The Netty {@link DnsNameResolverBuilder} instance to apply the settings
*/
public static void applyJdkDnsCacheSettings(DnsNameResolverBuilder dnsNameResolverBuilder) {
dnsNameResolverBuilder.ttl(MIN_TTL, TTL);
dnsNameResolverBuilder.negativeTtl(NEGATIVE_TTL);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.haproxy.HAProxyMessage;
import io.netty.handler.ssl.SslHandler;
import io.netty.resolver.dns.DnsNameResolver;
import java.net.SocketAddress;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
Expand Down Expand Up @@ -71,6 +73,7 @@ public class ProxyConnection extends PulsarHandler {
private final AtomicLong requestIdGenerator =
new AtomicLong(ThreadLocalRandom.current().nextLong(0, Long.MAX_VALUE / 2));
private final ProxyService service;
private final DnsNameResolver dnsNameResolver;
AuthenticationDataSource authenticationData;
private State state;
private final Supplier<SslHandler> sslHandlerSupplier;
Expand Down Expand Up @@ -119,9 +122,11 @@ ConnectionPool getConnectionPool() {
return connectionPool;
}

public ProxyConnection(ProxyService proxyService, Supplier<SslHandler> sslHandlerSupplier) {
public ProxyConnection(ProxyService proxyService, Supplier<SslHandler> sslHandlerSupplier,
DnsNameResolver dnsNameResolver) {
super(30, TimeUnit.SECONDS);
this.service = proxyService;
this.dnsNameResolver = dnsNameResolver;
this.state = State.Init;
this.sslHandlerSupplier = sslHandlerSupplier;
this.brokerProxyValidator = service.getBrokerProxyValidator();
Expand Down Expand Up @@ -229,27 +234,26 @@ public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exce
}

private synchronized void completeConnect(AuthData clientData) throws PulsarClientException {
Supplier<ClientCnx> clientCnxSupplier;
if (service.getConfiguration().isAuthenticationEnabled()) {
if (service.getConfiguration().isForwardAuthorizationCredentials()) {
this.clientAuthData = clientData;
this.clientAuthMethod = authMethod;
}
if (this.connectionPool == null) {
this.connectionPool = new ProxyConnectionPool(clientConf, service.getWorkerGroup(),
() -> new ProxyClientCnx(clientConf, service.getWorkerGroup(), clientAuthRole, clientAuthData,
clientAuthMethod, protocolVersionToAdvertise));
} else {
LOG.error("BUG! Connection Pool has already been created for proxy connection to {} state {} role {}",
remoteAddress, state, clientAuthRole);
}
clientCnxSupplier =
() -> new ProxyClientCnx(clientConf, service.getWorkerGroup(), clientAuthRole, clientAuthData,
clientAuthMethod, protocolVersionToAdvertise);
} else {
if (this.connectionPool == null) {
this.connectionPool = new ProxyConnectionPool(clientConf, service.getWorkerGroup(),
() -> new ClientCnx(clientConf, service.getWorkerGroup(), protocolVersionToAdvertise));
} else {
LOG.error("BUG! Connection Pool has already been created for proxy connection to {} state {}",
remoteAddress, state);
}
clientCnxSupplier =
() -> new ClientCnx(clientConf, service.getWorkerGroup(), protocolVersionToAdvertise);
}

if (this.connectionPool == null) {
this.connectionPool = new ConnectionPool(clientConf, service.getWorkerGroup(),
clientCnxSupplier, Optional.of(dnsNameResolver));
} else {
LOG.error("BUG! Connection Pool has already been created for proxy connection to {} state {} role {}",
remoteAddress, state, clientAuthRole);
}

LOG.info("[{}] complete connection, init proxy handler. authenticated with {} role {}, hasProxyToBrokerUrl: {}",
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
import org.apache.pulsar.client.impl.auth.AuthenticationDisabled;
import org.apache.pulsar.common.allocator.PulsarByteBufAllocator;
import org.apache.pulsar.common.configuration.PulsarConfigurationLoader;
import org.apache.pulsar.common.util.netty.DnsResolverUtil;
import org.apache.pulsar.common.util.netty.EventLoopUtil;
import org.apache.pulsar.metadata.api.MetadataStoreException;
import org.apache.pulsar.metadata.api.extended.MetadataStoreExtended;
Expand Down Expand Up @@ -163,6 +164,8 @@ public ProxyService(ProxyConfiguration proxyConfig,

DnsNameResolverBuilder dnsNameResolverBuilder = new DnsNameResolverBuilder(workerGroup.next())
.channelType(EventLoopUtil.getDatagramChannelClass(workerGroup));
DnsResolverUtil.applyJdkDnsCacheSettings(dnsNameResolverBuilder);

dnsNameResolver = dnsNameResolverBuilder.build();

brokerProxyValidator = new BrokerProxyValidator(dnsNameResolver.asAddressResolver(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ public SslHandler get() {
}

ch.pipeline().addLast("handler",
new ProxyConnection(proxyService, sslHandlerSupplier));
new ProxyConnection(proxyService, sslHandlerSupplier, proxyService.getDnsNameResolver()));

}
}