Netty框架中的ByteBuf對java.nio.ByteBuffer的微創新(1)

這幾天在研究netty框架,其中的ByteBufjava.nio.ByteBuffer的微創新令人感受頗深。java.nio.ByteBuffer的引入對套接字編程提供了極大的方便性。在此之前,如果要從Socket中讀取一個4字節整數,假如採用Big-Endian編碼,其過程通常如下:



/**
 * 使用Socket.getInputStream從套接字中讀取一個4字節整數,表示PDU的長度.
 * @param s 套接字
 * @return PDU的長度
 * @throws IOException 如果在訪問套接字時出現錯誤
 */
publiclong getPduLength(Socket s) throws IOException {
long pduLength;
byte[] buf = newbyte[4];
int cnt = 0;
while (cnt < buf.length) {
cnt += s.getInputStream().read(buf, cnt, buf.length - cnt);
}
pduLength = buf[0];
pduLength &= buf[1] << 8;
pduLength &= buf[2] << 16;
pduLength &= buf[3] << 24;
return pduLength;
}


分析:由於java的安全性限制不能直接訪問內存,所以在讀取到足夠的字節之後,需要通過位運算來得到一個4字節整數。而且每次讀取字節時都要進行邊界檢查,對於高性能的網絡通信要求來說,不是很理想。



java引入nio包的ByteBuffer之後,這種情況得到了明顯改善。ByteBuffernative方法實現了對內存的直接訪問,避免了位操作及反覆的邊界檢查,性能得到改善。代碼也很簡潔:


/**
 * 使用java.nio.SocketChannel從套接字中讀取一個4字節整數,表示PDU的長度.
 * @param s 套接字
 * @return PDU的長度
 * @throws IOException 如果在訪問套接字時出現錯誤
 */
publiclong getPduLength(SocketChannel s) throws IOException {
ByteBuffer buf = ByteBuffer.allocate(4);
int cnt = 0;
while (cnt < 4) {
cnt += s.read(buf);
}
buf.flip();
return buf.getLong();
}



但在實際編程上,由於nio包只是提供了一個基本的通信接口,工程師仍然需要大量的多線程、異步操作、共享訪問等工作,這些工作對很多程序員來說不啻於一場夢魘。java是一個開放的語言,大量框架應運而生,這些框架的目標是希望解放程序員的精力,讓程序員關注於Business Rules,而不是編程本身。Netty就是其中一個優秀的框架。以其中的ByteBuf重寫上述例程:


/**
 * 使用Netty框架的ByteBuf從套接字中讀取一個4字節整數,表示PDU的長度.
 * @param s 套接字
 * @return PDU的長度
 * @throws IOException 如果在訪問套接字時出現錯誤
 */
publiclong getPduLengthNetty(SocketChannel s) throws IOException {
ByteBuf buf = PooledByteBufAllocator.DEFAULT.buffer(4);
int cnt = 0;
ByteBuffer tmp = ByteBuffer.allocate(4);
while (cnt < 4) {
cnt += s.read(tmp);
}
tmp.flip();
buf.writeBytes(tmp);
return buf.readLong();
}


從上述代碼看,ByteBuf的使用和ByteBuffer基本類似,而且由於Netty是採用Pure-java編寫的,所以仍然需要依賴nio提供的接口,讀取一個4字節整數的代碼量並沒有減少,而且還有所增加,邏輯更多了一層,這顯然沒有達到Netty的初衷。

請關注:Netty框架中的ByteBufjava.nio.ByteBuffer的微創新(2



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章