写在前面
请先阅读HDFSRPC通信框架详解,对整体框架先有一定的了解。
参数列表
参数 | 默认值 | 描述 |
---|---|---|
ipc.server.read.connection-queue.size | 100 | reader |
ipc.server.read.threadpool.size | 1 | reader |
ipc.server.listen.queue.size | 128 | Listener:backlog |
ipc.server.tcpnodelay | true | Listener |
ipc.maximum.data.length | 64 1024 1024 | Connection:rpc data len |
dfs.namenode.handler.count | 10 | handler nn requests from clients |
dfs.namenode.service.handler.count | 10 | handler nn requests from DataNodes |
dfs.namenode.lifeline.handler.count | 10 | handler nn lifeline requests |
dfs.datanode.handler.count | 10 | handler dn |
ipc.server.read.connection-queue.size
reader的处理连接数,默认是100。使用BlockingQueue实现。
final private BlockingQueue<Connection> pendingConnections;
public void addConnection(Connection conn) throws InterruptedException {
pendingConnections.put(conn);
readSelector.wakeup();
}
使用addConnection加入队列,队列满了此方法会阻塞。
ipc.server.read.threadpool.size
reader的线程数,默认为1。reader主要用于连接头,连接上下文的处理,还是要花费一定的时间的,特别在使用Kerberos的时候。reader多线程处理会比较合理,所以笔者认为这个默认值不太合理,设置为不小于1会比较合理。
ipc.server.listen.queue.size
默认值为128,属于listener。本参数虽然叫queuesize,单实际上不是listenr的数量或者listener的队列长度,而是来源于ServerSocket的bind(SocketAddress endpoint, int backlog) 方法中的backlog。listener源码为
private int backlogLength = Config.getInt(
CommonConfigurationKeysPublic.IPC_SERVER_LISTEN_QUEUE_SIZE_KEY,
CommonConfigurationKeysPublic.IPC_SERVER_LISTEN_QUEUE_SIZE_DEFAULT);
public Listener(String bindAddress, int port, int readThreads) throws IOException {
...
acceptChannel.socket().bind(address, backlogLength);
...
}
backlog参数为如果server.accept()没有及时接收,系统会把socket缓存起来,代表这个缓存队列的大小。如果队列已满,新连接会被拒接。
void doAccept(SelectionKey key) throws InterruptedException, IOException, OutOfMemoryError {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel channel;
while ((channel = server.accept()) != null) {
channel.configureBlocking(false);
channel.socket().setTcpNoDelay(tcpNoDelay);
channel.socket().setKeepAlive(true);
Reader reader = getReader();
Connection c = Server.connectionManager.register(channel);
// If the connectionManager can't take it, close the connection.
if (c == null) {
if (channel.isOpen()) {
IOUtils.cleanup(null, channel);
}
Server.connectionManager.droppedConnections.getAndIncrement();
continue;
}
key.attach(c); // so closeCurrentConnection can get the object
reader.addConnection(c);
}
}
看doAccept方法,Connection接收到了以后会放入connectionManager,如果connectionManager的队列满了(ipc.server.max.connections)会直接关闭连接,ipc.server.max.connections默认值为0,代表没有上限。reader.addConnection代表连接会放入reader的pendingConnections,pendingConnections为BlockingQueue,满了会阻塞,默认值为100。所以默认值情况下,可以处理100+128个连接,超过了以后会被拒绝。如果ipc.server.max.connections设置为50,此值会直接失效,因为连接超过50以后,会直接关闭连接。
ipc.server.tcpnodelay
连接的TcpNoDelay属性,默认为true。
channel.configureBlocking(false);
channel.socket().setTcpNoDelay(tcpNoDelay);
channel.socket().setKeepAlive(true);
对应的就是TCP_NODELAY,本质上设置是否使用Nagle 算法,缓存数据合并发送。建议使用默认值true,关闭Nagle 算法。
ipc.maximum.data.length
请求的数据最大长度,默认是64MB。
this.maxDataLength = Config.getInt(CommonConfigurationKeys.IPC_MAXIMUM_DATA_LENGTH,
CommonConfigurationKeys.IPC_MAXIMUM_DATA_LENGTH_DEFAULT);
private void checkDataLength(int dataLength) throws IOException {
if (dataLength < 0) {
String error = "Unexpected data length " + dataLength +
"!! from " + getHostAddress();
LOG.warn(error);
throw new IOException(error);
} else if (dataLength > maxDataLength) {
String error = "Requested data length " + dataLength +
" is longer than maximum configured RPC length " +
maxDataLength + ". RPC came from " + getHostAddress();
LOG.warn(error);
throw new IOException(error);
}
}
如果长度超过最大长度,server不会返回任何数据,会直接关闭连接。
handler.count
handler的数量,默认为10。有4个参数,分别为dfs.namenode.handler.count,dfs.namenode.service.handler.count,dfs.namenode.lifeline.handler.count,dfs.datanode.handler.count 。handler是用于实际处理rpc请求的,比如createfile,mkdir等。这些操作都是要花费一定的时间,而且随着存储文件数,存储集群的变大,理论上会变慢。所以handler.count可以设置的大一点。当然建议dfs.namenode.handler.count设置大一点就行,dfs.namenode.handler.count主要用于处理客户端请求,网上建议设置为20logN,N为集群服务器数量。其他都是集群内部的请求处理,没有特殊情况,使用默认值即可。