一、netty自带的example代码
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();
}
}
}
二、NioEventLoopGroup的初始化
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
bossGroup用于接受Tcp请求,并将请求交给workerGroup,workerGroup会获取到真正的连接,然后和连接通信,比如读写解码编码等操作。EventLoopGroup是事件循环组(线程组)含有多个EventLoop,每个EventLoop中都有个selector,可以注册channel。
创建bossGroup 时的参数1是指定的线程数。如果没有指定则线程数为cpu核数*2。
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
追着构造函数一层层点进去,这行代码是创建EventExecutor数组,数组元素个数即为线程数。每个元素类型是NIOEventLoop,NIOEventLoop实现了EventExecutor接口
children = new EventExecutor[nThreads];
下面这段是循环构造children 数组中的每一个元素
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
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) {
//这里是构造失败后的处理
}
}
}
为数组中的每个元素(NIOEventLoop)循环注册监听器
for (EventExecutor e: children) {
e.terminationFuture().addListener(terminationListener);
}
总结:
bossGroup和workerGroup都可以包含多个NIOEventLoop,默认个数是核数*2,也可以指定。NIOEventLoop实现了EventLoop接口和 Executor 接口。
三、NioEventLoopGroup初始化代码
参数说明:
@param nThreads使用的线程数,默认为 core *2 [可以追踪源码]
@param executor 执行器:如果传入null,则采用Netty默认的线程工厂和默认的执行器ThreadPerTaskExecutor
@param chooserFactory单例new DefaultEventExecutorChooserFactory()
@param argsargs在创建执行器的时候传入固定参数
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 {
// 创建 new 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);
}
//将所有的单例线程池添加到一个 HashSet 中。
Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
Collections.addAll(childrenSet, children);
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
总结:
- 如果 executor 是null,创建一个默认的ThreadPerTaskExecutor,使用Netty默认的线程工厂。
- 根据传入的线程数(CPU*2)创建一个线程池(单例线程池)数组。
- 循环填充数组中的元素。如果异常,则关闭所有的单例线程池。
- 根据线程选择工厂创建一个线程选择器。
- 为每一个单例线程池添加一个关闭监听器。
- 将所有的单例线程池添加到一个 HashSet 中。
四、ServerBootstrap 初始化
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>() {
//省略
});
ServerBootstrap的初始化代码如下
ServerBootstrap b = new ServerBootstrap();
调用的构造函数
public ServerBootstrap() { }
下面这行代码是将bossGroup和workerGroup分别赋值给ServerBootstrap的两个成员变量
b.group(bossGroup, workerGroup)
然后添加了一个 channel,其中参数一个Class对象,引导类将通过这个 Class 对象反射创建ChannelFactory。
.channel(NioServerSocketChannel.class)
这里注册的handler是给bossGroup的
.handler(new LoggingHandler(LogLevel.INFO))
这里注册的handler是给workerGroup的
.childHandler(xxx)
总结:
- 链式调用:group方法,将boss和worker传入,boss 赋值给parentGroup属性,worker 赋值给childGroup属性
- channel 方法传入NioServerSocketChannel class 对象。会根据这个 class 创建 channel 对象。
- option 方法传入 TCP 参数,放在一个LinkedHashMap中。
- handler 方法传入一个 handler 中,这个hanlder只专属于ServerSocketChannel而不是SocketChannel
- childHandler传入一个hanlder,这个handler 将会在每个客户端连接的时候调用。供SocketChannel使用
五、绑定端口分析
示例程序:
ChannelFuture f = b.bind(PORT).sync();
点击进去
public ChannelFuture bind(int inetPort) {
return bind(new InetSocketAddress(inetPort));
}
再点击进去
public ChannelFuture bind(SocketAddress localAddress) {
validate();
if (localAddress == null) {
throw new NullPointerException("localAddress");
}
return doBind(localAddress);
}
创建了一个端口对象,并做了一些空判断,核心代码doBind。
initAndRegister方法
1、NioServerSocketChannel的创建
channel = channelFactory.newChannel();
(1) 通过 NIO 的SelectorProvider的openServerSocketChannel方法得到JDK 的 channel。目的是让Netty包装 JDK 的 channel。
(2) 创建了一个唯一的ChannelId,创建了一个NioMessageUnsafe,用于操作消息,创建了一个DefaultChannelPipeline管道,是个双向链表结构,用于过滤所有的进出的消息。
(3) 创建了一个NioServerSocketChannelConfig对象,用于对外展示一些配置。
总结:
channelFactory.newChannel() 方法的作用通过ServerBootstrap的通道工厂反射创建一个NioServerSocketChannel。
2、init方法对channel的初始化操作
init(channel)
(1) init方法,这是个抽象方法(AbstractBootstrap类的),由ServerBootstrap实现(可以追一下源码 //setChannelOptions(channel, options, logger);)。
(2) 设置NioServerSocketChannel的 TCP 属性。
(3) 由于LinkedHashMap是非线程安全的,使用同步进行处理。
(4) 对NioServerSocketChannel的ChannelPipeline添加ChannelInitializer处理器。
(5) 可以看出,init的方法的核心作用在和ChannelPipeline相关。
(6) 从NioServerSocketChannel的初始化过程中,我们知道,pipeline 是一个双向链表,并且,他本身就初始化了 head 和 tail,如果调用了他的addLast方法,也就是将整个 handler 插入到 tail 的前面,因为 tail 永远会在后面,需要做一些系统的固定工作。
handler的addLast方法
p.addLast(new LoggingHandler(LogLevel.INFO));
追入addLast源码可发现如下代码
@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
final AbstractChannelHandlerContext newCtx;
synchronized (this) {
// 检查该 handler 是否符合标准
checkMultiplicity(handler);
newCtx = newContext(group, filterName(name, handler), handler);
addLast0(newCtx);
/*
省略
*/
}
调用addLast0方法把AbstractChannelHandlerContext 对象放入双向链表,把新节点放到tail节点的前一个节点
private void addLast0(AbstractChannelHandlerContext newCtx) {
AbstractChannelHandlerContext prev = tail.prev;
newCtx.prev = prev;
newCtx.next = tail;
prev.next = newCtx;
tail.prev = newCtx;
}
总结:
addLast方法在DefaultChannelPipeline类中, 创建一个AbstractChannelHandlerContext对象,ChannelHandlerContext对象是ChannelHandler和ChannelPipeline之间的关联,每当有ChannelHandler添加到 Pipeline 中时,都会创建 Context。Context 的主要功能是管理他所关联的 Handler 和同一个 Pipeline 中的其他 Handler 之间的交互。 将 Context 添加到链表中。也就是追加到 tail 节点的前面。