一、说说Netty的工作原理
上图
上面的这个图从网上找来的,对这个图进行说明:
Netty抽象出两组线程池,BossGroup专门负责接收客户端的连接,WorkerGroup专门负责网络的读写BossGroup和WorkerGroup类型的本质都是NioEventLoopGroup类型。NioEventLoopGroup相当于一个线程管理器(类似于ExecutorServevice),它下面维护很多个NioEventLoop线程。(我认为图中的NioEventGroup的地方应该改成NioEventLoop,可能我的理解有点差错吧)- 在初始化这两个
Group线程组时,默认会在每个Group中生成CPU*2个NioEventLoop线程 - 当
n个连接来了,Group默认会按照连接请求的顺序分别将这些连接分给各个NioEventLoop去处理。 - 同时
Group还负责管理EventLoop的生命周期。
- 在初始化这两个
NioEventLoop表示一个不断循环的执行处理任务的线程- 它维护了一个线程和任务队列。
- 每个
NioEventLoop都包含一个Selector,用于监听绑定在它上面的socket通讯。 - 每个
NioEventLoop相当于Selector,负责处理多个Channel上的事件 - 每增加一个请求连接,
NioEventLoopGroup就将这个请求依次分发给它下面的NioEventLoop处理。
- 每个
Boss NioEventLoop循环执行的步骤有3步:- 轮询
accept事件 - 处理
accept事件,与client建立连接,生成NioSocketChannel,并将其注册到某个Worker NioEventLoop的selector上。 - 处理任务队列到任务,即
runAllTasks
- 轮询
- 每个
Worker NioEventLoop循环执行的步骤:- 轮询
read,write事件 - 处理
I/O事件,即read,write事件,在对应的NioSocketChannel中进行处理 - 处理任务队列的任务,即
runAllTasks
- 轮询
- 每个
Worker NioEventLoop处理业务时,会使用pipeline(管道),pipeline中维护了一个ChannelHandlerContext链表,而ChannelHandlerContext则保存了Channel相关的所有上下文信息,同时关联一个ChannelHandler对象。
二、写个netty的例子
package com.angliali.nio.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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.channel.socket.nio.NioSocketChannel;
/**
* 使用netty进行简单的请求处理
*/
public class NettyServer {
public static void main(String[] args) throws InterruptedException {
//初始化两个工作组,这两个工作组,都是线程池
EventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//设置启动器,开始使用netty
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup) //设置两个线程组
.channel(NioServerSocketChannel.class) //指定channel的类型,这里使用netty自己的Nioserversocketchannel
.option(ChannelOption.SO_BACKLOG,128)//设置线程队列得到的连接个数
.childOption(ChannelOption.SO_KEEPALIVE,true)//设置保持活动连接状态
.childHandler(new ChannelInitializer<SocketChannel>() {
//给pipeline设置handler
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyServerHandler());
}
});
System.out.println("服务器装备就绪,马上启动");
//绑定端口,并生成一个异步对象
ChannelFuture cf = serverBootstrap.bind(6668).sync();
//对断开连接进行监听
cf.channel().close().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
package com.angliali.nio.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
/**
* 客户端的handler
*/
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
//通道就绪后,执行该方法
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("ctx:"+ctx);
ctx.writeAndFlush(Unpooled.copiedBuffer("hello,服务器", CharsetUtil.UTF_8));
}
//当通道有读取事件时,执行
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("ctx:"+ctx);
//处理服务器返回的消息
ByteBuf buf = (ByteBuf) msg;
System.out.println("服务器返回的消息:"+buf.toString(CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
package com.angliali.nio.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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.channel.socket.nio.NioSocketChannel;
/**
* 使用netty进行简单的请求处理
*/
public class NettyServer {
public static void main(String[] args) throws InterruptedException {
//初始化两个工作组,这两个工作组,都是线程池
EventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//设置启动器,开始使用netty
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup) //设置两个线程组
.channel(NioServerSocketChannel.class) //指定channel的类型,这里使用netty自己的Nioserversocketchannel
.option(ChannelOption.SO_BACKLOG,128)//设置线程队列得到的连接个数
.childOption(ChannelOption.SO_KEEPALIVE,true)//设置保持活动连接状态
.childHandler(new ChannelInitializer<SocketChannel>() {
//给pipeline设置handler
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyServerHandler());
}
});
System.out.println("服务器装备就绪,马上启动");
//绑定端口,并生成一个异步对象
ChannelFuture cf = serverBootstrap.bind(6668).sync();
//对断开连接进行监听
cf.channel().close().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
package com.angliali.nio.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
import java.nio.charset.Charset;
/**
* netty中的handler
*/
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
//这个方法就是用来处理读取请求对象
//简单处理请求,输出请求的原始地址
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("请求上下文:"+ctx);
ByteBuf buf = (ByteBuf) msg;
System.out.println("请求对象:"+buf.toString(CharsetUtil.UTF_8));
//输出原始地址
System.out.println("请求地址:"+ctx.channel().remoteAddress());
}
//请求处理完后的操作
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("hello",CharsetUtil.UTF_8));
}
//发生异常时的处理
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
三、简单说明下上面的列子
上面这个例子是用netty做的简单的网络编程,有些方法后面会说到。这里主要说明下bootstrap,我称它为启动器。
上面那串链式编程的主要用途就是为了设置netty工作原理的构建。
- 绑定工作线程组
- 设置channel的实现类型
- 设置主nioeventloopgroup的属性
- 设置从nioeventloopgroup的属性
- 给pipeline绑定handler
其余后面再说啦!