vertx router源码分析

543 阅读5分钟

简介

在上篇分析中,我们已经知道了verticle是如何部署的 而route就是实现了AbstractVerticle的一种

route

package cn.ctyun.moho.router.verticle;

import cn.ctyun.moho.router.config.AppConfig;
import cn.ctyun.moho.router.handler.*;
import cn.ctyun.moho.router.model.PluginType;
import cn.ctyun.moho.router.plugin.IPluginHandler;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.Comparator;
import java.util.List;

/**
 * 负责路由转发的核心Vertical
 * @author Codi
 */
@Slf4j
@Component
@Scope("prototype")
public class RouterVerticle extends AbstractVerticle {

    @Autowired
    private RouteHandler routeHandler;
    @Autowired
    private BlockerHandler blockerHandler;
    @Autowired
    private RequestHandler requestHandler;
    @Autowired
    private ResponseHandler responseHandler;
    @Autowired
    private HttpClientHandler httpClientHandler;
    @Autowired
    private List<IPluginHandler> pluginHandlers;
    @Autowired
    private ReturnHandler returnHandler;
    @Autowired
    private GlobalErrorHandler errorHandler;
    @Autowired
    private AppConfig appConfig;
    @Autowired
    private StopHandler stopHandler;
    //从之前的分析可以知道,vertx在初始化完成后,会运行注册的AbstractVerticle
    //中的start方法
    @Override
    public void start() {
        Router router = Router.router(vertx);
        //处理请求body
        //TODO 对异常body过滤
        router.route().handler(BodyHandler.create());
        //优雅关闭handler
        router.route().handler(stopHandler);
        //整理HttpRequest
        router.route().handler(requestHandler);
        //处理路由拦截
        router.route().handler(blockerHandler);
        //处理路由匹配
        router.route().handler(routeHandler);
        //处理前置插件
        initPreHandlers(router);
        //处理转发 整理response
        router.route().handler(httpClientHandler);
        //处理后置插件
        initPostHandlers(router);
        //处理返回 这里调用last方法会改变route中order的值,让后边的handler放到最后
        router.route().last().handler(returnHandler);
        //集中异常处理
        router.route().failureHandler(errorHandler);
        //启动http服务 这个才是启动route的关键
        startHttpServer(router);
    }

    private void initPreHandlers(Router router) {
        pluginHandlers.stream()
                .filter(handler -> handler.getPluginType() == PluginType.PRE)
                .sorted(Comparator.comparing(IPluginHandler::priority))
                .forEach(handler -> {
                    router.route().handler(handler);
                    log.info("Add pre plugin <" + handler.getClass().getSimpleName() + ">");
                });
        log.info("PreHandlers init complete");
    }

    private void initPostHandlers(Router router) {
        pluginHandlers.stream()
                .filter(handler -> handler.getPluginType() == PluginType.POST)
                .sorted(Comparator.comparing(IPluginHandler::priority))
                .forEach(handler -> {
                    router.route().handler(handler);
                    log.info("Add post plugin <" + handler.getClass().getSimpleName() + ">");
                });
        log.info("PostHandlers init complete");
    }

    private void startHttpServer(Router router) {
        int port= appConfig.getPort();
        HttpServerOptions options = new HttpServerOptions();
        //增加配置初始长度,规避可能存在的uri过长被服务器拒绝的问题
        options.setMaxInitialLineLength(appConfig.getMaxInitialLineLength());
        vertx.createHttpServer(options).requestHandler(router).listen(port, http -> {
            if (http.succeeded()) {
                log.info("Moho-Router HTTP server started on port " + port);
            } else {
                log.error("Moho-Router HTTP server start error!", http.cause());
            }
        });
    }

}

createHttpServer:

public HttpServer createHttpServer(HttpServerOptions serverOptions) {
    return new HttpServerImpl(this, serverOptions);
  }
  
  public HttpServerImpl(VertxInternal vertx, HttpServerOptions options) {
    this.options = new HttpServerOptions(options);
    this.vertx = vertx;
    //这个时整个route的全局context,原理和之前的vertx的context是一致的
    this.creatingContext = vertx.getContext();
    if (creatingContext != null) {
      if (creatingContext.isMultiThreadedWorkerContext()) {
        throw new IllegalStateException("Cannot use HttpServer in a multi-threaded worker verticle");
      }
      creatingContext.addCloseHook(this);
    }
    this.sslHelper = new SSLHelper(options, options.getKeyCertOptions(), options.getTrustOptions());
    this.subProtocols = options.getWebsocketSubProtocols();
    this.logEnabled = options.getLogActivity();
  }

requestHandler 将route的配置注入到vertx中:

