1、EventLoop
EventLoop is essentially a single-threaded executor (while maintaining a Selector), which has a run method to handle the continuous IO events on the Channel.
package io.netty.channel;
import io.netty.util.concurrent.OrderedEventExecutor;
/**
* 注册后将处理 Channel 的所有 I/O 操作。
*
* 一个 EventLoop 实例通常会处理多个 Channel,但这可能取决于实现细节和内部结构。
*/
public interface EventLoop extends OrderedEventExecutor, EventLoopGroup {
@Override
EventLoopGroup parent();
}
Its inheritance relationship is more complicated:
- One is inherited from
j.u.c.ScheduleExecutorServer
and therefore contains all methods in the thread pool - One is inherited from Netty's own
OrderedEventExecutor
- Provides
boolean inEventLoop(Thread thread);
a method to determine whether a thread belongs to this EventLoop - Provides
EventLoopGroup parent();
a method to see which EventLoop you belong to
- Provides
package io.netty.channel;
import io.netty.util.concurrent.EventExecutorGroup;
/**
* 特殊的 EventExecutorGroup 允许注册在事件循环期间处理以供以后选择的 Channel。
*/
public interface EventLoopGroup extends EventExecutorGroup {
/**
* 返回下一个要使用的 EventLoop
*/
@Override
EventLoop next();
/**
* 用这个 EventLoop 注册一个 Channel。
* 注册完成后,返回的 ChannelFuture 将收到通知。
*/
ChannelFuture register(Channel channel);
/**
* 使用 thisEventLoop 注册一个 Channel。 注册完成后,传递的 ChannelFuture 将收到通知,并且也会返回。
*/
ChannelFuture register(Channel channel, ChannelPromise promise);
}
EventLoopGroup is a group of EventLoops. Channel generally calls the EventLoopGroup register
method to bind one of the EventLoops, and subsequent IO events on this Channel are handled by this EventLoop (to ensure thread safety during IO event processing)
- Inherit Netty's own EventExecutorGroup
- Implemented
iterator
the interface to provide the ability to traverse EventLoop - Implemented
next
the method to get the next EventLoop in the collection
- Implemented
Code understanding implementation:
public class TestEventLoop {
public static void main(String[] args) {
// 1、创建时间循环组
EventLoopGroup group = new NioEventLoopGroup(2); // 处理IO事件、提交普通、定时任务
/** new NioEventLoopGroup() 默认构造的线程数
* Runtime.getRuntime().availableProcessors() 电脑 CPU 的核心数
* static {
* DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
* "io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors() * 2));
* if (logger.isDebugEnabled()) {
* logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
* }
* }
*/
// 2、获取下一个事件循环对象
System.out.println(group.next()); // io.netty.channel.nio.NioEventLoop@6f2b958e
System.out.println(group.next()); // io.netty.channel.nio.NioEventLoop@1eb44e46
System.out.println(group.next()); // io.netty.channel.nio.NioEventLoop@6f2b958e (循环回第一个)
// 3、执行普通任务
// group.next().submit
group.next().execute(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("[" + Thread.currentThread().getName() + "] ok");
});
System.out.println("[" + Thread.currentThread().getName() + "] main");
// 4、执行定时任务
// 固定频率执行任务 参数1: 任务对象,参数2:初始延时事件,参数3:间隔时间,参数4:时间单位
group.next().scheduleAtFixedRate(()->{
System.out.println("[" + Thread.currentThread().getName() + "] scheduleAtFixedRate");
}, 0, 1, TimeUnit.SECONDS);
}
}
1) EventLoop - IO task
Server code:
public class EventLoopServer {
public static void main(String[] args) {
new ServerBootstrap()
.group(new NioEventLoopGroup())
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
@Override // msg ByteBuf
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("[" + Thread.currentThread().getName() +"] "+ buf.toString(Charset.defaultCharset()));
}
});
}
})
.bind(8888);
}
}
Client code:
public class EventLoopClient {
public static void main(String[] args) throws InterruptedException {
// 1、客户端启动器
Channel channel = new Bootstrap()
// 2、添加 EventLoop
.group(new NioEventLoopGroup())
// 3、选择客户端 Channel 事件
.channel(NioSocketChannel.class)
// 4、添加处理器,
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override // 连接建立后被调用
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new StringEncoder());
}
})
// 5、连接到服务器
.connect(new InetSocketAddress("localhost", 8888))
.sync()
.channel();
System.out.println(channel);
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()){
String msg = scanner.nextLine();
channel.writeAndFlush(msg);
}
}
}
run test
It can be seen that two workers (EventLoop) process the Channel in turn, but the worker (EventLoop) is bound to the Channel
Once the connection is established, the Channel will be bound to a NioEventLoopGroup, and subsequent requests will be processed by the same EventLoop.
2) EventLoop - division of labor and refinement
Refinement of labor 1: As in the previous example, there are two roles: boss (only responsible for accept events on ServerSocketChannel) and worker (only responsible for read and write events on SocketChannel)
new ServerBootstrap()
// 参数1: boss(只负责 ServerSocketChannel 上 accept 事件) 参数2: worker(只负责 SocketChannel 上的读写事件)
// 只会占用 参数1(boss)里面的一个线程, worker 初始化两个线程(只有两个worker)一个 worker 可以管理多个 Channel
.group(new NioEventLoopGroup(), new NioEventLoopGroup(2))
Division of labor and refinement 2: Handle time-consuming handlers separately
// 细化2:创建一个独立的 EventLoopGroup
EventLoopGroup group = new DefaultEventLoopGroup();
...
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast("handler1", new ChannelInboundHandlerAdapter(){
@Override // msg ByteBuf
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("[" + Thread.currentThread().getName() +"] "+ buf.toString(Charset.defaultCharset()));
ctx.fireChannelRead(msg); // 将消息传递给下一个 handler
}
// 使用 group
}).addLast(group, "handler2", new ChannelInboundHandlerAdapter(){
@Override // msg ByteBuf
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("[" + Thread.currentThread().getName() +"] "+ buf.toString(Charset.defaultCharset()));
}
});
...
How to replace the handler during execution?
key codeio.netty.channel.AbstractChannelHandlerContext#invokeChannelRead()
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
// 下一个 handler 的事件循环是否与当前的事件循环是同一个线程(EventLoop)
EventExecutor executor = next.executor(); // 返回下一个 handler 的 EventLoop
// 是,直接使用
if (executor.inEventLoop()) {
// 当前 handler 中的线程,是否和 EventLoop 是同一个线程(EventLoop)
next.invokeChannelRead(m);
}
// 不是,将要执行的代码作为一个任务提交给下一个事件循环处理(换人)
else {
executor.execute(new Runnable() {
/// executor 下一个 handler 线程(EventLoop)
@Override
public void run() {
next.invokeChannelRead(m);
}
});
}
}
Summary: If the two handlers are bound to the same thread (EventLoop), then call them directly; otherwise, encapsulate the code to be called into a task object, which will be called by the next handler thread (EventLoop).