爲何使用Nettyapache
Netty是業界最流行的NIO框架之一,它的健壯性、功能、性能、可定製性、可擴展性在同類框架中都是數一數二的,它已經獲得了成百上千的商用項目的證實。對於爲何使用Netty這個話題,咱們先看一下使用原生的NIO有什麼缺點:編程
也正是由於有種種缺點,所以不建議使用原生的NIO而是建議使用一些比較成熟的NIO框架例如Netty、Mina,這一系列文章講的是Netty,Netty做爲一款高性能NIO框架,其優勢總結有:api
正由於這些優勢,Netty逐漸成爲了Java NIO變成的首選框架。緩存
Netty入門Demo網絡
下面演示一下Netty的Demo(注:Demo來自Netty權威指南第三章),本文只寫代碼與演示結果,不作講解,對Netty的使用基本講解放在下一篇文章中,按部就班,先感性地認識Netty,再理性地認識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>
首先從服務端代碼開始,定義一個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 // 等待服務端監聽端口關閉 18 f.channel().closeFuture().sync(); 19 } finally { 20 // 優雅退出,釋放線程池資源 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這麼定義:maven
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 }
即讀取來自客戶端的數據,若是是"QUERY TIME ORDER",則把當前時間寫到Channel中去。至此,Netty服務端代碼已經開發完畢。接下來是Netty客戶端代碼,首先仍是TimeClient:ide
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 // 發起異步鏈接操做 18 ChannelFuture f = b.connect(host, port).sync(); 19 // 等待客戶端鏈接關閉 20 f.channel().closeFuture().sync(); 21 } finally { 22 // 優雅退出,釋放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 }
客戶端的操做爲打印來自服務端的數據,這樣,整個Netty Demo代碼就寫完了,結構比較清楚,都是一個Server+一個Handler的模式,Handler用於處理讀取到的信息。
運行Demo
上面寫完了Demo,接着寫一下測試代碼,很簡單,分別運行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 }
先運行timeServerTest讓服務端先啓動,再運行timeClientServer讓客戶端後啓動,運行結果服務端的打印爲:
The time server receive order:QUERY TIME ORDER
結合代碼能夠看到,服務端讀取到了來自客戶端的數據,數據內容爲"QUERY TIME ORDER",接着服務端取本身的時間,傳輸給客戶端,看一下客戶端的打印:
Now is:Thu Apr 05 21:07:39 CST 2018
打印了來自服務端的時間,這樣,利用Netty進行服務端+客戶端的相互通訊的Demo完成,有了這個Demo,對Netty有了感性上的認識,接着咱們一點一點深刻去學習Netty。