@Override
  public synchronized HttpServer requestHandler(Handler<HttpServerRequest> handler) {
  //requeststream是创建httpserverImpl时初始化的
  //private final HttpStreamHandler<HttpServerRequest> requestStream = new HttpStreamHandler<>();
    requestStream.handler(handler);
    return this;
  }
  
   @Override
    public ReadStream handler(Handler<C> handler) {
      synchronized (HttpServerImpl.this) {
        if (listening) {
          throw new IllegalStateException("Please set handler before server is listening");
        }
        this.handler = handler;
        return this;
      }
    }

开始监听listen:

 public synchronized HttpServer listen(SocketAddress address, Handler<AsyncResult<HttpServer>> listenHandler) {
    if (requestStream.handler() == null && wsStream.handler() == null) {
      throw new IllegalStateException("Set request or websocket handler first");
    }
    if (listening) {
      throw new IllegalStateException("Already listening");
    }
    //新建一个context给这个eventloop用
    listenContext = vertx.getOrCreateContext();
    listening = true;
    String host = address.host() != null ? address.host() : "localhost";
    int port = address.port();
    serverOrigin = (options.isSsl() ? "https" : "http") + "://" + host + ":" + port;
    List<HttpVersion> applicationProtocols = options.getAlpnVersions();
    if (listenContext.isWorkerContext()) {
      applicationProtocols =  applicationProtocols.stream().filter(v -> v != HttpVersion.HTTP_2).collect(Collectors.toList());
    }
    sslHelper.setApplicationProtocols(applicationProtocols);
    //如果host和端口一样,防止同时创建多个
    synchronized (vertx.sharedHttpServers()) {
      this.actualPort = port; // Will be updated on bind for a wildcard port
      id = new ServerID(port, host);
      HttpServerImpl shared = vertx.sharedHttpServers().get(id);
      if (shared == null || port == 0) {
        serverChannelGroup = new DefaultChannelGroup("vertx-acceptor-channels", GlobalEventExecutor.INSTANCE);
        //这一步就是netty的服务端创建的方式,详细分析可以自行谷歌netty服务端实现
        ServerBootstrap bootstrap = new ServerBootstrap();
        //第一个为bosseventloopgroup第二个为workeventloopgroup
        bootstrap.group(vertx.getAcceptorEventLoopGroup(), availableWorkers);
        applyConnectionOptions(address.path() != null, bootstrap);
        sslHelper.validate(vertx);
        //childhandler为第二个workeventloopgroup的配置,只有在有请求第一次访问时,才会调用channel的initChannel方法进行初始化,所以addHandlers方法可以放在这段之后
        bootstrap.childHandler(new ChannelInitializer<Channel>() {
          @Override
            protected void initChannel(Channel ch) throws Exception {
              if (!requestStream.accept() || !wsStream.accept()) {
                ch.close();
                return;
              }
              ChannelPipeline pipeline = ch.pipeline();
              //由于分析整个启动流程,这里忽略SSL的处理
              if (sslHelper.isSSL()) {
                ch.pipeline().addFirst("handshaker", new SslHandshakeCompletionHandler(ar -> {
                  if (ar.succeeded()) {
                    if (options.isUseAlpn()) {
                      SslHandler sslHandler = pipeline.get(SslHandler.class);
                      String protocol = sslHandler.applicationProtocol();
                      if ("h2".equals(protocol)) {
                        handleHttp2(ch);
                      } else {
                        handleHttp1(ch);
                      }
                    } else {
                      handleHttp1(ch);
                    }
                  } else {
                    HandlerHolder<HttpHandlers> handler = httpHandlerMgr.chooseHandler(ch.eventLoop());
                    handler.context.executeFromIO(v -> {
                      handler.handler.exceptionHandler.handle(ar.cause());
                    });
                  }
                }));
                if (options.isSni()) {
                  SniHandler sniHandler = new SniHandler(sslHelper.serverNameMapper(vertx));
                  pipeline.addFirst(sniHandler);
                } else {
                  SslHandler handler = new SslHandler(sslHelper.createEngine(vertx));
                  pipeline.addFirst("ssl", handler);
                }
              } else {
                if (DISABLE_H2C) {
                //只分析这段,简化分析的难度
                  handleHttp1(ch);
                } else {
                  IdleStateHandler idle;
                  if (options.getIdleTimeout() > 0) {
                    pipeline.addLast("idle", idle = new IdleStateHandler(0, 0, options.getIdleTimeout(), options.getIdleTimeoutUnit()));
                  } else {
                    idle = null;
                  }
                  // Handler that detects whether the HTTP/2 connection preface or just process the request
                  // with the HTTP 1.x pipeline to support H2C with prior knowledge, i.e a client that connects
                  // and uses HTTP/2 in clear text directly without an HTTP upgrade.
                  pipeline.addLast(new Http1xOrH2CHandler() {
                    @Override
                    protected void configure(ChannelHandlerContext ctx, boolean h2c) {
                      if (idle != null) {
                        // It will be re-added but this way we don't need to pay attention to order
                        pipeline.remove(idle);
                      }
                      if (h2c) {
                        handleHttp2(ctx.channel());
                      } else {
                        handleHttp1(ch);
                      }
                    }

                    @Override
                    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                      if (evt instanceof IdleStateEvent && ((IdleStateEvent) evt).state() == IdleState.ALL_IDLE) {
                        ctx.close();
                      }
                    }

                    @Override
                    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                      super.exceptionCaught(ctx, cause);
                      HandlerHolder<HttpHandlers> handler = httpHandlerMgr.chooseHandler(ctx.channel().eventLoop());
                      handler.context.executeFromIO(v -> handler.handler.exceptionHandler.handle(cause));
                    }
                  });
                }
              }
            }
        });

        addHandlers(this, listenContext);
        try {
            //绑定netty服务
          bindFuture = AsyncResolveConnectHelper.doBind(vertx, address, bootstrap);
          bindFuture.addListener(res -> {
            if (res.failed()) {
              vertx.sharedHttpServers().remove(id);
            } else {
              Channel serverChannel = res.result();
              if (serverChannel.localAddress() instanceof InetSocketAddress) {
                HttpServerImpl.this.actualPort = ((InetSocketAddress)serverChannel.localAddress()).getPort();
              } else {
                HttpServerImpl.this.actualPort = address.port();
              }
              serverChannelGroup.add(serverChannel);
              VertxMetrics metrics = vertx.metricsSPI();
              this.metrics = metrics != null ? metrics.createHttpServerMetrics(options, address) : null;
            }
          });
        } catch (final Throwable t) {
          // Make sure we send the exception back through the handler (if any)
          if (listenHandler != null) {
            vertx.runOnContext(v -> listenHandler.handle(Future.failedFuture(t)));
          } else {
            // No handler - log so user can see failure
            log.error(t);
          }
          listening = false;
          return this;
        }
        vertx.sharedHttpServers().put(id, this);
        actualServer = this;
      } else {
        // Server already exists with that host/port - we will use that
        actualServer = shared;
        this.actualPort = shared.actualPort;
        addHandlers(actualServer, listenContext);
        VertxMetrics metrics = vertx.metricsSPI();
        this.metrics = metrics != null ? metrics.createHttpServerMetrics(options, address) : null;
      }
      actualServer.bindFuture.addListener(future -> {
        if (listenHandler != null) {
          final AsyncResult<HttpServer> res;
          if (future.succeeded()) {
            res = Future.succeededFuture(HttpServerImpl.this);
          } else {
            res = Future.failedFuture(future.cause());
            listening = false;
          }
          listenContext.runOnContext((v) -> listenHandler.handle(res));
        } else if (future.failed()) {
          listening  = false;
          // No handler - log so user can see failure
          log.error(future.cause());
        }
      });
    }
    return this;
  }

