Skip to content

Commit b53cae0

Browse files
authored
HDFS-16157. Support configuring DNS record to get list of journal nodes contributed by Leon Gao. (#3284)
* Add DNS resolution for QJM * Add log * Resolve comments * checkstyle * typo
1 parent ad54f51 commit b53cae0

File tree

9 files changed

+113
-14
lines changed

9 files changed

+113
-14
lines changed

hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ha/TestZKFailoverControllerStress.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public void testRandomHealthAndDisconnects() throws Exception {
128128
// Mockito errors if the HM calls the proxy in the middle of
129129
// setting up the mock.
130130
cluster.start();
131-
131+
132132
long st = Time.now();
133133
while (Time.now() - st < runFor) {
134134
cluster.getTestContext().checkException();

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSConfigKeys.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,17 @@ public class DFSConfigKeys extends CommonConfigurationKeys {
645645
public static final String DFS_NAMENODE_EDITS_PLUGIN_PREFIX = "dfs.namenode.edits.journal-plugin";
646646
public static final String DFS_NAMENODE_EDITS_DIR_REQUIRED_KEY = "dfs.namenode.edits.dir.required";
647647
public static final String DFS_NAMENODE_EDITS_DIR_DEFAULT = "file:///tmp/hadoop/dfs/name";
648+
649+
public static final String
650+
DFS_NAMENODE_EDITS_QJOURNALS_RESOLUTION_ENABLED =
651+
"dfs.namenode.edits.qjournals.resolution-enabled";
652+
public static final boolean
653+
DFS_NAMENODE_EDITS_QJOURNALS_RESOLUTION_ENABLED_DEFAULT = false;
654+
655+
public static final String
656+
DFS_NAMENODE_EDITS_QJOURNALS_RESOLUTION_RESOLVER_IMPL =
657+
"dfs.namenode.edits.qjournals.resolver.impl";
658+
648659
public static final String DFS_METRICS_SESSION_ID_KEY =
649660
HdfsClientConfigKeys.DeprecatedKeys.DFS_METRICS_SESSION_ID_KEY;
650661
public static final String DFS_METRICS_PERCENTILES_INTERVALS_KEY = "dfs.metrics.percentiles.intervals";

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSUtil.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ public static Set<String> getJournalNodeAddresses(
490490
" to append it with namenodeId");
491491
URI uri = new URI(journalsUri);
492492
List<InetSocketAddress> socketAddresses = Util.
493-
getAddressesList(uri);
493+
getAddressesList(uri, conf);
494494
for (InetSocketAddress is : socketAddresses) {
495495
journalNodeList.add(is.getHostName());
496496
}
@@ -501,7 +501,7 @@ public static Set<String> getJournalNodeAddresses(
501501
} else {
502502
URI uri = new URI(journalsUri);
503503
List<InetSocketAddress> socketAddresses = Util.
504-
getAddressesList(uri);
504+
getAddressesList(uri, conf);
505505
for (InetSocketAddress is : socketAddresses) {
506506
journalNodeList.add(is.getHostName());
507507
}
@@ -512,7 +512,7 @@ public static Set<String> getJournalNodeAddresses(
512512
return journalNodeList;
513513
} else {
514514
URI uri = new URI(journalsUri);
515-
List<InetSocketAddress> socketAddresses = Util.getAddressesList(uri);
515+
List<InetSocketAddress> socketAddresses = Util.getAddressesList(uri, conf);
516516
for (InetSocketAddress is : socketAddresses) {
517517
journalNodeList.add(is.getHostName());
518518
}

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/qjournal/client/QuorumJournalManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ static List<AsyncLogger> createLoggers(Configuration conf,
414414
String nameServiceId)
415415
throws IOException {
416416
List<AsyncLogger> ret = Lists.newArrayList();
417-
List<InetSocketAddress> addrs = Util.getAddressesList(uri);
417+
List<InetSocketAddress> addrs = Util.getAddressesList(uri, conf);
418418
if (addrs.size() % 2 == 0) {
419419
LOG.warn("Quorum journal URI '" + uri + "' has an even number " +
420420
"of Journal Nodes specified. This is not recommended!");

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/qjournal/server/JournalNodeSyncer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ private List<InetSocketAddress> getJournalAddrList(String uriStr) throws
315315
IOException {
316316
URI uri = new URI(uriStr);
317317
return Util.getLoggerAddresses(uri,
318-
Sets.newHashSet(jn.getBoundIpcAddress()));
318+
Sets.newHashSet(jn.getBoundIpcAddress()), conf);
319319
}
320320

321321
private void getMissingLogSegments(List<RemoteEditLog> thisJournalEditLogs,

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/common/Util.java

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
import org.apache.hadoop.hdfs.server.namenode.ImageServlet;
4444
import org.apache.hadoop.hdfs.util.DataTransferThrottler;
4545
import org.apache.hadoop.io.MD5Hash;
46+
import org.apache.hadoop.net.DomainNameResolver;
47+
import org.apache.hadoop.net.DomainNameResolverFactory;
4648
import org.apache.hadoop.net.NetUtils;
4749
import org.apache.hadoop.security.UserGroupInformation;
4850
import org.apache.hadoop.security.authentication.client.AuthenticationException;
@@ -361,7 +363,7 @@ private static MD5Hash parseMD5Header(HttpURLConnection connection) {
361363
return (header != null) ? new MD5Hash(header) : null;
362364
}
363365

364-
public static List<InetSocketAddress> getAddressesList(URI uri)
366+
public static List<InetSocketAddress> getAddressesList(URI uri, Configuration conf)
365367
throws IOException{
366368
String authority = uri.getAuthority();
367369
Preconditions.checkArgument(authority != null && !authority.isEmpty(),
@@ -372,21 +374,49 @@ public static List<InetSocketAddress> getAddressesList(URI uri)
372374
parts[i] = parts[i].trim();
373375
}
374376

377+
boolean resolveNeeded = conf.getBoolean(
378+
DFSConfigKeys.DFS_NAMENODE_EDITS_QJOURNALS_RESOLUTION_ENABLED,
379+
DFSConfigKeys.DFS_NAMENODE_EDITS_QJOURNALS_RESOLUTION_ENABLED_DEFAULT);
380+
DomainNameResolver dnr = DomainNameResolverFactory.newInstance(
381+
conf,
382+
DFSConfigKeys.DFS_NAMENODE_EDITS_QJOURNALS_RESOLUTION_RESOLVER_IMPL);
383+
375384
List<InetSocketAddress> addrs = Lists.newArrayList();
376385
for (String addr : parts) {
377-
InetSocketAddress isa = NetUtils.createSocketAddr(
378-
addr, DFSConfigKeys.DFS_JOURNALNODE_RPC_PORT_DEFAULT);
379-
if (isa.isUnresolved()) {
380-
throw new UnknownHostException(addr);
386+
if (resolveNeeded) {
387+
LOG.info("Resolving journal address: " + addr);
388+
InetSocketAddress isa = NetUtils.createSocketAddr(
389+
addr, DFSConfigKeys.DFS_JOURNALNODE_RPC_PORT_DEFAULT);
390+
// Get multiple hostnames from domain name if needed,
391+
// for example multiple hosts behind a DNS entry.
392+
int port = isa.getPort();
393+
// QJM should just use FQDN
394+
String[] hostnames = dnr
395+
.getAllResolvedHostnameByDomainName(isa.getHostName(), true);
396+
if (hostnames.length == 0) {
397+
throw new UnknownHostException(addr);
398+
}
399+
for (String h : hostnames) {
400+
addrs.add(NetUtils.createSocketAddr(
401+
h + ":" + port,
402+
DFSConfigKeys.DFS_JOURNALNODE_RPC_PORT_DEFAULT)
403+
);
404+
}
405+
} else {
406+
InetSocketAddress isa = NetUtils.createSocketAddr(
407+
addr, DFSConfigKeys.DFS_JOURNALNODE_RPC_PORT_DEFAULT);
408+
if (isa.isUnresolved()) {
409+
throw new UnknownHostException(addr);
410+
}
411+
addrs.add(isa);
381412
}
382-
addrs.add(isa);
383413
}
384414
return addrs;
385415
}
386416

387417
public static List<InetSocketAddress> getLoggerAddresses(URI uri,
388-
Set<InetSocketAddress> addrsToExclude) throws IOException {
389-
List<InetSocketAddress> addrsList = getAddressesList(uri);
418+
Set<InetSocketAddress> addrsToExclude, Configuration conf) throws IOException {
419+
List<InetSocketAddress> addrsList = getAddressesList(uri, conf);
390420
addrsList.removeAll(addrsToExclude);
391421
return addrsList;
392422
}

hadoop-hdfs-project/hadoop-hdfs/src/main/resources/hdfs-default.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,25 @@
502502
<value>org.apache.hadoop.hdfs.qjournal.client.QuorumJournalManager</value>
503503
</property>
504504

505+
<property>
506+
<name>dfs.namenode.edits.qjournals.resolution-enabled</name>
507+
<value>false</value>
508+
<description>
509+
Determines if the given qjournals address is a domain name which needs to
510+
be resolved.
511+
This is used by namenode to resolve qjournals.
512+
</description>
513+
</property>
514+
515+
<property>
516+
<name>dfs.namenode.edits.qjournals.resolver.impl</name>
517+
<value></value>
518+
<description>
519+
Qjournals resolver implementation used by namenode.
520+
Effective with dfs.namenode.edits.qjournals.resolution-enabled on.
521+
</description>
522+
</property>
523+
505524
<property>
506525
<name>dfs.permissions.enabled</name>
507526
<value>true</value>

hadoop-hdfs-project/hadoop-hdfs/src/site/markdown/HDFSHighAvailabilityWithQJM.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,15 @@ The order in which you set these configurations is unimportant, but the values y
194194
<value>qjournal://node1.example.com:8485;node2.example.com:8485;node3.example.com:8485/mycluster</value>
195195
</property>
196196

197+
You can also configure journal nodes by setting up dns round-robin record to avoid hardcoded names:
198+
199+
<property>
200+
<name>dfs.namenode.edits.qjournals.resolution-enabled</name>
201+
<value>true</value>
202+
</property>
203+
204+
This will require you to configure multiple IPs behind one dns record on the host level, for example round robin DNS.
205+
197206
* **dfs.client.failover.proxy.provider.[nameservice ID]** - the Java class that HDFS clients use to contact the Active NameNode
198207

199208
Configure the name of the Java class which will be used by the DFS Client to

hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/qjournal/client/TestQuorumJournalManager.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,17 @@
3333
import java.io.File;
3434
import java.io.IOException;
3535
import java.net.InetSocketAddress;
36+
import java.net.URI;
3637
import java.net.URISyntaxException;
3738
import java.net.URL;
39+
import java.net.UnknownHostException;
3840
import java.util.ArrayList;
3941
import java.util.List;
4042
import java.util.concurrent.ExecutorService;
4143
import java.util.concurrent.TimeoutException;
4244

45+
import org.apache.hadoop.hdfs.server.common.Util;
46+
import org.apache.hadoop.net.MockDomainNameResolver;
4347
import org.apache.hadoop.util.Lists;
4448
import org.slf4j.Logger;
4549
import org.slf4j.LoggerFactory;
@@ -1123,6 +1127,32 @@ public void testSelectViaRpcAfterJNRestart() throws Exception {
11231127
Mockito.verify(logger, Mockito.times(1)).getEditLogManifest(1, true);
11241128
}
11251129
}
1130+
1131+
@Test
1132+
public void testGetJournalAddressListWithResolution() throws Exception {
1133+
Configuration configuration = new Configuration();
1134+
configuration.setBoolean(
1135+
DFSConfigKeys.DFS_NAMENODE_EDITS_QJOURNALS_RESOLUTION_ENABLED, true);
1136+
configuration.set(
1137+
DFSConfigKeys.DFS_NAMENODE_EDITS_QJOURNALS_RESOLUTION_RESOLVER_IMPL,
1138+
MockDomainNameResolver.class.getName());
1139+
1140+
URI uriWithDomain = URI.create("qjournal://"
1141+
+ MockDomainNameResolver.DOMAIN + ":1234" + "/testns");
1142+
List<InetSocketAddress> result = Util.getAddressesList(uriWithDomain, configuration);
1143+
assertEquals(2, result.size());
1144+
assertEquals(new InetSocketAddress(MockDomainNameResolver.FQDN_1, 1234), result.get(0));
1145+
assertEquals(new InetSocketAddress(MockDomainNameResolver.FQDN_2, 1234), result.get(1));
1146+
1147+
uriWithDomain = URI.create("qjournal://"
1148+
+ MockDomainNameResolver.UNKNOW_DOMAIN + ":1234" + "/testns");
1149+
try{
1150+
Util.getAddressesList(uriWithDomain, configuration);
1151+
fail("Should throw unknown host exception.");
1152+
} catch (UnknownHostException e) {
1153+
// expected
1154+
}
1155+
}
11261156

11271157
private QuorumJournalManager createSpyingQJM()
11281158
throws IOException, URISyntaxException {

0 commit comments

Comments
 (0)