Skip to content

Commit c271cec

Browse files
author
zengqiang.xu
committed
HDFS-13274. RBF: Extend RouterRpcClient to use multiple sockets
1 parent 34e548c commit c271cec

File tree

6 files changed

+204
-39
lines changed

6 files changed

+204
-39
lines changed

hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/ConnectionContext.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,12 @@ public class ConnectionContext {
5353
private long lastActiveTs = 0;
5454
/** The connection's active status would expire after this window. */
5555
private final static long ACTIVE_WINDOW_TIME = TimeUnit.SECONDS.toMillis(30);
56+
/** The maximum number of requests that this connection can handle concurrently. **/
57+
private final int maxConcurrencyPerConn;
5658

57-
public ConnectionContext(ProxyAndInfo<?> connection) {
59+
public ConnectionContext(ProxyAndInfo<?> connection, int maxConcurrencyPerConn) {
5860
this.client = connection;
61+
this.maxConcurrencyPerConn = maxConcurrencyPerConn;
5962
}
6063

6164
/**
@@ -93,6 +96,23 @@ public synchronized boolean isClosed() {
9396
* @return True if the connection can be used.
9497
*/
9598
public synchronized boolean isUsable() {
99+
return hasAvailableConcurrency() && !isClosed();
100+
}
101+
102+
/**
103+
* Return true if this connection context still has available concurrency,
104+
* else return false.
105+
*/
106+
private synchronized boolean hasAvailableConcurrency() {
107+
return this.numThreads < maxConcurrencyPerConn;
108+
}
109+
110+
/**
111+
* Check if the connection is idle. It checks if the connection is not used
112+
* by another thread.
113+
* @return True if the connection is not used by another thread.
114+
*/
115+
public synchronized boolean isIdle() {
96116
return !isActive() && !isClosed();
97117
}
98118

hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/ConnectionPool.java

Lines changed: 57 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ public class ConnectionPool {
7777
private static final Logger LOG =
7878
LoggerFactory.getLogger(ConnectionPool.class);
7979

80-
8180
/** Configuration settings for the connection pool. */
8281
private final Configuration conf;
8382

@@ -94,6 +93,8 @@ public class ConnectionPool {
9493
private volatile List<ConnectionContext> connections = new ArrayList<>();
9594
/** Connection index for round-robin. */
9695
private final AtomicInteger clientIndex = new AtomicInteger(0);
96+
/** Underlying socket index. **/
97+
private final AtomicInteger socketIndex = new AtomicInteger(0);
9798

9899
/** Min number of connections per user. */
99100
private final int minSize;
@@ -105,6 +106,12 @@ public class ConnectionPool {
105106
/** The last time a connection was active. */
106107
private volatile long lastActiveTime = 0;
107108

109+
/** Enable using multiple physical socket or not. **/
110+
private final boolean enableMultiSocket;
111+
112+
/** Max Concurrency of each connection. */
113+
private final int maxConcurrencyPerConn;
114+
108115
/** Map for the protocols and their protobuf implementations. */
109116
private final static Map<Class<?>, ProtoImpl> PROTO_MAP = new HashMap<>();
110117
static {
@@ -149,9 +156,15 @@ protected ConnectionPool(Configuration config, String address,
149156
this.minSize = minPoolSize;
150157
this.maxSize = maxPoolSize;
151158
this.minActiveRatio = minActiveRatio;
159+
this.enableMultiSocket = conf.getBoolean(
160+
RBFConfigKeys.DFS_ROUTER_NAMENODE_ENABLE_MULTIPLE_SOCKET_KEY,
161+
RBFConfigKeys.DFS_ROUTER_NAMENODE_ENABLE_MULTIPLE_SOCKET_DEFAULT);
162+
this.maxConcurrencyPerConn = conf.getInt(
163+
RBFConfigKeys.DFS_ROUTER_MAX_CONCURRENCY_PER_CONNECTION_KEY,
164+
RBFConfigKeys.DFS_ROUTER_MAX_CONCURRENCY_PER_CONNECTION_DEFAULT);
152165

153166
// Add minimum connections to the pool
154-
for (int i=0; i<this.minSize; i++) {
167+
for (int i = 0; i < this.minSize; i++) {
155168
ConnectionContext newConnection = newConnection();
156169
this.connections.add(newConnection);
157170
}
@@ -210,24 +223,22 @@ public AtomicInteger getClientIndex() {
210223
* @return Connection context.
211224
*/
212225
protected ConnectionContext getConnection() {
213-
214226
this.lastActiveTime = Time.now();
215-
216-
// Get a connection from the pool following round-robin
217-
ConnectionContext conn = null;
218227
List<ConnectionContext> tmpConnections = this.connections;
219-
int size = tmpConnections.size();
220-
// Inc and mask off sign bit, lookup index should be non-negative int
221-
int threadIndex = this.clientIndex.getAndIncrement() & 0x7FFFFFFF;
222-
for (int i=0; i<size; i++) {
223-
int index = (threadIndex + i) % size;
224-
conn = tmpConnections.get(index);
225-
if (conn != null && conn.isUsable()) {
226-
return conn;
228+
for (ConnectionContext tmpConnection : tmpConnections) {
229+
if (tmpConnection != null && tmpConnection.isUsable()) {
230+
return tmpConnection;
227231
}
228232
}
229233

230-
// We return a connection even if it's active
234+
ConnectionContext conn = null;
235+
// We return a connection even if it's busy
236+
int size = tmpConnections.size();
237+
if (size > 0) {
238+
// Get a connection from the pool following round-robin
239+
int threadIndex = this.clientIndex.getAndIncrement() & 0x7FFFFFFF;
240+
conn = tmpConnections.get(threadIndex % size);
241+
}
231242
return conn;
232243
}
233244

@@ -256,19 +267,18 @@ public synchronized List<ConnectionContext> removeConnections(int num) {
256267
int targetCount = Math.min(num, this.connections.size() - this.minSize);
257268
// Remove and close targetCount of connections
258269
List<ConnectionContext> tmpConnections = new ArrayList<>();
259-
for (int i = 0; i < this.connections.size(); i++) {
260-
ConnectionContext conn = this.connections.get(i);
270+
for (ConnectionContext conn : this.connections) {
261271
// Only pick idle connections to close
262-
if (removed.size() < targetCount && conn.isUsable()) {
272+
if (removed.size() < targetCount && conn.isIdle()) {
263273
removed.add(conn);
264274
} else {
265275
tmpConnections.add(conn);
266276
}
267277
}
268278
this.connections = tmpConnections;
269279
}
270-
LOG.debug("Expected to remove {} connection " +
271-
"and actually removed {} connections", num, removed.size());
280+
LOG.debug("Expected to remove {} connection and actually removed {} connections",
281+
num, removed.size());
272282
return removed;
273283
}
274284

@@ -303,7 +313,6 @@ protected int getNumConnections() {
303313
*/
304314
protected int getNumActiveConnections() {
305315
int ret = 0;
306-
307316
List<ConnectionContext> tmpConnections = this.connections;
308317
for (ConnectionContext conn : tmpConnections) {
309318
if (conn.isActive()) {
@@ -320,10 +329,9 @@ protected int getNumActiveConnections() {
320329
*/
321330
protected int getNumIdleConnections() {
322331
int ret = 0;
323-
324332
List<ConnectionContext> tmpConnections = this.connections;
325333
for (ConnectionContext conn : tmpConnections) {
326-
if (conn.isUsable()) {
334+
if (conn.isIdle()) {
327335
ret++;
328336
}
329337
}
@@ -393,28 +401,34 @@ public String getJSON() {
393401
* @throws IOException If it cannot get a new connection.
394402
*/
395403
public ConnectionContext newConnection() throws IOException {
396-
return newConnection(
397-
this.conf, this.namenodeAddress, this.ugi, this.protocol);
404+
return newConnection(this.conf, this.namenodeAddress,
405+
this.ugi, this.protocol, this.enableMultiSocket,
406+
this.socketIndex.incrementAndGet(),
407+
this.maxConcurrencyPerConn);
398408
}
399409

400410
/**
401411
* Creates a proxy wrapper for a client NN connection. Each proxy contains
402412
* context for a single user/security context. To maximize throughput it is
403413
* recommended to use multiple connection per user+server, allowing multiple
404414
* writes and reads to be dispatched in parallel.
405-
* @param <T>
415+
* @param <T> Input type T.
406416
*
407417
* @param conf Configuration for the connection.
408418
* @param nnAddress Address of server supporting the ClientProtocol.
409419
* @param ugi User context.
410420
* @param proto Interface of the protocol.
421+
* @param enableMultiSocket Enable multiple socket or not.
422+
* @param maxConcurrencyPerConn The maximum number of requests that
423+
* this connection can handle concurrently.
411424
* @return proto for the target ClientProtocol that contains the user's
412425
* security context.
413426
* @throws IOException If it cannot be created.
414427
*/
415428
protected static <T> ConnectionContext newConnection(Configuration conf,
416-
String nnAddress, UserGroupInformation ugi, Class<T> proto)
417-
throws IOException {
429+
String nnAddress, UserGroupInformation ugi, Class<T> proto,
430+
boolean enableMultiSocket, int socketIndex,
431+
int maxConcurrencyPerConn) throws IOException {
418432
if (!PROTO_MAP.containsKey(proto)) {
419433
String msg = "Unsupported protocol for connection to NameNode: "
420434
+ ((proto != null) ? proto.getName() : "null");
@@ -437,23 +451,31 @@ protected static <T> ConnectionContext newConnection(Configuration conf,
437451
}
438452
InetSocketAddress socket = NetUtils.createSocketAddr(nnAddress);
439453
final long version = RPC.getProtocolVersion(classes.protoPb);
440-
Object proxy = RPC.getProtocolProxy(classes.protoPb, version, socket, ugi,
441-
conf, factory, RPC.getRpcTimeout(conf), defaultPolicy, null).getProxy();
454+
Object proxy;
455+
if (enableMultiSocket) {
456+
FederationConnectionId connectionId = new FederationConnectionId(
457+
socket, classes.protoPb, ugi, RPC.getRpcTimeout(conf),
458+
defaultPolicy, conf, socketIndex);
459+
proxy = RPC.getProtocolProxy(classes.protoPb, version, connectionId,
460+
conf, factory).getProxy();
461+
} else {
462+
proxy = RPC.getProtocolProxy(classes.protoPb, version, socket, ugi,
463+
conf, factory, RPC.getRpcTimeout(conf), defaultPolicy, null).getProxy();
464+
}
465+
442466
T client = newProtoClient(proto, classes, proxy);
443467
Text dtService = SecurityUtil.buildTokenService(socket);
444468

445-
ProxyAndInfo<T> clientProxy =
446-
new ProxyAndInfo<T>(client, dtService, socket);
447-
ConnectionContext connection = new ConnectionContext(clientProxy);
448-
return connection;
469+
ProxyAndInfo<T> clientProxy = new ProxyAndInfo<T>(client, dtService, socket);
470+
return new ConnectionContext(clientProxy, maxConcurrencyPerConn);
449471
}
450472

451473
private static <T> T newProtoClient(Class<T> proto, ProtoImpl classes,
452474
Object proxy) {
453475
try {
454476
Constructor<?> constructor =
455477
classes.protoClientPb.getConstructor(classes.protoPb);
456-
Object o = constructor.newInstance(new Object[] {proxy});
478+
Object o = constructor.newInstance(proxy);
457479
if (proto.isAssignableFrom(o.getClass())) {
458480
@SuppressWarnings("unchecked")
459481
T client = (T) o;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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.hdfs.server.federation.router;
19+
20+
import org.apache.commons.lang3.builder.EqualsBuilder;
21+
import org.apache.commons.lang3.builder.HashCodeBuilder;
22+
import org.apache.hadoop.conf.Configuration;
23+
import org.apache.hadoop.io.retry.RetryPolicy;
24+
import org.apache.hadoop.ipc.Client;
25+
import org.apache.hadoop.security.UserGroupInformation;
26+
27+
import java.net.InetSocketAddress;
28+
29+
public class FederationConnectionId extends Client.ConnectionId {
30+
private static final int PRIME = 16777619;
31+
private final int index;
32+
33+
public FederationConnectionId(InetSocketAddress address, Class<?> protocol,
34+
UserGroupInformation ticket, int rpcTimeout,
35+
RetryPolicy connectionRetryPolicy, Configuration conf, int index) {
36+
super(address, protocol, ticket, rpcTimeout, connectionRetryPolicy, conf);
37+
this.index = index;
38+
}
39+
40+
@Override
41+
public int hashCode() {
42+
return new HashCodeBuilder()
43+
.append(PRIME * super.hashCode())
44+
.append(this.index)
45+
.toHashCode();
46+
}
47+
48+
@Override
49+
public boolean equals(Object obj) {
50+
if (!super.equals(obj)) {
51+
return false;
52+
}
53+
if (obj instanceof FederationConnectionId) {
54+
FederationConnectionId other = (FederationConnectionId)obj;
55+
return new EqualsBuilder()
56+
.append(this.index, other.index)
57+
.isEquals();
58+
}
59+
return false;
60+
}
61+
}

hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RBFConfigKeys.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,13 @@ public class RBFConfigKeys extends CommonConfigurationKeysPublic {
135135
FEDERATION_ROUTER_PREFIX + "connection.clean.ms";
136136
public static final long DFS_ROUTER_NAMENODE_CONNECTION_CLEAN_MS_DEFAULT =
137137
TimeUnit.SECONDS.toMillis(10);
138+
public static final String DFS_ROUTER_NAMENODE_ENABLE_MULTIPLE_SOCKET_KEY =
139+
FEDERATION_ROUTER_PREFIX + "enable.multiple.socket";
140+
public static final boolean DFS_ROUTER_NAMENODE_ENABLE_MULTIPLE_SOCKET_DEFAULT
141+
= false;
142+
public static final String DFS_ROUTER_MAX_CONCURRENCY_PER_CONNECTION_KEY =
143+
FEDERATION_ROUTER_PREFIX + "max.concurrency.per.connection";
144+
public static final int DFS_ROUTER_MAX_CONCURRENCY_PER_CONNECTION_DEFAULT = 1;
138145

139146
// HDFS Router RPC client
140147
public static final String DFS_ROUTER_CLIENT_THREADS_SIZE =

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,22 @@
134134
</description>
135135
</property>
136136

137+
<property>
138+
<name>dfs.federation.router.enable.multiple.socket</name>
139+
<value>false</value>
140+
<description>
141+
If enable multiple downstream socket or not.
142+
</description>
143+
</property>
144+
145+
<property>
146+
<name>dfs.federation.router.max.concurrency.per.connection</name>
147+
<value>1</value>
148+
<description>
149+
The maximum number of requests that a connection can handle concurrently.
150+
</description>
151+
</property>
152+
137153
<property>
138154
<name>dfs.federation.router.connection.pool.clean.ms</name>
139155
<value>60000</value>

0 commit comments

Comments
 (0)