初識Netty -- 基於Netty的DayTime時間服務器
1. 關於Netty的基本認知:
在JDK 1.4推出Java NIO之前,基於Java的所有Socket通信都采用的BIO(同步阻塞式IO),同步阻塞式IO存在巨大的性能和可靠性瓶頸,無法適用於高性能服務器的開發。雖然後來出現了偽異步I/O通信框架,但它僅僅是對之前I/O線程模型的一個簡單優化。
在JDK 1.4之後,Java誕生了NIO,也就是引入了java.nio類庫,提供了很多進行異步I/O開發的API。隨著java NIO的發展,對於高性能的服務器開發來說,java原生NIO仍然存在許多問題,甚至出現了非常嚴重的epoll bug。另外Java原生NIO的API類庫繁雜,上手難度大,並非開發者心中理想的框架。
Netty是業界最流行的NIO框架之一,它經歷的大規模的商業應用考驗,在互聯網、大數據、網絡遊戲等眾多行業已經得到了成功商用,因此Netty逐漸成為了Java NIO編程的首選框架。
2. Netty開發環境搭建
可以使用maven自動構建java項目,本例中只需一個netty jar包,手動導入即可。
下載Netty jar包並導入Java項目中。Netty jar包到http://netty.io/ (netty官網)的download頁面,本例中就使用最新的netty-4.1.16.Final,在解壓之後的文件夾中找到netty-all-4.1.16.Final.jar,路徑netty-4.1.16.Final > jar > all-in-one 。
在java項目中創建lib文件夾,導入netty jar包(jar包要作為庫文件導入)。
本例中需要創建兩個項目,分別是時間服務器和時間客戶端。
3. 基於Netty的DayTime時間服務器與客戶端架構
DayTime服務器只需一個Server啟動類和一個業務邏輯處理類(處理客戶端請求)即可,客戶端也只需一個Client啟動類和一個業務邏輯處理類(向服務器發送請求並處理服務器響應),參考下圖。
①TimeServer.java(服務器啟動類)源碼如下:
--------------------------------------------------------------------------------------------------------------------
public class TimeServer {
public void bind(int port) throws Exception {
//配置服務端NIO線程組。
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChildChannelHandler());
//綁定端口,同步等待成功。
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
//等待服務端監聽端口關閉。
channelFuture.channel().closeFuture().sync();
} finally {
//優雅退出,釋放線程池資源。
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
--------------------------------------------------------------------------------------------------------------------
②TimeServerHandler.java(處理客戶端請求)源碼如下:
--------------------------------------------------------------------------------------------------------------------
public class TimeServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext channelHandlerContext, Object msg)
throws Exception {
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "UTF-8");
System.out.println("時間服務器收到請求:" + body);
String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(
System.currentTimeMillis()).toString() : "BAD ORDER";
ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
channelHandlerContext.write(resp);
}
@Override
public void channelReadComplete(ChannelHandlerContext channelHandlerContext)
throws Exception {
channelHandlerContext.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
--------------------------------------------------------------------------------------------------------------------
③TimeClient.java(客戶端啟動類)源碼如下(流程與服務器啟動類類似):
--------------------------------------------------------------------------------------------------------------------
public class TimeClient {
public void connect(int port, String host) throws Exception {
//配置客戶端NIO線程組。
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new TimeClientHandler());
}
});
//發起異步連接操作。
ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
//等待客戶端鏈路關閉。
channelFuture.channel().closeFuture().sync();
} finally {
//優雅推出,釋放NIO線程組。
eventLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
if (args != null && args.length > 0) {
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
//采用默認值。
}
}
new TimeClient().connect(port, "127.0.0.1");
}
}
--------------------------------------------------------------------------------------------------------------------
④TimeClientHandler.java(客戶端業務邏輯處理類)源碼如下:
--------------------------------------------------------------------------------------------------------------------
public class TimeClientHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = Logger
.getLogger(TimeClientHandler.class.getName());
private final ByteBuf firstMessage;
public TimeClientHandler() {
byte[] request = "QUERY TIME ORDER".getBytes();
firstMessage = Unpooled.buffer(request.length);
firstMessage.writeBytes(request);
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.writeAndFlush(firstMessage);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
byte[] req = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(req);
String body = new String(req, "UTF-8");
System.out.println("Now is:" + body);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 釋放資源
logger.warning("Unexpected exception from downstream : " + cause.getMessage());
ctx.close();
}
}
--------------------------------------------------------------------------------------------------------------------
4. 啟動服務器,運行客戶端
先啟動服務器,然後運行客戶端,此時服務收到客戶端的獲取時間的請求“QUERY TIME ORDER”,會在控制臺輸出。客戶端此時會接收到服務器響應消息,也就是當前服務器時間。
本文出自 “一赫。” 博客,謝絕轉載!
初識Netty -- 基於Netty的DayTime時間服務器