一、基本说明
1)、只有看过Netty源码,才能说是真正掌握了Netty框架。
2)、在 io.netty.example 包下,有很多Netty源码案例,可以用来分析。
二、启动过程源码剖析
2.1 目的
1)、用源码分析的方式走一下 Netty(服务器)的启动过程,更好的理解 Netty 的整体设计和运行机制。
2)、源码需要剖析到 Netty 调用 doBind 方法,追踪到 NioServerSocketChannel 的 doBind 方法
3)、要 Debug 程序到 NioEventLoop 类的run代码,无限循环,在服务器端运行
2.2 启动类分析
public final class EchoServer {
static final boolean SSL = System.getProperty("ssl") != null;
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
public static void main(String[] args) throws Exception {
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
} else {
sslCtx = null;
}
// Configure the server.
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc()));
}
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new EchoServerHandler());
}
});
// Start the server.
ChannelFuture f = b.bind(PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
1)、在启动类 main 方法中,首先创建了关于 SSL 的配置类。
2)、在启动类 main 方法中,创建了两个 EventLoopGroup 对象:
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
- 这两个对象是整个 Netty 的核心对象,可以说,整个 Netty 的运作都依赖于它们。bossGroup 用于接收 Tcp 请求,它会将请求交给 workerGroup,workerGroup 会获取到真正的连接,然后和连接进行通信,比如读写编码解码等等。
- EventLoopGroup 是事件循环组(线程组)含有多个 EventLoop,可以注册 channel,用于在事件循环中去进行选择(和选择器相关)。
- new NioEventLoopGroup(1):这个 1 表示 bossGroup 事件组有 1 个线程,这个值可以指定,如果 new NioEventLoopGroup() 会含有默认个线程 cpu核数*2,即可以充分的利用多核的优势。
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
- 创建 EventExecutor 数组,children = new EventExecutor[nThreads]; 每个元素的类型就是 NIOEventLoop ,NIOEventLoop 实现了 EventLoop 接口 和 Executor 接口
- 在启动类 main 方法中,try 块中创建了一个 ServerBootstrap 对象,它是一个引导类,用于启动服务器和引导整个程序的初始化。它和 ServerChannel 关联,而 ServerChannel 继承了 Channel,所以它提供了一些方法供我们使用,比如:remoteAddress等。随后,变量 b(ServerBootstrap) 调用了 group 方法,将两个 group 放入到自己的字段中,用于后期引导使用。
- 然后添加了一个 channel,其中参数是一个 Class 对象,引导类将通过这个 Class 对象反射创建 ChannelFactory。然后添加了一些 TCP 的参数。【说明:Channel的创建在 bind 方法,Debug下可以找到:channel = channelFactory.newChannel();】
- 添加一个服务器专属的日志处理器
- 添加一个 SocketChannel (不是 ServerSocketChannel)的 handler
- 绑定端口并阻塞至连接成功
- 最后 main 线程阻塞等待关闭
- finally 块中的代码将在服务器关闭时优雅关闭所有资源
2.3 处理器源码
@Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ctx.write(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
1)、这是一个普通的处理器类,用于处理器客户端发送来的消息。
2.3 EventLoopGroup分析
1)、构造器方法,依次调用(CTRL+D)
public NioEventLoopGroup() {
this(0);
}
public NioEventLoopGroup(int nThreads) {
this(nThreads, (Executor) null);
}
public NioEventLoopGroup(int nThreads, Executor executor) {
this(nThreads, executor, SelectorProvider.provider());
}
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider) {
this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
}
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
final SelectStrategyFactory selectStrategyFactory) {
super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
2)、追踪到源码 抽象类 MultithreadEventExecutorGroup 的构造方法 MultithreadEventExecutorGroup 才是 NioEventLoopGroup 真正的构造方法,这里可以看成是一个模板方法,使用了设计模式的模板模式。
3)、MultithreadEventExecutorGroup
/**
* @param nThreads 使用的线程数,默认为 core*2
* @param executor 执行器,如果传入null,则采用Netty默认的线程工厂和默认的执行器 ThreadPerTaskExecutor
* @param chooserFactory 单例 new DefaultEventExecutorChooserFactory()
* @param args 在创建执行器的时候传入固定参数
*/
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
if (nThreads <= 0) {
throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
}
if (executor == null) { // 如果传入的执行器是空,则采用默认的线程工厂和默认的执行器
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
children = new EventExecutor[nThreads]; // 创建指定线程数的执行器数组
for (int i = 0; i < nThreads; i ++) { // 初始化线程数组
boolean success = false;
try {
// 创建 NioEventLoop
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
// TODO: Think about if this is a good exception type
throw new IllegalStateException("failed to create a child event loop", e);
} finally {
// 如果创建失败,优雅关闭
if (!success) {
for (int j = 0; j < i; j ++) {
children[j].shutdownGracefully();
}
for (int j = 0; j < i; j ++) {
EventExecutor e = children[j];
try {
while (!e.isTerminated()) {
e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
}
} catch (InterruptedException interrupted) {
// Let the caller handle the interruption.
Thread.currentThread().interrupt();
break;
}
}
}
}
}
chooser = chooserFactory.newChooser(children);
final FutureListener<Object> terminationListener = new FutureListener<Object>() {
@Override
public void operationComplete(Future<Object> future) throws Exception {
if (terminatedChildren.incrementAndGet() == children.length) {
terminationFuture.setSuccess(null);
}
}
};
// 为每一个单例线程池添加一个关闭监听器
for (EventExecutor e: children) {
e.terminationFuture().addListener(terminationListener);
}
Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
// 将所有的单例线程池添加到一个 HashSet 中
Collections.addAll(childrenSet, children);
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
1)、如果 executor 是 null,创建一个默认的 ThreadPerTaskExecutor,使用 Netty 默认的线程工厂
2)、根据传入的线程数(CPU*2),创建一个线程池(单例线程池)数组
3)、循环填充数组中的元素,如果异常,则关闭所有的单例线程池
4)、根据线程选择工厂创建一个 线程选择器
5)、为每一个单例线程池添加一个关闭监听器
6)、将所有的单例线程池添加到一个 HashSet 中
2.4 ServerBootstrap分析
1)、ServerBootstrap 是个空构造,但是有默认的成员变量
private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>();
private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
private volatile EventLoopGroup childGroup;
private volatile ChannelHandler childHandler;
2)、基本使用
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc()));
}
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new EchoServerHandler());
}
});
1)、链式调用:group 方法,将 bossGroup 和 workerGroup 传入,bossGroup 赋值给 parentGroup 属性,workerGroup 赋值给 childGroup 属性
2)、channel 方法传入 NioServerSocketChannel.class 对象。会根据这个 class 创建 channel 对象
3)、option 方法传入 TCP 参数,放在一个 LinkedHashMap 中
4)、handler 方法传入一个 handler ,这个 handler 只属于 ServerSocketChannel,而不是 SocketChannel
5)、childHandler 方法传入一个 handler ,这个 handler 将会在每个客户端连接的时候调用。供 SocketChannel 使用。