我已經很難調試這個問題兩天了。我在/ stackoverflow外搜索了很多內容後找不到答案。網絡響應緩衝區重寫壓力?
我正在爲定製的鍵/值服務器編寫客戶端。協議很簡單。 如果客戶端發送
"GET 1 12\r\nkey1\r\nkey2\r\n"
服務器可以重放
"0 1 16\r\nvalue1\r\nvalue2\r\n"
在響應中,第一行表示主體的長度是16個字節,併爲下一個16個字節,它包含了值兩個鍵。他們被「\ r \ n」分隔。
問題是,在壓力測試中,有時我從客戶端看到響應消息看起來不正確。它看起來像緩衝區被覆蓋。
例如發送
"GET 1 12\r\nkey1\r\nkey2\r\n"
爲10,000次, 在響應緩衝區我可能會看到
"0 1 16\r\nvalue1\r\nval0 1 16"
它看起來像值2這裏由一個響應部分覆蓋。
服務器在那裏工作了很長時間,我認爲它運行良好。我也使用tcpdump並證明它是正確的。所以錯誤應該在客戶端。
我遵循Netty Telnet示例here,並做了小的修改。
在DelimiterBasedFrameDecoder之後,一個處理程序逐個解析這些行並組裝響應。
我以爲它可能與多線程有關。但即使我把線程號設置爲1,問題重演。
那麼,我是否以錯誤的方式使用Netty?
====================
更新: 更多的調查之後,我發現它不與Netty的關係。即使使用簡單的java NIO程序,它也可以重新生成。它似乎與緩衝區溢出有關。
從tcpdump,我可以看到來自遠程服務器的包是正確的。
因此,我捕獲每個ByteBuffer,並在發生錯誤時將其打印出來。
protected void onRead(ByteBuffer buf) throws Exception {
buf.mark();
int l = buf.limit();
int p = buf.position();
byte[] bytes = new byte[l - p];
buf.get(bytes, p, l - p);
String v = new String(bytes, Charset.forName("UTF-8"));
buffers.addFirst(v);
if (buffers.size() > 30) {
buffers.removeLast();
}
buf.reset();
//...
// process one line of buf
};
以下是最後三個緩衝器捕獲,因爲我處理它逐行:與此代碼 - (1K我故意將緩衝區大小設置爲小的數目。)。看起來頭部「0 0 1040」錯誤放置了一個截斷線「20」
*************************************
20131101/booking.com.png^M
20131101/booking.com.png^M
20131101/booking.com.png^M
20131101/booking.com.png^M
0 0 1040^M
20131101/booking.com.png^M
20131101/booking.com.png^M
20131101/booking.com.png^M
20131101/booking.com.png^M
200 0 1040^M
20131101/booking.com.png^M
*************************************
20131101/booking.com.png^M
20131101/booking.com.png^M
20131101/booking.com.png^M
20131101/booking.com.png^M
20131101/booking.com.png^M
0 0 1040^M
20131101/booking.com.png^M
20131101/booking.com.png^M
20131101/booking.com.png^M
20131101/booking.com.png^M
20
*************************************
20131101/booking.com.png^M
20131101/booking.com.png^M
20131101/booking.com.png^M
20131101/booking.com.png^M
20131101/booking.com.png^M
20131101/booking.com.png^M
0 0 1040^M
20131101/booking.com.png^M
20131101/booking.com.png^M
20131101/booking.com.png^M
20131101/boo
*************************************
我沒有找到根本原因。一旦我得到答案,我會回覆它。
=====================
這是我原來的代碼片段, 初始化
@Override
public void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(DECODER);
pipeline.addLast(ENCODER);
pipeline.addLast(new NettyClientHandler());
}
和處理程序:
//@Sharable
public class NettyClientHandler extends SimpleChannelInboundHandler<String> {
boolean head = true;
int len = -1;
ArrayList<String> vals = new ArrayList<>();
@Override
public void channelRead0(ChannelHandlerContext ctx, String request) throws InterruptedException {
if (head) {
vals.clear();
String[] splits = request.split(" ");
len = -1;
try {
len = Integer.parseInt(splits[2]);
} catch (NumberFormatException ex) {
ex.printStackTrace();
}
if (len == -1) {
return;
}
head = false;
} else {
vals.add(request);
len -= (request.length() + 2);
if (len == 0) {
// System.err.print("[");
// for (int i = 0; i < vals.size(); i++) {
// System.err.print(vals.get(i) + ",");
// }
// System.err.println("]");
head = true;
}
}
//System.err.println(request);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}