handleHttp1:

private void handleHttp1(Channel ch) {
    //handlerMGr中包含了所有的workeventloop 而它的初始化则在下边的addHandlers方法处完成
    HandlerHolder<HttpHandlers> holder = httpHandlerMgr.chooseHandler(ch.eventLoop());
    if (holder == null) {
      sendServiceUnavailable(ch);
      return;
    }
    configureHttp1(ch.pipeline(), holder);
  }
  //在channelPipline中添加vertx实现的http handler
   private void configureHttp1(ChannelPipeline pipeline, HandlerHolder<HttpHandlers> holder) {
    if (logEnabled) {
      pipeline.addLast("logging", new LoggingHandler());
    }
    if (USE_FLASH_POLICY_HANDLER) {
      pipeline.addLast("flashpolicy", new FlashPolicyHandler());
    }
    pipeline.addLast("httpDecoder", new VertxHttpRequestDecoder(options));
    pipeline.addLast("httpEncoder", new VertxHttpResponseEncoder());
    if (options.isDecompressionSupported()) {
      pipeline.addLast("inflater", new HttpContentDecompressor(false));
    }
    if (options.isCompressionSupported()) {
      pipeline.addLast("deflater", new HttpChunkContentCompressor(options.getCompressionLevel()));
    }
    if (sslHelper.isSSL() || options.isCompressionSupported()) {
      // 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()));
    }
    if (!DISABLE_H2C) {
      pipeline.addLast("h2c", new Http1xUpgradeToH2CHandler(this, httpHandlerMgr));
    }
    if (DISABLE_WEBSOCKETS) {
      // As a performance optimisation you can set a system property to disable websockets altogether which avoids
      // some casting and a header check
    } else {
      holder = new HandlerHolder<>(holder.context, new HttpHandlers(
        this,
        new WebSocketRequestHandler(metrics, holder.handler),
        holder.handler.wsHandler,
        holder.handler.connectionHandler,
        holder.handler.exceptionHandler));
      initializeWebsocketExtensions (pipeline);
    }
    HandlerHolder<HttpHandlers> holder2 = holder;
    VertxHandler<Http1xServerConnection> handler = VertxHandler.create(holder2.context, chctx -> {
      Http1xServerConnection conn = new Http1xServerConnection(holder2.context.owner(),
        sslHelper,
        options,
        chctx,
        holder2.context,
        serverOrigin,
        holder2.handler,
        metrics);
      if (metrics != null) {
        holder2.context.executeFromIO(v -> conn.metric(metrics.connected(conn.remoteAddress(), conn.remoteName())));
      }
      return conn;
    });
    handler.addHandler(conn -> {
      connectionMap.put(pipeline.channel(), conn);
    });
    handler.removeHandler(conn -> {
      connectionMap.remove(pipeline.channel());
    });
    pipeline.addLast("handler", handler);

  }

