'Socket Hang Up' thực chất là gì?Ít có điều gì gây ức chế hơn lỗi Error: socket hang up. Nó giống như phiên bản mạng của việc ai đó dập máy ngay giữa cuộc hội thoại. Ứng dụng Node.js của bạn đã thiết lập kết nối thành công, nhưng máy chủ hoặc một bên trung gian—như bộ cân bằng tải (load balancer)—đã ngắt kết nối trước khi phản hồi hoàn tất.
Về mặt kỹ thuật, lỗi này thường biểu hiện dưới dạng ECONNRESET. Trong khi ETIMEDOUT có nghĩa là bạn thậm chí không thể kết nối tới máy chủ, thì một lỗi hang up có nghĩa là bạn đang nói chuyện nhưng đường truyền đột ngột bị ngắt. Điều này thường xảy ra do cài đặt thời gian chờ (timeout) không khớp hoặc các nhóm kết nối (connection pools) bị cạn kiệt.
Error: socket hang up
at connResetException (internal/errors.js:607:14)
at TLSSocket.socketOnEnd (_http_client.js:493:23)
at TLSSocket.emit (events.js:327:22)
at endReadableNT (internal/streams/readable.js:1327:12)
at processTicksAndRejections (internal/process/task_queues.js:80:21) {
code: 'ECONNRESET'
}
Tìm ra thủ phạmTrước khi bắt đầu viết lại mã, bạn cần xác định chính xác nơi kết nối bị đứt. Đó là do mã của bạn, do mạng hay do máy chủ từ xa?
- Kiểm tra nhật ký máy chủ (Server-Side Logs): Máy chủ API có bị sập không? Hãy tìm kiếm các đợt tăng vọt bộ nhớ hoặc các lỗi đầu 5 (500-series) tại đúng thời điểm xảy ra lỗi hang up.- Kiểm tra đường truyền mạng: Nếu bạn đang ở sau một VPN doanh nghiệp hoặc sử dụng AWS Application Load Balancer (ALB), các bên trung gian này thường có thời gian chờ nhàn rỗi (idle timeout) là 60 giây. Nếu yêu cầu của bạn mất 61 giây, ALB sẽ ngắt socket đó.- Chạy một lệnh vết chi tiết (Verbose Trace): Thực hiện lệnh
curl -v https://api.example.com. Điều này giúp bạn xem máy chủ có yêu cầu các header cụ thể nào không, chẳng hạn nhưUser-Agent, thứ mà script Node của bạn có thể đang bỏ sót.- Bật chế độ gỡ lỗi Node.js: Khởi chạy ứng dụng của bạn bằng lệnhNODE_DEBUG=http,https,net node app.js. Bạn sẽ thấy quá trình bắt tay (handshake) cấp thấp và chính xác thời điểm nhận được gói tinFIN.## Các giải pháp hiệu quả### 1. Sử dụng một HTTP Agent bền vữngCác phiên bản Node.js trước v19 không bật tính năng Keep-Alive theo mặc định. Điều này có nghĩa là mỗi yêu cầu sẽ mở và đóng một kết nối TCP mới, việc này vừa chậm vừa dễ bị lỗi. Tái sử dụng các kết nối thông qua một agent bền vững là cách khắc phục hiệu quả nhất cho lỗi này. Đối với module https gốc:
const https = require('https');
const agent = new https.Agent({
keepAlive: true,
keepAliveMsecs: 1000, // Gửi các gói tin thăm dò TCP keep-alive mỗi 1 giây
timeout: 60000 // Khớp với thời gian chờ nhàn rỗi tiêu chuẩn 60 giây của máy chủ
});
const options = {
hostname: 'api.example.com',
path: '/large-dataset',
agent: agent
};
const req = https.request(options, (res) => { /* xử lý phản hồi */ });
req.on('error', (e) => console.error(e));
req.end();
Đối với người dùng Axios:
const axios = require('axios');
const https = require('https');
const instance = axios.create({
// Keep-Alive giảm bớt chi phí bắt tay và ngăn chặn việc đóng kết nối đột ngột
httpsAgent: new https.Agent({ keepAlive: true }),
});
instance.get('https://api.example.com/data')
.then(res => console.log(res.data))
.catch(err => console.error(err.message));
2. Đồng bộ hóa thời gian chờ (Timeouts)Nếu máy chủ của bạn mất 30 giây để xử lý một truy vấn cơ sở dữ liệu nặng nhưng socket Node.js của bạn mặc định có khoảng thời gian ngắn hơn, phía máy khách (client) sẽ ngắt kết nối. Bạn phải đảm bảo thời gian chờ cục bộ dài hơn một chút so với thời gian xử lý tối đa của máy chủ.
const req = https.request(options, (res) => { /* ... */ });
// Tăng thời gian chờ socket lên 2 phút cho các báo cáo chậm
req.setTimeout(120000, () => {
console.warn('Socket nhàn rỗi trong 120 giây. Đang tự động hủy.');
req.destroy();
});
3. Tính toán Content-Length cho các yêu cầu POSTNhiều tường lửa hiện đại và cấu hình Nginx sẽ ngay lập tức ngắt một yêu cầu POST nếu thiếu header Content-Length hoặc giá trị không chính xác. Đây là một biện pháp bảo mật để ngăn chặn tấn công request smuggling. Đừng dựa vào việc Node tự đoán kích thước; hãy tính toán nó một cách rõ ràng.
const data = JSON.stringify({ user: 'jsmith', action: 'sync' });
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(data),
'User-Agent': 'MyApp/1.0.0'
}
};
Cách xác minh bản sửa lỗiSau khi bạn đã triển khai một agent bền vững hoặc điều chỉnh thời gian chờ, hãy kiểm tra kỹ kết nối:
- Kiểm tra vòng lặp: Chạy 50 yêu cầu liên tiếp. Nếu vấn đề là do cạn kiệt socket, 10 yêu cầu đầu tiên có thể đã hoạt động trước đó, nhưng hiện tại cả 50 yêu cầu đều phải thành công.- Theo dõi các handle đang hoạt động: Sử dụng
process._getActiveHandles()trong môi trường phát triển của bạn. Nó giúp xác nhận rằng các socket đang được tái sử dụng và không bị rò rỉ vào trạng thái mở "vô hạn".- Kiểm tra nhật ký Proxy: Nếu bạn sử dụng Nginx, hãy tìm lỗiupstream prematurely closed connection. Nếu lỗi đó biến mất khỏi nhật ký Nginx, cấu hình Node.js Agent của bạn đang hoạt động chính xác.## Kết luận- Keep-Alive là bắt buộc: Đối với các ứng dụng có lưu lượng truy cập cao, hãy luôn sử dụng một Agent để quản lý tính bền vững của kết nối.- Xử lý lỗi một cách khéo léo: Luôn đính kèm một trình lắng nghe.on('error'). Nếu không có nó, một lỗi socket hang up đơn giản có thể kích hoạtuncaughtExceptionvà làm sập toàn bộ tiến trình của bạn.- Chú ý các bên trung gian: Mã của bạn có thể hoàn hảo, nhưng một AWS Load Balancer hoặc Cloudflare worker với thời gian chờ ngắn vẫn sẽ ngắt kết nối nếu backend của bạn phản hồi chậm.

