什么是netty
netty是一个nio的客户端服务器rpc框架,开发者可以很快很容易和开发出协议服务器和客户端等网络应用。它是一个高性能的基于socket的网络通信框架。它在高并发的业务场景下常常有很好的性能表现。
netty核心组件
- Channel
- 回调
- Future
- 事件和ChannelHandler
这些构建代表了不同类型的构造:资源、逻辑以及通知。你的应用将使用它们来访问网络以及流经网络的数据。
Channel
Channel是jave nio的一个基本构造。它代表一个到实体的开放链接,如读操作和写操作,即可将channel看作是一个输入输出的载体。因此它应该具有打开或者关闭,连接或者断开连接的功能。
回调
一个回调其实就是一个方法,一个指向已经被提供给另外一个方法的方法的引用。这使得后者(接受回调的方法)可以在适当的时候调用前者。
Future
Future提供了另外一种在操作完成时通知应用程序的方式,这个对象可以看做是一个异步操作的结果的占位符:他将在未来的某个时刻完成,并提供对其结果的访问。
事件和ChannelHandler
Netty使用不同的事件来通知我们状态的改变或者是操作的状态,这使得我们能够基于已经发生的事件来触发适当的动作。这些动作可能是:
- 记录日志
- 数据转换
- 流控制
- 应用程序逻辑
也就是说我们对数据的处理逻辑一般都会定义在ChannelHandler中。
因此,在开发netty程序时,请注意以下3个关键点:
- 针对不同类型的时间来调用ChannelHandler
- 应用程序通过文件或者扩展ChannelHandler来挂钩到时间的生命周期,并且提供自定义的应用程序逻辑
- 在架构上,ChannelHandler有助于保持业务逻辑与网络处理代码的分离。这简化了开发过程,因为代码必须不断地演化以响应不断变化的需求
第一个netty程序
使用netty开发了一个简单的http程序,使用浏览器作为客户端,我们只需要实现服务端逻辑。
netty的编程模型一般分为三步,首先是定义一个程序启动入口,其次是定义一个初始化器,最后需要指定一个ChannelHandler。
首先需要定义一个程序的启动入口,对于代码中每个组件的详细解释此处暂时不做说明,代码清单如下:
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class TestServer {
public static void main(String[] args) throws Exception {
//不断从客户端接受连接,但是不对连接作出处理,然后将连接转给worker
EventLoopGroup bossGroup = new NioEventLoopGroup();
//处理连接
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)//指定所使用的nio传输channel
.childHandler(new TestServerInitializer());
ChannelFuture channelFuture = bootstrap.bind(8899).sync();
channelFuture.channel().closeFuture().sync();
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
netty每次启动时,都会首先执行初始化方法,在初始化器中我们可以添加一些自定义或者已经实现的处理器,比如编解码器等等。如下所示,TestServerInitializer继承自ChannelInitializer并且实现了initChannel方法,该方法在server创建时就会被执行,代码清单如下:
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
public class TestServerInitializer extends ChannelInitializer<SocketChannel> {
//server 一旦被创建就会执行这个方法
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("httpServerCodec", new HttpServerCodec());
//添加自定义的处理器
pipeline.addLast("testHttpServerHandler", new TestHttpServerHandler());
}
}
对于我们自定义的处理器,通常通过ChannelInitializer
的initChannel
方法添加,比如在我们实现的代码中就添加了一个名为testHttpServerHandler的自定义处理器,该处理器会读取客户端的请求,并返回客户端请求响应。定义一个TestHttpServerHandler
类,该类继承自抽象类SimpleChannelInboundHandler
,并需要实现channelRead0方法,该方法定义了定义了客户端的请求的读取和返回逻辑。代码清单如下:
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
//读取客户端发送的请求,并返回客户端请求响应
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
if (msg instanceof HttpRequest) {
HttpRequest httpRequest=(HttpRequest)msg;
System.out.println("请求方法名:"+httpRequest.method().name());
ByteBuf content = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.OK, content);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
ctx.writeAndFlush(response);
ctx.channel().close();
}
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelActive");
super.channelActive(ctx);
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelRegistered");
super.channelRegistered(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelInactive");
super.channelInactive(ctx);
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelUnregistered");
super.channelUnregistered(ctx);
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("handlerAdded");
super.handlerAdded(ctx);
}
}
在server启动时,netty首先会监听到handler的add,然后会注册该handler,将该handler的状态更改为active,然后会处理该handler的msg方法,即channelRead0方法中我们自定义的处理逻辑,处理完以后会将该handler置为inactive,最终将该handler的注册状态更改为unregistered。
为了验证以上的handler的生命周期,我们重写了SimpleChannelInboundHandler
的生命周期方法,并添加了打印字段。
启动TestServer,并在浏览器输入localhost:8899,浏览器会返回hello world字符串,在终端中,我们可以看到如下输入:
- handlerAdded
- channelRegistered
- channelActive
- 请求方法名:GET
- channelInactive
- channelUnregistered