Vertx tcpserver启动再次探秘
我们之前已经弄明白了是如何启动一个TCP服务器了,现在要弄明白我们挂载的connectHandler和netsocket.handler是如何处理连接事件和读事件的
还记得在上篇文章写的是如何找到vertx和netty的交界点吗?我们是找到了一个listen方法内部出现了netty的Future
其在NetServerImpl中
public synchronized Future<NetServer> listen(SocketAddress localAddress) {
//省略。。。
io.netty.util.concurrent.Future<Channel> bindFuture = listen(localAddress, listenContext, new NetServerWorker(listenContext, handler, exceptionHandler));
//省略。。。
}
即我们使用connecthandler()函数绑定的链接处理器包裹在了这个NetServerWoker中
请大家记住这个NetServerWorker后面还会遇到,此时我们再深入一层,追踪到我们上次追踪到的ServerBootStrap附近
在上面代码里面的listen方法的签名为public synchronized io.netty.util.concurrent.Future<Channel> listen(SocketAddress localAddress, ContextInternal context, Handler<Channel> worker),即传入的NetServerWorker当成了最后一个参数传入
再向下追踪到一个channelBalancer
以没有mainserver的情况举例子,其余情况同理
channelBalancer = new ServerChannelLoadBalancer(vertx.getAcceptorEventLoopGroup().next());
//此时包含我们连接处理器的NetServerWorker就放到balancer中了
//这个worker就是上面那个NetServerWorker
channelBalancer.addWorker(eventLoop, worker);
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(vertx.getAcceptorEventLoopGroup(),channelBalancer.workers());
bootstrap.childHandler(channelBalancer);
此时我们切换到ServerChnannelLoadBalancer类,值得一提的是,他是Netty的ChannelInitializer的子类,所以其作为BootStrap最重要的函数就是initChannel(Channel)
protected void initChannel(Channel ch) {
Handler<Channel> handler = chooseInitializer(ch.eventLoop());
if (handler == null) {
ch.close();
} else {
//用我们传入的NetServerWorker处理channel
//这个handler从下面展示的两个方法源码可知就是我们传入的NetServerWorker
channelGroup.add(ch);
handler.handle(ch);
}
}
//根据此时的工作线程获取到handler,简化一下就是把NetServerWorker拿到了
private Handler<Channel> chooseInitializer(EventLoop worker) {
WorkerList handlers = workerMap.get(worker);
return handlers == null ? null : handlers.chooseHandler();
}
//只要知道我们那个很重要的类放到了workerMap中handlers就可以了
public synchronized void addWorker(EventLoop eventLoop, Handler<Channel> handler) {
workers.addWorker(eventLoop);
WorkerList handlers = new WorkerList();
WorkerList prev = workerMap.putIfAbsent(eventLoop, handlers);
if (prev != null) {
handlers = prev;
}
handlers.addWorker(handler);
hasHandlers = true;
}
总结一下,本质上就是在ChannelInitializer里面用NetServerWorker使用handle(Channel)这个方法处理Netty中的Channel,现在可以把注意力转向这个重要的伏笔了
然后记住其内部的一个字段,这个就是我们对netserver实例挂载的连接处理器
private final Handler<NetSocket> connectionHandler;
我们直接跳转到这个类的handle()
@Override
public void handle(Channel ch) {
if (!accept()) {
ch.close();
return;
}
if (HAProxyMessageCompletionHandler.canUseProxyProtocol(options.isUseProxyProtocol())) {
//高可用配置。。忽略
if (future.isSuccess()) {
if (idle != null) {
ch.pipeline().remove(idle);
}
//关注这个方法
configurePipeline(future.getNow());
} else {
handleException(future.cause());
}
} else {
configurePipeline(ch);
}
}
在所有的分支语句里面都存在一个相同的方法调用,那么重点就在configurePipeline中了
private void configurePipeline(Channel ch) {
if (sslHelper.isSSL()) {
if (options.isSni()) {
SniHandler sniHandler = new SniHandler(sslHelper.serverNameMapper(vertx));
//挂载sni
ch.pipeline().addLast("ssl", sniHandler);
} else {
SslHandler sslHandler = new SslHandler(sslHelper.createEngine(vertx));
sslHandler.setHandshakeTimeout(sslHelper.getSslHandshakeTimeout(), sslHelper.getSslHandshakeTimeoutUnit());
//挂载ssl
ch.pipeline().addLast("ssl", sslHandler);
}
ChannelPromise p = ch.newPromise();
ch.pipeline().addLast("handshaker", new SslHandshakeCompletionHandler(p));
p.addListener(future -> {
if (future.isSuccess()) {
//重点
connected(ch);
} else {
handleException(future.cause());
}
});
} else {
connected(ch);
}
}
观察源码,我们可知最关键的函数是connect(Channel)
private void connected(Channel ch) {
NetServerImpl.this.initChannel(ch.pipeline());
//省略
VertxHandler<NetSocketImpl> nh = VertxHandler.create(ctx -> new NetSocketImpl(context, ctx, sslHelper, metrics));
nh.addHandler(conn -> {
//省略...
context.emit(conn, connectionHandler::handle);
});
//省略
ch.pipeline().addLast("handler", nh);
}
}
再次观察为了弄清楚到底在pipeline放了哪些handler,就要找pipeline去哪里了
第一处即第一行,很简单就是各种根据NetServerOptions加入需要的handler
protected void initChannel(ChannelPipeline pipeline) {
if (options.getLogActivity()) {
pipeline.addLast("logging", new LoggingHandler());
}
if (sslHelper.isSSL()) {
// only add ChunkedWriteHandler when SSL is enabled otherwise it is not needed as FileRegion is used.
pipeline.addLast("chunkedWriter", new ChunkedWriteHandler()); // For large file / sendfile support
}
if (options.getIdleTimeout() > 0) {
pipeline.addLast("idle", new IdleStateHandler(0, 0, options.getIdleTimeout(), options.getIdleTimeoutUnit()));
}
}
第二处就是最后一行的把nh放到pipeline里面,而且我们connectHandler就是在这里面调用的,也就说关键在于VertxHandler里面,直接追到里面的addHandler()方法
而且通过检测VertxHandler的继承关系,其也是Netty channelhandler的子类
//此时泛型C的类型为NetSocketImpl
public VertxHandler<C> addHandler(Handler<C> handler) {
this.addHandler = handler;
return this;
}
//反查addHandler的使用
private void setConnection(C connection) {
//很重要
conn = connection;
if (addHandler != null) {
addHandler.handle(connection);
}
}
//再反查上面方法的调用
//这个方法是Netty中的ChannelHandler中当此handler被加入时的回调
@Override
public void handlerAdded(ChannelHandlerContext ctx) {
setConnection(connectionFactory.apply(ctx));
}
//再贴上构造函数和工程函数
public static <C extends ConnectionBase> VertxHandler<C> create(Function<ChannelHandlerContext, C> connectionFactory) {
return new VertxHandler<>(connectionFactory);
}
private VertxHandler(Function<ChannelHandlerContext, C> connectionFactory) {
this.connectionFactory = connectionFactory;
}
到这里就明白了,通过在放入pipeline之前,给VertxHandler初始化addHandler字段,当我们把它放到pipeline的时候,netsocketImpl就生成了,并且赋值到内部字段conn上面了,然后通过其内部的addHandler处理这个NetSocketImpl,使用我们之前挂载的connectionHandler处理这个NetSocketImp
从时间序列上来讲其顺序为
handlerAdded->connectionFactory(用于生成NetsocketImp)->setConnection(用于把生成的初始化conn字段)
综上其实就是在做处理读事件之前的初始化
那么到底读的时候是如何调用到我们给netSocket挂载的Handler的呢?
这之前挂载到pipeline上面的并没有用于读取msg的handler,所以肯定是VertxHandler做了读事件的处理
所以直接查看作为channelhandler的核心方法channelRead
public void channelRead(ChannelHandlerContext chctx, Object msg) {
conn.read(msg);
}
这个conn是什么?就是上面我们初始化的netsocketImpl
其read方法最终调用的是NetSocketImpl内的HandlerMessage
再有pending字段实际处理msg
public void handleMessage(Object msg) {
if (msg instanceof ByteBuf) {
msg = VertxHandler.safeBuffer((ByteBuf) msg);
}
context.emit(msg, elt -> {
if (!pending.write(elt)) {
doPause();
}
});
}
出现了一个没见过的东西pending这个就是一个字段,在构造器里面初始化的
pending = new InboundBuffer<>(context);
pending.drainHandler(v -> doResume());
pending.exceptionHandler(context::reportException);
//即我们netsocket.handler(handler)挂载的处理器又被封装了一层到这里面
pending.handler(obj -> {
if (obj == InboundBuffer.END_SENTINEL) {
Handler<Void> handler = endHandler();
if (handler != null) {
handler.handle(null);
}
} else {
//这个就是我们netsocket.handler(handler)挂载的处理器
Handler<Object> handler = messageHandler();
if (handler != null) {
handler.handle(obj);
}
}
});
@Override
public synchronized NetSocket handler(Handler<Buffer> dataHandler) {
if (dataHandler != null) {
messageHandler(new DataMessageHandler(dataHandler));
} else {
messageHandler(null);
}
return this;
}
private synchronized Handler<Object> messageHandler() {
return messageHandler;
}
@Override
public synchronized NetSocketInternal messageHandler(Handler<Object> handler) {
messageHandler = handler;
return this;
}
这下我们可以看看这个pending.write()是什么了,就是直接调用了我们挂载到netsocket上面的handler
public boolean write(E element) {
checkThread();
Handler<E> handler;
synchronized (this) {
if (demand == 0L || emitting) {
pending.add(element);
return checkWritable();
} else {
if (demand != Long.MAX_VALUE) {
--demand;
}
emitting = true;
handler = this.handler;
}
}
handleEvent(handler, element);
return emitPending();
}
private <T> void handleEvent(Handler<T> handler, T element) {
if (handler != null) {
try {
handler.handle(element);
} catch (Throwable t) {
handleException(t);
}
}
}
总结一下
一个buffer从netty层面传递给VertxHandler中的channelRead方法,再转发到netsocket中,其再找到内部的pending,这里面封装了我们传入的netsocket.handler的处理器,再由pending调用这个处理器完成我们设定的函数