初识Netty
Netty是什么
参考文献:极客时间傅健老师的《Netty源码剖析与实战》Talk is cheap.show me the code!
官网(https://netty.io/)表明:“Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.”,翻译过来就是:Netty是一个异步事件驱动的网络应用框架,用于快速开发可维护的高性能协议服务器和客户端。简单的拆分可以理解:
①本质:网络应用程序框架
②实现:异步、事件驱动
③特性:高性能、可维护、快速开发
④用途:开发服务器和客户端
这是官网给的一张结构图:
不难看出Nettty一共有三个部分,最底层是核心层,从下往上看,最下面是"零拷贝Buffer",往上一格是"通信层API",再上一格则是"拓展的事件模型",左上是传输层服务,包括Http的Tunnel,TCP的Socket,UDP的Datagram等等,右上则是Netty支持的各种各样的协议,比如我们常说的HTTP协议,WebSocket协议,Google的ProtoBuf协议等等。
第一个HelloWorld
用InterlliJ IDEA建立一个Maven工程,在pom.xml配置文件加入Netty的依赖
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.39.Final</version> </dependency>
编写服务器端MyService.java
package com.lql.netty; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; /** * @author: lql * @date: 2019.10.14 * Description: 第一个Netty程序 服务器 */ public class MyServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap sb = new ServerBootstrap(); sb.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); p.addLast(new LoggingHandler(LogLevel.INFO)); p.addLast(new MyServerHandler()); } }); ChannelFuture f = sb.bind(8090).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
编写MyServerHandler.java
package com.lql.netty; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; /** * @author: lql * @date: 2019.10.14 * Description: */ @Sharable public class MyServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ctx.write(msg); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
再来编写客户端MyClient.java
package com.lql.netty; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; /** * @author: lql * @date: 2019.10.14 * Description: 第一个Netty程序 客户端 */ public class MyClient { public static void main(String[] args) throws Exception { //客户端只需要一个事件循环组 EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline p = socketChannel.pipeline(); p.addLast(new LoggingHandler(LogLevel.INFO)); p.addLast(new MyClientHandle()); } }); //开始客户端 ChannelFuture f = b.connect("127.0.0.1",8090).sync(); //等待连接关闭 f.channel().closeFuture().sync(); } finally { //优雅关闭 group.shutdownGracefully(); } } }
同样,创建MyClientHandle.java
package com.lql.netty; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import java.util.concurrent.TimeUnit; /** * @author: lql * @date: 2019.10.14 * Description: */ public class MyClientHandle extends ChannelInboundHandlerAdapter{ private final ByteBuf firstMessage; public MyClientHandle() { firstMessage = Unpooled.wrappedBuffer("I am echo message".getBytes()); } @Override public void channelActive(ChannelHandlerContext ctx) { ctx.writeAndFlush(firstMessage); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { ctx.write(msg); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws InterruptedException { TimeUnit.SECONDS.sleep(3); ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } }
先启动服务器,后启动客户端,结果是可行的!
Netty也可以和Spring mvc一样。如下所示:
建立HttpServer.java
package com.lql.netty; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.HttpServerExpectContinueHandler; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; /** * @author: lql * @date: 2019.10.14 * Description: * Created with IntelliJ IDEA */ public class HttpServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer() { protected void initChannel(Channel ch) throws Exception { ChannelPipeline p = ch.pipeline(); p.addLast(new HttpServerCodec()); p.addLast(new HttpServerExpectContinueHandler()); p.addLast(new HttpServerHandler()); } }); ChannelFuture channelFuture = b.bind(8899).sync(); channelFuture.channel().closeFuture().sync(); }finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
再建立HttpServerHandler.java
package com.lql.netty; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.*; /** * @author: lql * @date: 2019.10.14 * Description: * Created with IntelliJ IDEA */ public class HttpServerHandler extends SimpleChannelInboundHandler<HttpObject> { private static final byte[] CONTENT = "helloworld".getBytes(); @Override public void channelReadComplete(ChannelHandlerContext ctx) { ctx.flush(); } @Override public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) { if (msg instanceof HttpRequest) { HttpRequest req = (HttpRequest) msg; FullHttpResponse response = new DefaultFullHttpResponse(req.protocolVersion(), HttpResponseStatus.OK, Unpooled.wrappedBuffer(CONTENT)); response.headers() .set(HttpHeaderNames.CONTENT_TYPE, "text/plain") .setInt(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); ctx.write(response); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } }
启动HttpServer:在网址输“http://localhost:8899/”,显示如下:
至此,Netty的HelloWorld已经写完了!