Skip to content

Commit cc4268a

Browse files
authored
HBASE-27304 Support using IP to expose master/rs servers for some special scenarios (#4713)
Signed-off-by: Duo Zhang <zhangduo@apache.org>
1 parent de127bd commit cc4268a

File tree

7 files changed

+335
-4
lines changed

7 files changed

+335
-4
lines changed

hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,6 +1571,22 @@ public enum OperationStatusCode {
15711571
*/
15721572
public static final int BATCH_ROWS_THRESHOLD_DEFAULT = 5000;
15731573

1574+
/**
1575+
* In some scenarios, such as the elastic scaling scenario on the cloud, the HBase client may not
1576+
* be able to resolve the hostname of the newly added node. If the network is interconnected, the
1577+
* client can actually access the HBase cluster nodes through ip. However, since the HBase client
1578+
* obtains the Master/RS address info from or the ZK or the meta table, so the Master/RS of the
1579+
* HBase cluster needs to expose the service with ip instead of the hostname. Therefore, We can
1580+
* use hostname by default, but at the same time, we can also provide a config to support whether
1581+
* to use ip for Master/RS service. See HBASE-27304 for details.
1582+
*/
1583+
public final static String HBASE_SERVER_USEIP_ENABLED_KEY = "hbase.server.useip.enabled";
1584+
1585+
/**
1586+
* Default value of {@link #HBASE_SERVER_USEIP_ENABLED_KEY}
1587+
*/
1588+
public final static boolean HBASE_SERVER_USEIP_ENABLED_DEFAULT = false;
1589+
15741590
private HConstants() {
15751591
// Can't be instantiated with this ctor.
15761592
}

hbase-server/src/main/java/org/apache/hadoop/hbase/HBaseServerBase.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,15 @@ public HBaseServerBase(Configuration conf, String name) throws IOException {
252252
this.rpcServices = createRpcServices();
253253
useThisHostnameInstead = getUseThisHostnameInstead(conf);
254254
InetSocketAddress addr = rpcServices.getSocketAddress();
255-
String hostName = StringUtils.isBlank(useThisHostnameInstead)
256-
? addr.getHostName()
257-
: this.useThisHostnameInstead;
255+
256+
// if use-ip is enabled, we will use ip to expose Master/RS service for client,
257+
// see HBASE-27304 for details.
258+
boolean useIp = conf.getBoolean(HConstants.HBASE_SERVER_USEIP_ENABLED_KEY,
259+
HConstants.HBASE_SERVER_USEIP_ENABLED_DEFAULT);
260+
String isaHostName =
261+
useIp ? addr.getAddress().getHostAddress() : addr.getAddress().getHostName();
262+
String hostName =
263+
StringUtils.isBlank(useThisHostnameInstead) ? isaHostName : useThisHostnameInstead;
258264
serverName = ServerName.valueOf(hostName, addr.getPort(), this.startcode);
259265
// login the zookeeper client principal (if using security)
260266
ZKAuthentication.loginClient(this.conf, HConstants.ZK_CLIENT_KEYTAB_FILE,

hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,13 @@ ServerName regionServerStartup(RegionServerStartupRequest request, int versionNu
219219
// in, it should have been removed from serverAddressToServerInfo and queued
220220
// for processing by ProcessServerShutdown.
221221

222+
// if use-ip is enabled, we will use ip to expose Master/RS service for client,
223+
// see HBASE-27304 for details.
224+
boolean useIp = master.getConfiguration().getBoolean(HConstants.HBASE_SERVER_USEIP_ENABLED_KEY,
225+
HConstants.HBASE_SERVER_USEIP_ENABLED_DEFAULT);
226+
String isaHostName = useIp ? ia.getHostAddress() : ia.getHostName();
222227
final String hostname =
223-
request.hasUseThisHostnameInstead() ? request.getUseThisHostnameInstead() : ia.getHostName();
228+
request.hasUseThisHostnameInstead() ? request.getUseThisHostnameInstead() : isaHostName;
224229
ServerName sn = ServerName.valueOf(hostname, request.getPort(), request.getServerStartCode());
225230
checkClockSkew(sn, request.getServerCurrentTime());
226231
checkIsDead(sn, "STARTUP");

hbase-server/src/main/java/org/apache/hadoop/hbase/util/RegionMover.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.io.FileInputStream;
2727
import java.io.FileOutputStream;
2828
import java.io.IOException;
29+
import java.net.InetAddress;
2930
import java.nio.file.Files;
3031
import java.nio.file.Paths;
3132
import java.util.ArrayList;
@@ -67,6 +68,7 @@
6768
import org.slf4j.Logger;
6869
import org.slf4j.LoggerFactory;
6970

71+
import org.apache.hbase.thirdparty.com.google.common.net.InetAddresses;
7072
import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine;
7173
import org.apache.hbase.thirdparty.org.apache.commons.collections4.CollectionUtils;
7274

@@ -116,6 +118,17 @@ private RegionMover(RegionMoverBuilder builder) throws IOException {
116118
setConf(builder.conf);
117119
this.conn = ConnectionFactory.createConnection(conf);
118120
this.admin = conn.getAdmin();
121+
122+
// if the hostname of master is ip, it indicates that the master/RS has enabled use-ip, we need
123+
// to resolve the current hostname to ip to ensure that the RegionMover logic can be executed
124+
// normally, see HBASE-27304 for details.
125+
ServerName master = admin.getClusterMetrics(EnumSet.of(Option.MASTER)).getMasterName();
126+
if (InetAddresses.isInetAddress(master.getHostname())) {
127+
if (!InetAddresses.isInetAddress(this.hostname)) {
128+
this.hostname = InetAddress.getByName(this.hostname).getHostAddress();
129+
}
130+
}
131+
119132
// Only while running unit tests, builder.rackManager will not be null for the convenience of
120133
// providing custom rackManager. Otherwise for regular workflow/user triggered action,
121134
// builder.rackManager is supposed to be null. Hence, setter of builder.rackManager is
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hbase.master;
19+
20+
import static org.junit.Assert.assertEquals;
21+
22+
import java.net.InetAddress;
23+
import org.apache.hadoop.conf.Configuration;
24+
import org.apache.hadoop.hbase.HBaseClassTestRule;
25+
import org.apache.hadoop.hbase.HBaseConfiguration;
26+
import org.apache.hadoop.hbase.HBaseTestingUtil;
27+
import org.apache.hadoop.hbase.HConstants;
28+
import org.apache.hadoop.hbase.SingleProcessHBaseCluster;
29+
import org.apache.hadoop.hbase.StartTestingClusterOption;
30+
import org.apache.hadoop.hbase.testclassification.MasterTests;
31+
import org.apache.hadoop.hbase.testclassification.MediumTests;
32+
import org.junit.After;
33+
import org.junit.Before;
34+
import org.junit.ClassRule;
35+
import org.junit.Test;
36+
import org.junit.experimental.categories.Category;
37+
import org.slf4j.Logger;
38+
import org.slf4j.LoggerFactory;
39+
40+
@Category({ MasterTests.class, MediumTests.class })
41+
public class TestMasterUseIp {
42+
@ClassRule
43+
public static final HBaseClassTestRule CLASS_RULE =
44+
HBaseClassTestRule.forClass(TestMasterUseIp.class);
45+
46+
private static final Logger LOG = LoggerFactory.getLogger(TestMasterUseIp.class);
47+
48+
private HBaseTestingUtil TEST_UTIL;
49+
private SingleProcessHBaseCluster CLUSTER;
50+
51+
private static final int NUM_MASTERS = 1;
52+
private static final int NUM_RS = 1;
53+
54+
@Before
55+
public void setup() throws Exception {
56+
Configuration conf = HBaseConfiguration.create();
57+
conf.setBoolean(HConstants.HBASE_SERVER_USEIP_ENABLED_KEY, true);
58+
TEST_UTIL = new HBaseTestingUtil(conf);
59+
StartTestingClusterOption option = StartTestingClusterOption.builder().numMasters(NUM_MASTERS)
60+
.numRegionServers(NUM_RS).numDataNodes(NUM_RS).build();
61+
CLUSTER = TEST_UTIL.startMiniCluster(option);
62+
}
63+
64+
@After
65+
public void teardown() throws Exception {
66+
TEST_UTIL.shutdownMiniCluster();
67+
}
68+
69+
@Test
70+
public void testMasterUseIp() throws Exception {
71+
String hostname = CLUSTER.getMaster(0).getServerName().getHostname();
72+
String ip = InetAddress.getByName(hostname).getHostAddress();
73+
LOG.info("hostname= " + hostname + " ,ip=" + ip);
74+
assertEquals(ip, hostname);
75+
}
76+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hbase.regionserver;
19+
20+
import static org.junit.Assert.assertEquals;
21+
22+
import java.net.InetAddress;
23+
import org.apache.hadoop.conf.Configuration;
24+
import org.apache.hadoop.hbase.HBaseClassTestRule;
25+
import org.apache.hadoop.hbase.HBaseConfiguration;
26+
import org.apache.hadoop.hbase.HBaseTestingUtil;
27+
import org.apache.hadoop.hbase.HConstants;
28+
import org.apache.hadoop.hbase.SingleProcessHBaseCluster;
29+
import org.apache.hadoop.hbase.StartTestingClusterOption;
30+
import org.apache.hadoop.hbase.testclassification.MediumTests;
31+
import org.apache.hadoop.hbase.testclassification.RegionServerTests;
32+
import org.junit.After;
33+
import org.junit.Before;
34+
import org.junit.ClassRule;
35+
import org.junit.Test;
36+
import org.junit.experimental.categories.Category;
37+
import org.slf4j.Logger;
38+
import org.slf4j.LoggerFactory;
39+
40+
@Category({ RegionServerTests.class, MediumTests.class })
41+
public class TestRegionServerUseIp {
42+
@ClassRule
43+
public static final HBaseClassTestRule CLASS_RULE =
44+
HBaseClassTestRule.forClass(TestRegionServerUseIp.class);
45+
46+
private static final Logger LOG = LoggerFactory.getLogger(TestRegionServerUseIp.class);
47+
48+
private HBaseTestingUtil TEST_UTIL;
49+
private SingleProcessHBaseCluster CLUSTER;
50+
51+
private static final int NUM_MASTERS = 1;
52+
private static final int NUM_RS = 1;
53+
54+
@Before
55+
public void setup() throws Exception {
56+
Configuration conf = HBaseConfiguration.create();
57+
conf.setBoolean(HConstants.HBASE_SERVER_USEIP_ENABLED_KEY, true);
58+
TEST_UTIL = new HBaseTestingUtil(conf);
59+
StartTestingClusterOption option = StartTestingClusterOption.builder().numMasters(NUM_MASTERS)
60+
.numRegionServers(NUM_RS).numDataNodes(NUM_RS).build();
61+
CLUSTER = TEST_UTIL.startMiniCluster(option);
62+
}
63+
64+
@After
65+
public void teardown() throws Exception {
66+
TEST_UTIL.shutdownMiniCluster();
67+
}
68+
69+
@Test
70+
public void testRegionServerUseIp() throws Exception {
71+
String hostname = CLUSTER.getRegionServer(0).getServerName().getHostname();
72+
String ip = InetAddress.getByName(hostname).getHostAddress();
73+
LOG.info("hostname= " + hostname + " ,ip=" + ip);
74+
assertEquals(ip, hostname);
75+
}
76+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hbase.util;
19+
20+
import java.util.List;
21+
import java.util.stream.Collectors;
22+
import java.util.stream.IntStream;
23+
import org.apache.hadoop.conf.Configuration;
24+
import org.apache.hadoop.hbase.HBaseClassTestRule;
25+
import org.apache.hadoop.hbase.HBaseConfiguration;
26+
import org.apache.hadoop.hbase.HBaseTestingUtil;
27+
import org.apache.hadoop.hbase.HConstants;
28+
import org.apache.hadoop.hbase.ServerName;
29+
import org.apache.hadoop.hbase.SingleProcessHBaseCluster;
30+
import org.apache.hadoop.hbase.TableName;
31+
import org.apache.hadoop.hbase.client.Admin;
32+
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
33+
import org.apache.hadoop.hbase.client.Put;
34+
import org.apache.hadoop.hbase.client.Table;
35+
import org.apache.hadoop.hbase.client.TableDescriptor;
36+
import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
37+
import org.apache.hadoop.hbase.regionserver.HRegionServer;
38+
import org.apache.hadoop.hbase.testclassification.LargeTests;
39+
import org.apache.hadoop.hbase.testclassification.MiscTests;
40+
import org.junit.AfterClass;
41+
import org.junit.Assert;
42+
import org.junit.Before;
43+
import org.junit.BeforeClass;
44+
import org.junit.ClassRule;
45+
import org.junit.Rule;
46+
import org.junit.Test;
47+
import org.junit.experimental.categories.Category;
48+
import org.junit.rules.TestName;
49+
import org.slf4j.Logger;
50+
import org.slf4j.LoggerFactory;
51+
52+
@Category({ MiscTests.class, LargeTests.class })
53+
public class TestRegionMoverUseIp {
54+
55+
@ClassRule
56+
public static final HBaseClassTestRule CLASS_RULE =
57+
HBaseClassTestRule.forClass(TestRegionMoverUseIp.class);
58+
private static final Logger LOG = LoggerFactory.getLogger(TestRegionMoverUseIp.class);
59+
60+
@Rule
61+
public TestName name = new TestName();
62+
63+
private static HBaseTestingUtil TEST_UTIL;
64+
private static ServerName rs0;
65+
private static ServerName rs1;
66+
private static ServerName rs2;
67+
68+
@BeforeClass
69+
public static void setUpBeforeClass() throws Exception {
70+
Configuration conf = HBaseConfiguration.create();
71+
conf.setBoolean(HConstants.HBASE_SERVER_USEIP_ENABLED_KEY, true);
72+
TEST_UTIL = new HBaseTestingUtil(conf);
73+
TEST_UTIL.startMiniCluster(3);
74+
75+
SingleProcessHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
76+
rs0 = cluster.getRegionServer(0).getServerName();
77+
rs1 = cluster.getRegionServer(1).getServerName();
78+
rs2 = cluster.getRegionServer(2).getServerName();
79+
LOG.info("rs0 hostname=" + rs0.getHostname());
80+
LOG.info("rs1 hostname=" + rs1.getHostname());
81+
LOG.info("rs2 hostname=" + rs2.getHostname());
82+
TEST_UTIL.getAdmin().balancerSwitch(false, true);
83+
}
84+
85+
@AfterClass
86+
public static void tearDownAfterClass() throws Exception {
87+
TEST_UTIL.shutdownMiniCluster();
88+
}
89+
90+
@Before
91+
public void setUp() throws Exception {
92+
final TableName tableName = TableName.valueOf(name.getMethodName());
93+
TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder(tableName)
94+
.setColumnFamily(ColumnFamilyDescriptorBuilder.of("fam1")).build();
95+
int startKey = 0;
96+
int endKey = 80000;
97+
TEST_UTIL.getAdmin().createTable(tableDesc, Bytes.toBytes(startKey), Bytes.toBytes(endKey), 9);
98+
}
99+
100+
@Test
101+
public void testRegionUnloadUesIp() throws Exception {
102+
final TableName tableName = TableName.valueOf(name.getMethodName());
103+
SingleProcessHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
104+
Admin admin = TEST_UTIL.getAdmin();
105+
Table table = TEST_UTIL.getConnection().getTable(tableName);
106+
List<Put> puts = IntStream.range(10, 50000).mapToObj(i -> new Put(Bytes.toBytes(i))
107+
.addColumn(Bytes.toBytes("fam1"), Bytes.toBytes("q1"), Bytes.toBytes("val_" + i)))
108+
.collect(Collectors.toList());
109+
table.put(puts);
110+
admin.flush(tableName);
111+
admin.compact(tableName);
112+
Thread.sleep(3000);
113+
HRegionServer hRegionServer0 = cluster.getRegionServer(0);
114+
HRegionServer hRegionServer1 = cluster.getRegionServer(1);
115+
HRegionServer hRegionServer2 = cluster.getRegionServer(2);
116+
int numRegions0 = hRegionServer0.getNumberOfOnlineRegions();
117+
int numRegions1 = hRegionServer1.getNumberOfOnlineRegions();
118+
int numRegions2 = hRegionServer2.getNumberOfOnlineRegions();
119+
120+
Assert.assertTrue(numRegions0 >= 3);
121+
Assert.assertTrue(numRegions1 >= 3);
122+
Assert.assertTrue(numRegions2 >= 3);
123+
int totalRegions = numRegions0 + numRegions1 + numRegions2;
124+
125+
// source RS: rs0
126+
String sourceRSName = rs0.getAddress().toString();
127+
RegionMover.RegionMoverBuilder rmBuilder =
128+
new RegionMover.RegionMoverBuilder(sourceRSName, TEST_UTIL.getConfiguration()).ack(true)
129+
.maxthreads(8);
130+
try (RegionMover regionMover = rmBuilder.build()) {
131+
regionMover.unload();
132+
int newNumRegions0 = hRegionServer0.getNumberOfOnlineRegions();
133+
int newNumRegions1 = hRegionServer1.getNumberOfOnlineRegions();
134+
int newNumRegions2 = hRegionServer2.getNumberOfOnlineRegions();
135+
Assert.assertEquals(0, newNumRegions0);
136+
Assert.assertEquals(totalRegions, newNumRegions1 + newNumRegions2);
137+
}
138+
}
139+
}

0 commit comments

Comments
 (0)