面试题答案
一键面试连接池的创建
- 确定初始参数:根据预估的应用负载和服务器资源,设定连接池的初始大小(
initialSize
)、最大连接数(maxSize
)以及每个连接的超时时间(timeout
)等参数。 - 使用
net
模块创建TCP连接:在Node.js中,利用内置的net
模块来创建TCP连接。可以使用一个数组(connections
)来存储连接池中的连接。示例代码如下:
const net = require('net');
class ConnectionPool {
constructor(options) {
this.initialSize = options.initialSize;
this.maxSize = options.maxSize;
this.timeout = options.timeout;
this.connections = [];
this.pendingRequests = [];
this.createInitialConnections();
}
createInitialConnections() {
for (let i = 0; i < this.initialSize; i++) {
this.addConnection();
}
}
addConnection() {
if (this.connections.length >= this.maxSize) return;
const connection = net.createConnection({ host: 'yourHost', port: yourPort });
connection.setTimeout(this.timeout);
connection.on('connect', () => {
this.connections.push(connection);
if (this.pendingRequests.length > 0) {
const request = this.pendingRequests.shift();
request.resolve(connection);
}
});
connection.on('timeout', () => {
connection.destroy();
this.removeConnection(connection);
});
connection.on('error', (err) => {
connection.destroy();
this.removeConnection(connection);
});
}
}
连接的分配与回收机制
- 分配连接:提供一个获取连接的方法(
getConnection
)。当有请求获取连接时,首先检查连接池中是否有可用连接。如果有,直接返回该连接;如果没有,则将请求加入等待队列(pendingRequests
)。示例代码如下:
getConnection() {
return new Promise((resolve, reject) => {
if (this.connections.length > 0) {
const connection = this.connections.pop();
resolve(connection);
} else {
if (this.connections.length < this.maxSize) {
this.addConnection();
}
this.pendingRequests.push({ resolve, reject });
}
});
}
- 回收连接:当使用完连接后,提供一个释放连接的方法(
releaseConnection
),将连接重新放回连接池。如果等待队列中有请求,唤醒其中一个请求并分配连接。示例代码如下:
releaseConnection(connection) {
if (this.pendingRequests.length > 0) {
const request = this.pendingRequests.shift();
request.resolve(connection);
} else {
this.connections.push(connection);
}
}
处理连接池满、连接超时等异常情况
- 连接池满:当连接池达到最大连接数且所有连接都在使用中时,新的请求会被放入等待队列(
pendingRequests
)。可以设置一个等待队列的最大长度,超过该长度时,直接拒绝新的请求并返回错误信息,告知客户端暂时无法处理请求。 - 连接超时:在创建连接时,通过
connection.setTimeout(this.timeout)
设置连接的超时时间。当连接超时时,触发timeout
事件,在事件处理中,销毁连接并将其从连接池中移除,同时尝试重新创建一个新的连接。如果连接创建过程中发生错误,也同样处理并尝试重新创建。
动态调整连接池的大小
- 监控负载:可以通过监控应用的请求队列长度、平均响应时间、CPU 和内存使用率等指标来判断当前应用的负载情况。例如,使用
node - native-prometheus
等工具来收集和暴露这些指标。 - 动态调整:根据负载情况动态调整连接池的大小。当负载较低时(如请求队列长度较短、平均响应时间较短),减少连接池的大小,关闭多余的连接以释放资源;当负载较高时(如请求队列长度较长、平均响应时间较长),增加连接池的大小,创建新的连接以满足需求。可以设定一些阈值来触发连接池大小的调整,例如:
- 当请求队列长度超过
highQueueThreshold
时,且连接池大小小于maxSize
,增加连接池大小。 - 当请求队列长度低于
lowQueueThreshold
,且连接池大小大于initialSize
,减少连接池大小。示例代码如下:
- 当请求队列长度超过
adjustPoolSize() {
const queueLength = this.pendingRequests.length;
if (queueLength > highQueueThreshold && this.connections.length < this.maxSize) {
this.addConnection();
} else if (queueLength < lowQueueThreshold && this.connections.length > this.initialSize) {
const connection = this.connections.pop();
connection.destroy();
}
}
通过以上方案,可以实现一个高效且可扩展的Node.js应用的TCP连接池管理,以应对高并发请求并在不同负载情况下达到最佳性能。