Netty實戰之(一)入門示例

Netty是什麼?

Netty是一個異步的、事件驅動的網絡編程框架,用於快速開發可維護的、高性能的網絡服務器、客戶端應用程序。


Netty特點

簡單易用,提供統一的API來支持阻塞式及非阻塞式I/O。靈活、可擴展的事件模型,更容易實現清晰的關注點分離。可定製的線程模型,單線程、多線程、多級事件驅動。高吞吐、低延遲。更少的資源佔用。最少化不必要的內存拷貝。

引入Netty依賴

<code> <dependency> <groupid>io.netty/<groupid> <artifactid>netty-handler/<artifactid> <version>4.1.45.Final/<version> /<dependency> <dependency> <groupid>ch.qos.logback/<groupid> <artifactid>logback-classic/<artifactid> <version>1.2.3/<version> /<dependency> <dependency> <groupid>junit/<groupid> <artifactid>junit/<artifactid> <version>4.11/<version> <scope>test/<scope> /<dependency>/<code>

創建服務端

創建服務端應用的代碼如下

<code> @Test public void testNettyServer() { // 0 NioEventLoopGroup parentGroup = new NioEventLoopGroup(1); NioEventLoopGroup childGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors() << 1); try { new ServerBootstrap()// 1 .group(parentGroup, childGroup)// 2 .channel(NioServerSocketChannel.class)//3 .handler(new LoggingHandler())//4 .childHandler(new ChannelInitializer<socketchannel>() {// 5 @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline() .addLast(new LengthFieldBasedFrameDecoder(2048, 0, 4, 0, 4)) .addLast(new StringDecoder()) .addLast(new LengthFieldPrepender(4, 0)) .addLast(new StringEncoder()) .addLast(new SimpleChannelInboundHandler<string>() {// 5.1 @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { log.info("msg from client: {}", msg); ctx.writeAndFlush(msg); } }); } }) .option(ChannelOption.SO_BACKLOG, 512)// 6 .childOption(ChannelOption.SO_KEEPALIVE, true)// 7 .bind(new InetSocketAddress(2020)).sync()// 8 .addListener(future -> log.info("Netty服務端綁定端口完成,等待客戶端鏈接......")) .channel().closeFuture().sync()// 9 ; } catch (Exception e) { log.error("Netty服務器啟動失敗", e); } finally {// 10 parentGroup.shutdownGracefully(); childGroup.shutdownGracefully(); } }/<string>/<socketchannel>/<code>NioEventLoopGroup是用於處理I/O操作的多線程事件循環器,這裡創建的兩個實例分別用於處理服務端、客戶端 I/O操作。ServerBootstrap用於構建服務端Channel實例。指定了用於處理I/O操作的NioEventLoopGroup實例。這裡指定了使用NioServerSocketChannel,即服務端將使用非阻塞式I/O來處理客戶端鏈接、請求、響應。用於服務端的Handler實例。創建一個ChannelHandler實例。設置服務端Socket參數。設置客戶端鏈接的Socket參數。綁定端口並開始接受客戶端鏈接。等待直到服務端Socket關閉。(這裡不會發生)關閉NioEventLoopGroup。

這樣,一個簡單的服務端應用就開發完成了。

創建客戶端

創建客戶端應用的代碼如下

<code> @Test public void testNettyClient() { // 0 NioEventLoopGroup group = new NioEventLoopGroup(1); try { Channel channel = new Bootstrap()// 1 .group(group)// 2 .channel(NioSocketChannel.class)// 3 .handler(new ChannelInitializer<socketchannel>() {// 4 @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline() .addLast(new LengthFieldBasedFrameDecoder(2048, 0, 4, 0, 4)) .addLast(new StringDecoder()) .addLast(new LengthFieldPrepender(4, 0)) .addLast(new StringEncoder()) .addLast(new SimpleChannelInboundHandler<string>() {// 4.1 @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { log.info("msg from server: {}", msg); } }); } }) .option(ChannelOption.SO_KEEPALIVE, true)// 5 .option(ChannelOption.TCP_NODELAY, true) .connect(new InetSocketAddress(2020)).sync()// 6 .addListener(future -> log.info("Netty客戶端已鏈接到服務端")) .channel(); // 7 channel.writeAndFlush("Hello, Netty!").addListener(future -> log.info("Netty客戶端發送數據完成")); // 8 channel.closeFuture().sync(); } catch (Exception e) { log.error("Netty客戶端啟動失敗", e); } finally {// 9 group.shutdownGracefully(); } }/<string>/<socketchannel>/<code>創建NioEventLoopGroup實例。Bootstrap用於幫助構建客戶端Channel實例。指定用於處理I/O操作的NioEventLoopGroup實例。同服務端類似,這裡指定創建NioSocketChannel的實例。創建一個ChannelHandler實例。設置客戶端鏈接的Socket參數。鏈接到指定的服務器及端口,這裡服務器是本機。使用創建的NioSocketChannel實例向服務端發送數據。等待直到NioSocketChannel實例關閉。關閉NioEventLoopGroup實例。

客戶端也創建完了。

分別啟動服務端示例、客戶端示例,日誌中可以看到服務端、客戶端已經可以正常通信了。

總結

以上開發了簡單的服務器、客戶端示例程序,從中可以看出,使用Netty開發網絡應用的簡單、易用性;同時也可以看出,Netty構建服務端、客戶端的API基本是統一的。

文章僅展現了服務器、客戶端示例程序,後續文章將對代碼中涉及的以下相關內容進行簡單的探討學習。

NioEventLoopGroupNioServerSocketChannel、NioSocketChannelChannelInitializer、ChannelHandlerLengthFieldBasedFrameDecoder、LengthFieldPrependerStringDecoder、StringEncoderSimpleChannelInboundHandler