SparkRPC源码分析之TransportServer
在上一篇博客中写到了Transprotclient的初始化,也就是那个很重要的currentMap里面数据的初始化,本次分析服务端初始化过程。
先看构造方法的源代码
//创建一个TransportServer,绑定到给定主机和给定端口,(如果为0)绑定到任何可用的主机和端口。
//如果不想绑定到任何特殊主机,请将“hostToBind”设置为空
public TransportServer(
TransportContext context,
String hostToBind,
int portToBind,
RpcHandler appRpcHandler,
List<TransportServerBootstrap> bootstraps) {
this.context = context;
this.conf = context.getConf();
this.appRpcHandler = appRpcHandler;
this.bootstraps = Lists.newArrayList(Preconditions.checkNotNull(bootstraps));
boolean shouldClose = true;
try {
init(hostToBind, portToBind);
shouldClose = false;
} finally {
if (shouldClose) {
JavaUtils.closeQuietly(this);
}
}
}
可以看出构造方法主要就是给一些属性赋值和调用init方法,那么接下来看init方法的源码
private void init(String hostToBind, int portToBind) {
IOMode ioMode = IOMode.valueOf(conf.ioMode());
//根据IOmodel创建一个EventGroup
//EventGroup是EventExecutorGroup的子接口,它允许注册LinkChannel,在事件循环期间为以后的选择进行处理。
EventLoopGroup bossGroup =
NettyUtils.createEventLoop(ioMode, conf.serverThreads(), conf.getModuleName() + "-server");
EventLoopGroup workerGroup = bossGroup;
//创建池式ByteBuf分配器,但禁用thread-local缓存。对TransportClients禁用thread-local缓存,因为ByteBufs是由事件循环线程分配的,但是却由Executor线程而不是事件循环线程释放的。
//这些线程本地缓存实际上延迟了缓冲区的回收,导致更大的内存使用量。
PooledByteBufAllocator allocator = NettyUtils.createPooledByteBufAllocator(
conf.preferDirectBufs(), true /* allowCache */, conf.serverThreads());
bootstrap = new ServerBootstrap()
.group(bossGroup, workerGroup)
.channel(NettyUtils.getServerChannelClass(ioMode))
.option(ChannelOption.ALLOCATOR, allocator)
.option(ChannelOption.SO_REUSEADDR, !SystemUtils.IS_OS_WINDOWS)
.childOption(ChannelOption.ALLOCATOR, allocator);
this.metrics = new NettyMemoryMetrics(
allocator, conf.getModuleName() + "-server", conf);
if (conf.backLog() > 0) {
bootstrap.option(ChannelOption.SO_BACKLOG, conf.backLog());
}
if (conf.receiveBuf() > 0) {
bootstrap.childOption(ChannelOption.SO_RCVBUF, conf.receiveBuf());
}
if (conf.sendBuf() > 0) {
bootstrap.childOption(ChannelOption.SO_SNDBUF, conf.sendBuf());
}
//设置初始化回调函数
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
RpcHandler rpcHandler = appRpcHandler;
for (TransportServerBootstrap bootstrap : bootstraps) {
rpcHandler = bootstrap.doBootstrap(ch, rpcHandler);
}
context.initializePipeline(ch, rpcHandler);
}
});
//绑定端口
InetSocketAddress address = hostToBind == null ?
new InetSocketAddress(portToBind): new InetSocketAddress(hostToBind, portToBind);
channelFuture = bootstrap.bind(address);
//等待,直到它完成,如果失败,则重新抛出失败的原因。
channelFuture.syncUninterruptibly();
port = ((InetSocketAddress) channelFuture.channel().localAddress()).getPort();
logger.debug("Shuffle server started on port: {}", port);
}
可以看到这里又出现了IOModel,那么IOModel到底是什么呢?
public enum IOMode {
NIO, EPOLL
}
嗯,EPOLL只有在Linux主机上才能使用,并且如果EPOLL模式可用,spark会自动使用EPOLL模式,如果不可用会采用NIO的模式,这里的NIO为JAVA NIO。
这个init方法主要做了什么呢?除了看注释,你还可以看图
到这里服务端的TransportServer初始化代码已经看完了。下期见!