addHandlers:

private void addHandlers(HttpServerImpl server, ContextInternal context) {
    server.httpHandlerMgr.addHandler(
      new HttpHandlers(
        this,
        requestStream.handler(),
        wsStream.handler(),
        connectionHandler,
        exceptionHandler == null ? DEFAULT_EXCEPTION_HANDLER : exceptionHandler)
      , context);
  }

下面是router 由于router继承了Handler

public interface Router extends Handler<HttpServerRequest> {

所以当请求到来时,channelpipline会按顺序调用到router的handler方法:

 @Override
  public void handle(HttpServerRequest request) {
    if (log.isTraceEnabled()) log.trace("Router: " + System.identityHashCode(this) +
      " accepting request " + request.method() + " " + request.absoluteURI());
    new RoutingContextImpl(null, this, request, routes).next();
  }

next方法:

  @Override
  public void next() {
    if (!iterateNext()) {
      checkHandleNoMatch();
    }
  }

iterateNext:

 protected boolean iterateNext() {
    boolean failed = failed();
    if (currentRoute != null) { // Handle multiple handlers inside route object
      try {
        if (!failed && currentRoute.hasNextContextHandler(this)) {
          currentRouteNextHandlerIndex.incrementAndGet();
          resetMatchFailure();
          currentRoute.handleContext(this);
          return true;
        } else if (failed && currentRoute.hasNextFailureHandler(this)) {
          currentRouteNextFailureHandlerIndex.incrementAndGet();
          currentRoute.handleFailure(this);
          return true;
        }
      } catch (Throwable t) {
        handleInHandlerRuntimeFailure(currentRoute, failed, t);
        return true;
      }
    }
    while (iter.hasNext()) { // Search for more handlers
      RouteImpl route = iter.next();
      currentRouteNextHandlerIndex.set(0);
      currentRouteNextFailureHandlerIndex.set(0);
      try {
        int matchResult = route.matches(this, mountPoint(), failed);
        if (matchResult == 0) {
          if (log.isTraceEnabled()) log.trace("Route matches: " + route);
          resetMatchFailure();
          try {
            currentRoute = route;
            if (log.isTraceEnabled()) log.trace("Calling the " + (failed ? "failure" : "") + " handler");
            if (failed && currentRoute.hasNextFailureHandler(this)) {
              currentRouteNextFailureHandlerIndex.incrementAndGet();
              route.handleFailure(this);
            } else if (currentRoute.hasNextContextHandler(this)) {
              currentRouteNextHandlerIndex.incrementAndGet();
              route.handleContext(this);
            } else {
              continue;
            }
          } catch (Throwable t) {
            handleInHandlerRuntimeFailure(route, failed, t);
          }
          return true;
        } else if (matchResult != 404) {
          this.matchFailure = matchResult;
        }
      } catch (Throwable e) {
        if (log.isTraceEnabled()) log.trace("IllegalArgumentException thrown during iteration", e);
        // Failure in matches algorithm (If the exception is instanceof IllegalArgumentException probably is a QueryStringDecoder error!)
        if (!this.response().ended())
          unhandledFailure((e instanceof IllegalArgumentException) ? 400 : -1, e, route.router());
        return true;
      }
    }
    return false;
  }

handleContext:

void handleContext(RoutingContextImplBase context) {
    Handler<RoutingContext> contextHandler;

    synchronized (this) {
      contextHandler = contextHandlers.get(context.currentRouteNextHandlerIndex() - 1);
    }

    contextHandler.handle(context);
  }