Chúng tôi có một ứng dụng máy chủ ứng dụng khách, 1 máy chủ, khoảng 10 khách hàng. Chúng giao tiếp thông qua các socket tcp bằng cách sử dụng các truy vấn tùy chỉnh.Điều gì gây ra kết nối ổ cắm chậm sau khi hoàn thành GC?
Hệ thống này đã được vận hành trôi chảy trong nhiều tháng, nhưng tại một số điểm, sau khi lịch trình máy chủ GC ĐẦY ĐỦ hàng ngày mà mất khoảng 50, chúng tôi đã tìm ra rằng thời gian giữa các truy vấn gửi của khách hàng và phản ứng nhận được từ máy chủ lớn,> 10-20 giây. Sau khoảng 3 giờ, hệ thống đã phục hồi, mọi thứ vẫn hoạt động tốt trở lại.
Trong khi điều tra vấn đề này, chúng tôi thấy:
- Không có vấn đề thu gom rác thải trên cả máy khách và máy chủ
- thời gian xử lý truy vấn trên máy chủ là nhỏ.
- Tải trên máy chủ cao.
- Băng thông mạng không bị bão hòa.
- Các kết nối không được đặt lại trong FULL GC (GC toàn ngày hàng ngày là sự kiện bình thường)
- Máy và hệ điều hành đã thay đổi gần đây từ Centos 6 (kernel 2.6.32) sang CentOS 7 (kernel 3.10.0) , nhưng cấu hình mới được extensivelly thử nghiệm. Phiên bản Oracle JDK cũng thay đổi từ 1.7.65 đến 1.7.75.
Chúng tôi mất một bãi chứa thread trên máy chủ:
java.lang.Thread.State: RUNNABLE
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at util.network.BytesBasedSocketConnection$ReadConnectionRunnable.run(BytesBasedSocketConnection.java:293)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Các FilterInputStream.read()
như sau:
public int read() throws IOException {
return in.read();
}
Các in
trong mã của chúng tôi là một BufferedInputStream
.
Các câu hỏi là: Tại sao hầu hết các kết nối bị chậm lại sau khi ngừng Full GC? Tại sao stacktrace kết thúc bằng FilterInputStream.read()
? Không nên kết thúc ở đâu đó trong dòng BufferedInputStream
hoặc trong luồng đầu vào socket? Điều này có thể đọc dẫn đến tải cao trên máy chủ?
Mã chúng tôi sử dụng cho việc đọc:
int constructLength = _socketDIS.readInt();
ByteArrayOutputStream constructBOAS = new ByteArrayOutputStream(constructLength);
for (int i = 0; i != constructLength; i++)
constructBOAS.write(_socketDIS.read());
constructBOAS.close();
byte[] bytes = constructBOAS.toByteArray();
nơi:
_socketDIS = new DataInputStream(new BufferedInputStream(_socket.getInputStream()));
Đây là stacktrace từ các kết nối client cũng làm việc:
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:152)
at java.net.SocketInputStream.read(SocketInputStream.java:122)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:235)
at java.io.BufferedInputStream.read(BufferedInputStream.java:254)
- locked <0x00007f522cbebca8> (a java.io.BufferedInputStream)
at java.io.DataInputStream.readInt(DataInputStream.java:387)
at util.network.BytesBasedSocketConnection$ReadConnectionRunnable.run(BytesBasedSocketConnection.java:287)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
UPDATE:
Về câu trả lời EJP:
Không có EOS có liên quan, các kết nối đã tăng, nhưng họ rất chậm
Thậm chí nếu có một EOS Tôi không thể nhìn thấy cách mã có thể quay tại EOS,
for
bị giới hạn bởi giá trịconstructLength
. Tuy nhiên, cải tiến được đề xuất là hợp lệ.Stacktrace với sự cố kết thúc bằng đọc được thực hiện trên
DataInputStream
((_socketDIS.read()
) được kế thừa từFilterInputStream.read()
, xem mã ở trên.DataInputStream
, khôngBufferedInputStream
bị thiếuread()
. đây trongFilterInputStream.read()
có mộtin.read()
kêu gọi mộtBufferedInputStream
, chương trình này có phương pháp riêng của mìnhread()
xác định. Nhưng stacktrace dừng ở giữa, không đạt đếnBufferedInputStream.read()
. Tại sao?
Ah, thực sự có là một 'ScheduledThreadPoolExecutor' sử dụng sai, nhưng nhiệm vụ là một liên tục chạy một, nó chỉ đọc truy vấn từ ổ cắm và đặt chúng vào một hàng đợi. – dcernahoschi