事件循环对象
EventLoop本质是一个单线程执行器(同时维护了一个Selector,里面有run方法处理Channel上源源不断的io事件
它的继承关系比较复杂
事件循环组
EventLoopGroup 是一组 EventLoop,Channel 一般会调用 EventLoopGroup 的 register 方法来绑定其中一个 EventLoop,后续这个 Channel 上的 io 事件都由此 EventLoop 来处理(保证了 io 事件处理时的线程安全)
示例
// 内部创建了两个 EventLoop, 每个 EventLoop 维护一个线程
EventLoopGroup eventLoopGroup=new NioEventLoopGroup(2);
EventLoop event = eventLoopGroup.next();//获取EventLoop对象,当遍历到尾节点后又从头开始
System.out.println(event);
EventLoop event2 = eventLoopGroup.next();
System.out.println(event2);
EventLoop event3 = eventLoopGroup.next();
System.out.println(event3);
输出
io.netty.channel.nio.NioEventLoop@282ba1e
io.netty.channel.nio.NioEventLoop@13b6d03
io.netty.channel.nio.NioEventLoop@282ba1e
普通任务
EventLoop event = eventLoopGroup.next();//获取EventLoop对象,当遍历到尾节点后又从头开始
//执行普通任务
event.submit(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("run task ...");
});
event.shutdownGracefully();
定时任务
event.scheduleAtFixedRate(()->{
System.out.println("run schedule task ...");
},0,1,TimeUnit.SECONDS);
System.out.println("main ...");
关闭 EventLoopGroup
优雅关闭 shutdownGracefully
方法。该方法会首先切换 EventLoopGroup
到关闭状态从而拒绝新的任务的加入,然后在任务队列的任务都处理完成后,停止线程的运行。从而确保整体应用是在正常有序的状态下退出的
服务器端代码
public class Server {
public static void main(String[] args) {
new ServerBootstrap().
group(new NioEventLoopGroup())
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter(){
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println(buf.toString(StandardCharsets.UTF_8) + "=>"+Thread.currentThread().getName());
}
});
}
})
.bind(8888);
}
}
客户端代码
public class Client {
public static void main(String[] args) throws InterruptedException, IOException {
Channel channel = new Bootstrap()
.group(new NioEventLoopGroup())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new StringEncoder());
}
}).connect(new InetSocketAddress("localhost", 8888))
.sync()
.channel();
channel.writeAndFlush("haha");
}
}
Bootstrap的group()方法可以传入两个EventLoopGroup参数,参数1为BossEventGroup
,参数2为WorkerEventGroup
,单个参数的方法会为Boss与Worker创建相同的EventGroup
public ServerBootstrap group(EventLoopGroup group) { //单参方法
return this.group(group, group);
}
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) { //双参方法
super.group(parentGroup);
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
} else {
this.childGroup = (EventLoopGroup)ObjectUtil.checkNotNull(childGroup, "childGroup");
return this;
}
}
当有的任务需要较长的时间处理时,可以使用非NioEventLoopGroup,避免同一个NioEventLoop中的其他Channel在较长的时间内都无法得到处理
服务器端代码
public class Server {
public static void main(String[] args) {
EventLoopGroup group=new DefaultEventLoopGroup();//处理耗时任务,防止阻塞worker thread
new ServerBootstrap()
//arg1: BossEventLoopGroup arg2:WorkerEventGroup
.group(new NioEventLoopGroup(1), new NioEventLoopGroup(2))
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println(Thread.currentThread().getName() + " " + buf.toString(StandardCharsets.UTF_8));
ctx.fireChannelRead(msg); //传递msg给下一个handler
}
}).addLast(group,"handlerA",new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println(Thread.currentThread().getName() + " " + buf.toString(StandardCharsets.UTF_8));
}
});
}
})
.bind(8888);
}
}
客户端代码
public class Client {
public static void main(String[] args) throws InterruptedException, IOException {
Channel channel = new Bootstrap()
.group(new NioEventLoopGroup())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new StringEncoder());
}
}).connect(new InetSocketAddress("localhost", 8888))
.sync()
.channel();
channel.writeAndFlush("hello");
}
}
输出
defaultEventLoopGroup-2-3 1
nioEventLoopGroup-4-1 1
defaultEventLoopGroup-2-3 1
nioEventLoopGroup-4-2 2
defaultEventLoopGroup-2-4 2
nioEventLoopGroup-4-2 2
defaultEventLoopGroup-2-4 2
nioEventLoopGroup-4-1 3
defaultEventLoopGroup-2-5 3
可以看到,nio 工人和 非 nio 工人也分别绑定了 channel
Handler执行中如何换人?
源码:io.netty.channel.AbstractChannelHandlerContext#invokeChannelRead()
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
// 获得下一个EventLoop, excutor即为 EventLoopGroup
EventExecutor executor = next.executor();
// 如果下一个EventLoop 在当前的 EventLoopGroup中
if (executor.inEventLoop()) {
// 使用当前 EventLoopGroup 中的 EventLoop 来处理任务
next.invokeChannelRead(m);
} else {
// 否则让另一个 EventLoopGroup 中的 EventLoop 来创建任务并执行
executor.execute(new Runnable() {
public void run() {
next.invokeChannelRead(m);
}
});
}
}
channel的主要作用
ChannelFuture
public class Client {
public static void main(String[] args) throws InterruptedException, IOException {
ChannelFuture future = new Bootstrap()
.group(new NioEventLoopGroup())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new StringEncoder());
}
}).connect(new InetSocketAddress("localhost", 8888));
//.sync() //不使用sync方法则返回未连接成功的channel对象[id: 0x4218fad1]
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
Channel channel = channelFuture.channel();
System.out.println(channel); //返回已连接的channel [id: 0x41bbc250, L:/127.0.0.1:57536 - R:localhost/127.0.0.1:8888]
channel.writeAndFlush("async ...");
}
});
}
}
sync
方法进行同步阻塞或着调用addListener
方法添加异步回调CloseFuture
@Slf4j
public class ClientClose {
public static void main(String[] args) throws InterruptedException {
NioEventLoopGroup group = new NioEventLoopGroup();
ChannelFuture channelFuture = new Bootstrap()
.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
socketChannel.pipeline().addLast(new StringEncoder());
}
})
.connect(new InetSocketAddress("localhost", 8888));
Channel channel = channelFuture.sync()
.channel();
log.info("connect success! " + channel);
new Thread(() -> {
Scanner scanner = new Scanner(System.in);
while (true) {
String msg = scanner.nextLine();
if ("q".equals(msg)) {
//close channel
channel.close(); // async
break;
}
log.info("send msg:" + msg);
channel.writeAndFlush(msg);
}
}, "myThread").start();
ChannelFuture closedFuture = channel.closeFuture();
// closedFuture.sync();
closedFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
log.info("channel closed!....");
group.shutdownGracefully();
}
});
}
}