Netty1:初识Netty
為什么使用Netty
Netty是業(yè)界最流行的NIO框架之一,它的健壯性、功能、性能、可定制性、可擴(kuò)展性在同類框架中都是首屈一指的,它已經(jīng)得到了成百上千的商用項(xiàng)目的證明。對于為什么使用Netty這個話題,我們先看一下使用原生的NIO有什么缺點(diǎn):
- NIO的類庫和API繁雜,使用麻煩,需要熟練掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等,這就像我們會使用Hibernate、MyBatis這些ORM框架而不會直接使用Connection、Statement一樣
- 需要其他額外技能作為鋪墊,必須對多線程和網(wǎng)絡(luò)編程非常熟悉才能寫出高質(zhì)量的NIO程序
- 可靠性能力補(bǔ)齊,工作量和難度都非常大,例如客戶端面臨斷線重連、網(wǎng)絡(luò)閃斷、半包讀寫、失敗緩存、網(wǎng)絡(luò)擁塞、異常碼流等問題的處理
- JDK NIO的BUG,例如著名的epoll bug,該問題會導(dǎo)致Selector空輪訓(xùn),最終導(dǎo)致CPU 100%
也正是因?yàn)橛蟹N種缺點(diǎn),因此不建議使用原生的NIO而是建議使用一些比較成熟的NIO框架例如Netty、Mina,這一系列文章講的是Netty,Netty作為一款高性能NIO框架,其優(yōu)點(diǎn)總結(jié)有:
- API使用簡單、開發(fā)門檻低
- 功能強(qiáng)大,預(yù)置了多種編碼解碼功能,支持多種主流協(xié)議
- 定制能力強(qiáng),可以通過ChannelHandler對通信框架進(jìn)行靈活擴(kuò)展
- 性能高,與業(yè)界其他主流NIO框架對比,Netty性能最優(yōu)
- 成熟、穩(wěn)定,Netty修復(fù)了已經(jīng)發(fā)現(xiàn)的所有JDK NIO的BUG,業(yè)務(wù)開發(fā)人員不需要再為NIO的BUG而煩惱
- 社區(qū)活躍、版本迭代周期短,發(fā)現(xiàn)的BUG可以被及時(shí)修復(fù),同時(shí),更多的新功能會被加入
- 經(jīng)歷了大規(guī)模的商業(yè)應(yīng)用考驗(yàn),質(zhì)量得到驗(yàn)證
正因?yàn)檫@些優(yōu)點(diǎn),Netty逐漸成為了Java NIO變成的首選框架。
?
Netty入門Demo
下面演示一下Netty的Demo(注:Demo來自Netty權(quán)威指南第三章),本文只寫代碼與演示結(jié)果,不做講解,對Netty的使用基本講解放在下一篇文章中,循序漸進(jìn),先感性地認(rèn)識Netty,再理性地認(rèn)識Netty中的東西。
提一下,本文及之后的文章Netty基于5.0.0.Alpha1這個版本,貼一下我自己的Maven配置吧:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.xrq.netty</groupId><artifactId>netty-test</artifactId><version>1.0.0</version><packaging>jar</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>5.0.0.Alpha1</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.25</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version></dependency></dependencies></project>首先從服務(wù)端代碼開始,定義一個TimeServer:
1 public class TimeServer { 2 3 public void bind(int port) throws Exception { 4 // NIO線程組 5 EventLoopGroup bossGroup = new NioEventLoopGroup(); 6 EventLoopGroup workerGroup = new NioEventLoopGroup(); 7 8 try { 9 ServerBootstrap b = new ServerBootstrap(); 10 b.group(bossGroup, workerGroup) 11 .channel(NioServerSocketChannel.class) 12 .option(ChannelOption.SO_BACKLOG, 1024) 13 .childHandler(new ChildChannelHandler()); 14 15 // 綁定端口,同步等待成功 16 ChannelFuture f = b.bind(port).sync(); 17 // 等待服務(wù)端監(jiān)聽端口關(guān)閉 18 f.channel().closeFuture().sync(); 19 } finally { 20 // 優(yōu)雅退出,釋放線程池資源 21 bossGroup.shutdownGracefully(); 22 workerGroup.shutdownGracefully(); 23 } 24 } 25 26 private class ChildChannelHandler extends ChannelInitializer<SocketChannel> { 27 @Override 28 protected void initChannel(SocketChannel arg0) throws Exception { 29 arg0.pipeline().addLast(new TimeServerHandler()); 30 } 31 } 32 33 }TimeServerHandler這么定義:
1 public class TimeServerHandler extends ChannelHandlerAdapter { 2 3 @Override 4 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 5 ByteBuf buf = (ByteBuf)msg; 6 byte[] req = new byte[buf.readableBytes()]; 7 buf.readBytes(req); 8 9 String body = new String(req, "UTF-8"); 10 System.out.println("The time server receive order:" + body); 11 String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER"; 12 13 ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes()); 14 ctx.write(resp); 15 } 16 17 @Override 18 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 19 ctx.flush(); 20 } 21 22 @Override 23 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 24 ctx.close(); 25 } 26 27 }即讀取來自客戶端的數(shù)據(jù),如果是"QUERY TIME ORDER",則把當(dāng)前時(shí)間寫到Channel中去。至此,Netty服務(wù)端代碼已經(jīng)開發(fā)完畢。接下來是Netty客戶端代碼,首先還是TimeClient:
1 public class TimeClient { 2 3 public void connect(int port, String host) throws Exception { 4 EventLoopGroup group = new NioEventLoopGroup(); 5 try { 6 Bootstrap b = new Bootstrap(); 7 8 b.group(group) 9 .channel(NioSocketChannel.class) 10 .option(ChannelOption.TCP_NODELAY, true) 11 .handler(new ChannelInitializer<SocketChannel>() { 12 protected void initChannel(SocketChannel ch) throws Exception { 13 ch.pipeline().addLast(new TimeClientHandler()); 14 }; 15 }); 16 17 // 發(fā)起異步連接操作 18 ChannelFuture f = b.connect(host, port).sync(); 19 // 等待客戶端連接關(guān)閉 20 f.channel().closeFuture().sync(); 21 } finally { 22 // 優(yōu)雅退出,釋放NIO線程組 23 group.shutdownGracefully(); 24 } 25 } 26 27 }同樣的,定義一個TimeClientHandler:
1 public class TimeClientHandler extends ChannelHandlerAdapter { 2 3 private static final Logger LOGGER = LoggerFactory.getLogger(TimeClientHandler.class); 4 5 private final ByteBuf firstMessage; 6 7 public TimeClientHandler() { 8 byte[] req = "QUERY TIME ORDER".getBytes(); 9 firstMessage = Unpooled.buffer(req.length); 10 firstMessage.writeBytes(req); 11 } 12 13 @Override 14 public void channelActive(ChannelHandlerContext ctx) throws Exception { 15 ctx.writeAndFlush(firstMessage); 16 } 17 18 @Override 19 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 20 ByteBuf buf = (ByteBuf)msg; 21 byte[] req = new byte[buf.readableBytes()]; 22 buf.readBytes(req); 23 24 String body = new String(req, "UTF-8"); 25 System.out.println("Now is:" + body); 26 } 27 28 @Override 29 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 30 LOGGER.warn("Unexcepted exception from downstream:" + cause.getMessage()); 31 ctx.close(); 32 } 33 34 }客戶端的操作為打印來自服務(wù)端的數(shù)據(jù),這樣,整個Netty Demo代碼就寫完了,結(jié)構(gòu)比較清楚,都是一個Server+一個Handler的模式,Handler用于處理讀取到的信息。
?
運(yùn)行Demo
上面寫完了Demo,接著寫一下測試代碼,很簡單,分別運(yùn)行bind方法和connect方法即可:
1 public class CoreTest { 2 3 @Test 4 public void timeServerTest() throws Exception { 5 new TimeServer().bind(8080); 6 } 7 8 @Test 9 public void timeClientTest() throws Exception { 10 new TimeClient().connect(8080, "127.0.0.1"); 11 } 12 13 }先運(yùn)行timeServerTest讓服務(wù)端先啟動,再運(yùn)行timeClientServer讓客戶端后啟動,運(yùn)行結(jié)果服務(wù)端的打印為:
The time server receive order:QUERY TIME ORDER結(jié)合代碼可以看到,服務(wù)端讀取到了來自客戶端的數(shù)據(jù),數(shù)據(jù)內(nèi)容為"QUERY TIME ORDER",接著服務(wù)端取自己的時(shí)間,傳輸給客戶端,看一下客戶端的打印:
Now is:Thu Apr 05 21:07:39 CST 2018打印了來自服務(wù)端的時(shí)間,這樣,利用Netty進(jìn)行服務(wù)端+客戶端的相互通信的Demo完成,有了這個Demo,對Netty有了感性上的認(rèn)識,接著我們一點(diǎn)一點(diǎn)深入去學(xué)習(xí)Netty。
總結(jié)
以上是生活随笔為你收集整理的Netty1:初识Netty的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CSS缩写总结
- 下一篇: 基于DeepConvLSTM的传感器信号