本文是对学到的netty知识的简单总结,在之后的文章中会逐步分析netty源码,如果文中存在错误希望能够指出,谢谢。
Netty是什么
学习netty之前,首先我们来了解下什么是netty。这里简单的来说就是基于NIO实现的高性能异步的通信框架。
因为Netty是基于NIO的,所以为了更加清楚Netty里面的结构,我们回顾下NIO的线程模型,如下图:
了解了NIO模型和什么是netty之后,我们来了解下Netty中常用的模块:
-
【NioEventLoopGroup】:管理 eventLoop 的生命周期,可以理解为一个线程池,内部维护了一组线程,每个线程(NioEventLoop)负责处理多个Channel 上的事件,而一个 Channel 只对应于一个线程。
-
【NioEventLoop】:一个线程,里面维护了一个任务队列,用来处理连接和读写事件,异步支持。
-
【Bootstrap、ServerBootstrap】:可以认为是一个Netty应用的启动类,通过它可以将Netty中的各个组件和配置连接起来,Bootstrap是客户端的启动类,ServerBootstrap是服务端的启动类。
-
【Selector】:Netty 基于 NIO中的Selector 对象实现 I/O 多路复用,用来监听连接请求事件。
-
【Channel】:由NIO模型可知,一个Channel对应一个客户端连接,它是客户端与服务端数据交互的通道,在Channel中,我们可以配置不同的服务端来处理不同的服务,比如:
NioSocketChannel,异步的客户端 TCP Socket 连接。
NioServerSocketChannel,异步的服务器端 TCP Socket 连接。
NioDatagramChannel,异步的 UDP 连接。 -
【ChannelPipline】:一个Channel存在一个ChannelPipline,它里面保存 ChannelHandler 的 List,用于处理或拦截 Channel 的入站事件和出站操作。
-
【ChannelHandler】:是业务处理逻辑的实现,里面可以实现指定的接口,然后通过类似于链条的形式依次执行每个ChannelHandler的某个方法。
-
【ChannelHandlerContext】:保存 Channel 相关的所有上下文信息,同时关联一个 ChannelHandler 对象。
-
为了理清上面一些组件的关系,这里简单作了图【首先通过BossGroup线程组接收来自客户端的连接请求,接下来将这些请求中的事件注册到WorkerGroup中的线程根据管道pipeline中的handler去处理具体连接请求的事件,读、写、等操作】,如下图:
-
了解了上面的组件后,我们来看一下netty实现的服务端代码,如下:
public class NettyServer {
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 创建一个线程用来接收连接
EventLoopGroup workerGroup = new NioEventLoopGroup(); //默认创建线程数为cpu核数的两倍
try {
//创建服务器端的启动对象
ServerBootstrap bootstrap = new ServerBootstrap();
//使用链式编程来配置参数
bootstrap.group(bossGroup, workerGroup) //设置两个线程组
.channel(NioServerSocketChannel.class) //使用NioServerSocketChannel作为服务器的通道实现
//.option(ChannelOption.SO_BACKLOG, 1024) //这里是添加一些额外的参数,比如数据缓存大小,连接数的处理
//.handler(new NettyServerHandler()) //这里可以添加handler
.childHandler(new ChannelInitializer<SocketChannel>() {//创建通道初始化对象,设置初始化参数
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//向管道中添加处理器handler
ch.pipeline().addLast(new NettyServerHandler());
}
});
System.out.println("netty server start。。");
//启动服务器(并绑定端口),bind是异步操作,sync方法是等待异步操作执行完毕
ChannelFuture cf = bootstrap.bind(9000).sync();
//对通道关闭进行监听,closeFuture是异步操作,通过sync方法同步等待通道关闭处理完毕,这里会阻塞等待通道关闭完成
cf.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
- 自定义的处理器handler,这里需要根据你自己的需要重写不同的方法,将常用的方法以给出,如下:
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("这里是客户端连接进来激活的方法");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("这里是客户端关闭激活的方法");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("这里是服务端读取客户端发送的消息,服务端触发的方法");
//将 msg 转成一个 ByteBuf,类似NIO 的 ByteBuffer
ByteBuf buf = (ByteBuf) msg;
System.out.println("客户端发送消息是:" + buf.toString(CharsetUtil.UTF_8));
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("这里是服务端读取完客户端发送的消息后,服务端触发的方法");
ByteBuf buf = Unpooled.copiedBuffer("Hello Client".getBytes(CharsetUtil.UTF_8));
ctx.writeAndFlush(buf);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("这里是有异常时候触发的方法");
ctx.close();
}
}
- 接下来我们来看一下客户端代码的实现:
public class NettyClient {
public static void main(String[] args) throws Exception {
//客户端需要一个事件循环组
EventLoopGroup group = new NioEventLoopGroup();
try {
//创建启动类对象Bootstrap
Bootstrap bootstrap = new Bootstrap();
//设置相关参数
bootstrap.group(group) //设置线程组
.channel(NioSocketChannel.class) // 使用NioSocketChannel作为客户端的通道实现
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//加入处理器
ch.pipeline().addLast(new NettyClientHandler());
}
});
System.out.println("netty client start");
//启动客户端去连接服务器端
ChannelFuture cf = bootstrap.connect("127.0.0.1", 9000).sync();
//对通道关闭进行监听
cf.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
}
- 客户端的NettyClinetHandler实现思路与服务端类似,这里不作说明了。
总结:Netty将NIO进行了二次封装,将NIO实现客户端与服务端通讯的代码简单化,用户只需要按照Netty给定的代码模板,只需要根据不同的需求,实现自己的handler处理器即可。