2015-05-20 16 views
18

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:

  1. Không có vấn đề thu gom rác thải trên cả máy khách và máy chủ
  2. thời gian xử lý truy vấn trên máy chủ là nhỏ.
  3. Tải trên máy chủ cao.
  4. Băng thông mạng không bị bão hòa.
  5. 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)
  6. 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:

  1. Không có EOS có liên quan, các kết nối đã tăng, nhưng họ rất chậm

  2. 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ệ.

  3. 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ông BufferedInputStream bị thiếu read(). đây trong FilterInputStream.read() có một in.read() kêu gọi một BufferedInputStream, chương trình này có phương pháp riêng của mình read() xác định. Nhưng stacktrace dừng ở giữa, không đạt đến BufferedInputStream.read(). Tại sao?

Trả lời

3

Dấu vết ngăn xếp cho biết bạn đang sử dụng ScheduledThreadPoolExecutor. Tôi sẽ đề nghị bạn nghiên cứu lịch trình. Có khả năng là sự chậm trễ chỉ đơn thuần là vì việc đọc được trên một số loại lịch biểu - điều này dường như không quan trọng đối với tôi.

+0

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

7

Đọc từng byte một là lãng phí CPU. Ném này đi:

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(); 

và sử dụng này:

int constructLength = _socketDIS.readInt(); 
byte[] bytes = new byte[constructLength]; 
_socketDIS.readFully(bytes); 

NB _socketDIS rõ ràng là không một BufferedInputStream nhưng một DataInputStream, đó là không có bộ đệm.

EDIT

Tại sao stacktrace kết thúc bằng FilterInputStream.read()?

Nhìn một cách cẩn thận. BufferedInputStream không thực hiện tất cả ba quá tải read(). Một trong số đó, tôi quên nó, được thực hiện trong FilterInputStream, lớp cơ sở và hai overloads khác gọi đó.

nên không nó kết thúc ở đâu đó trong BufferedInputStream

Không, xem ở trên.

hoặc trong luồng đầu vào ổ cắm?

Có, nếu nó đang chặn, nhưng không phải vì có lẽ bạn đang quay ở cuối luồng vì mã kém của bạn.

Đọc này có thể dẫn đến tải cao trên máy chủ không?

Có.

+0

Ok, nhưng phần lớn thời gian mã này hoạt động tốt. Và chắc chắn '_socketDIS' được đệm. Tôi sẽ sớm tải lên một stacktrace khác từ các kết nối hoạt động tốt và sử dụng cùng một mã. – dcernahoschi

+0

Dù sao, nhưng đây là mã tốt hơn anyway. Nó sẽ chăm sóc của các trường hợp góc đúng cách, ví dụ, EOS sớm, nơi bạn chỉ quay vòng. – EJP

+0

Cảm ơn, tôi đồng ý nó tốt hơn, nhưng không phải là vấn đề ở đây, kết nối ở lại tất cả thời gian còn sống và tại một số điểm phục hồi, tốc độ trở lại bình thường. – dcernahoschi

1

Đây là một nhận xét mở rộng, nhưng quá dài để nhận xét nên tôi sẽ cung cấp câu trả lời.

Như bạn lưu ý, các bãi chứa thread cho thấy một chủ đề ở giữa FilterInputStream.read() là không bình thường. Trong khi nó có thể xảy ra một cách tình cờ, trong khi FilterInputStream.read() được ghi đè() đang được giải quyết thành BufferedInputStream.read(), tình cờ xảy ra dường như không chắc.

Vì điều này xảy ra sau khi thu gom rác đầy đủ, có vẻ như tôi mất nhiều thời gian hơn để giải quyết cuộc gọi FilterInputStream vì lớp BufferedInputStream đã được di chuyển hoặc không tải trong khi thu gom rác đầy đủ. Ví dụ, nếu tình cờ không có các đối tượng BufferedInputStream được sử dụng khi toàn bộ quá trình thu gom rác xảy ra, lớp BufferedInputStream có thể đã được giải phóng, yêu cầu tải lớp khi phương thức read() của nó là cần thiết. Điều này có thể giải thích cho sự chậm trễ mà bạn thấy, ít nhất một lần.

Có thể trong một số trường cho các lớp học để bốc dỡ khi cuối cùng các trường hợp của họ là thu gom rác thải, ngay cả trong trường hợp không có thu gom rác thải đầy đủ. Nếu lớp BufferedInputStream bằng cách nào đó bị dỡ bỏ sau mỗi lần sử dụng và nạp lại lần sau khi nó được sử dụng, điều đó có thể giải thích các triệu chứng bạn nói, nhưng tôi thường không mong đợi điều đó xảy ra. Cũng có thể có sự đổ vỡ của trang bộ nhớ chứa lớp BufferedInputStream, có lẽ vì lớp đã được di chuyển trong khi thu gom rác đầy đủ, nếu bạn đang sử dụng bộ nhớ ảo. Nếu bạn có bất kỳ hồ sơ liên quan đến việc sử dụng bộ nhớ trong thời gian này, họ có thể có giá trị một cái nhìn.

+0

Cảm ơn. Các thể hiện 'BufferedInputStream' không phải là rác được thu thập khi các kết nối socket vẫn hoạt động. Không, bộ nhớ vẫn ở mức thấp, không có sự va chạm nào cả. – dcernahoschi

-1

Tôi đoán bạn phải cố gắng để tuôn ra nó để đọc và viết trong dòng mà không có lỗi hoặc làm chậm kết nối.

Các vấn đề liên quan