本来应该要写的是eventbus的源码分析的,但是Vert.x中国用户组(群号:515203212)的小伙伴们好像对vert.x-web深入了解的需求更大一些。所以就先分析vert.x-web的实现原理吧。
分析完之后发现篇幅较长,请耐心看完。
如果哪里有写的不好的话,或者不好理解的地方,欢迎在评论区提出来,谢谢。
vert.x-web主要的功能是路由,还有常用的http相关工具(例如:BodyHandler,SessionHandler)。 本文主要围绕vert.x-web是如何实现路由来展开分析。
vert.x-web最主要的是3个类: Router, Route,RoutingContext。
本文分为以下几部分展开分析:Router的api接口和实现类RouterImpl
Route的api接口和实现类RouteImpl
RoutingContext的api接口和实现类RoutingContextImpl
路由过程
一. 先来看Router部分。
Router其实就是Route的容器, 里面装载了全部的Route。当request到来时,通过Router匹配容器里的多个Route。Router把路由的工作丢给RoutingContext做了。
Router相关的api基本都是创建Route(路由点)的。
例如,route(),get(), get(path), 还有创建正则请求路径的Route等等。
除了这些说些比较重要的。
public interface Router {
//省略若干代码... //路由的入口方法。request进来之后,匹配对应的Route void accept(HttpServerRequest request);
//在当前router中,挂载子router。 Router mountSubRouter(String mountPoint, Router subRouter);
//为当前router设置异常处理器 Router exceptionHandler(@Nullable Handler exceptionHandler);
//这两个方法是用在子router,不能直接调用。 void handleContext(RoutingContext context);
void handleFailure(RoutingContext context);
}
接着看看RouterImpl实现。
public class RouterImpl implements Router {
//省略若干代码... private final Vertx vertx;
//存储Route用的是skiplist,相比于List,时间复杂度从O(n)降低到O(lgn)。 //并且用的是线程安全的skiplist //routeComparator是排序比较器。 private final Set routes = new ConcurrentSkipListSet<>(routeComparator);
//用于route的序号,保证route在routes的顺序。 //排序前面的route的order比较小。 private final AtomicInteger orderSequence = new AtomicInteger();
//异常处理器,待会分析该异常处理器的调用处。 private Handler exceptionHandler;
public RouterImpl(Vertx vertx) {
this.vertx = vertx;
}
//随便放出点创建route的代码。此时并未将route加入routes容器中, //而是为route设置handler时才加到routes容器,稍后分析 public Route route(HttpMethod method, String path) {
return new RouteImpl(this, orderSequence.getAndIncrement(), method, path);
}
public Route route(String path) {
return new RouteImpl(this, orderSequence.getAndIncrement(), path);
}
public void accept(HttpServerRequest request) {
//... //请求委托为RoutingContext,由RoutingContext来实现路由。 //这里稍后解释,只需要记住accept方法是路由的入口方法。 new RoutingContextImpl(null, this, request, routes).next();
}
//分析挂载,所谓的挂载就是把一个有多个子Route的容器Router放到另一个Router(主router) //mountPoint,挂载点, public Router mountSubRouter(String mountPoint, Router subRouter) {
//挂载点的路径必须是确定的,不能用通配符和模式匹配 if (mountPoint.endsWith("*")) {
throw new IllegalArgumentException("Don't include * when mounting subrouter");
}
if (mountPoint.contains(":")) {
throw new IllegalArgumentException("Can't use patterns in subrouter mounts");
}
//这里就是创建route,然后request达到handler时,转交给subRouter的handleContext。 route(mountPoint + "*").handler(subRouter::handleContext)
.failureHandler(subRouter::handleFailure);
return this;
}
public void handleContext(RoutingContext ctx) {
//这里创建一个新的RoutingContext,并且把原来的routingContext包装进去, //还有子router(subRouter)的routes也传进去,然后调用next()在subRouter中路由。 new RoutingContextWrapper(getAndCheckRoutePath(ctx), ctx.request(), routes, ctx)
.next();
}
public void handleFailure(RoutingContext ctx) {
new RoutingContextWrapper(getAndCheckRoutePath(ctx), ctx.request(), routes, ctx)
.next();
}
//getAndCheckRoutePath获取挂载路径 private String getAndCheckRoutePath(RoutingContext ctx) {
//currentRoute的路径只有挂载路径,在mountSubRouter方法中可以看的出来。 Route currentRoute = ctx.currentRoute();
String path = currentRoute.getPath();
return path;
}
}
先大体看看RouterImpl实现过程,看完RoutingContext和Route再回头来看看Router。
再扯扯subRouter的作用。
如果把所有的route都放在一个Router中,当前功能点多的时候,可能就会有好几百个Route。那么请求路由到排在后面Route时,整个路由的开销可能会比较大点。
使用subRouter,可以把同个模块的功能放到一个subRouter下面。
例如: 用户添加,修改,删除,新增等等。
//示例代码Router mainRouter = Router.router(vertx);
Router userRouter = Router.router(vertx);
userRouter.get("/:userId")...
userRouter.post()...
userRouter.delete("/:userId")...
userRouter.put("/:userId")...
//挂载到主router上mainRouter .mountSubRouter("/user", userRouter);
//其他模块亦是如此, 最后就是mainRouter挂载了多个功能模块的subRouter//当前subRouter也还可以继续挂subRouter 一般没必要 二级基本能满足绝大多数情况了
二. 接下来分析Route和它的实现类RouteImpl
上面分析我们知道,Route的创建是在Router调用route或get或post等等时创建的。
route常用的方法一般就是设置一下handler。链式调用的方式
router.route().handler(handler);
相关方法也不少,主要就是设置各种http的参数。启动关闭Route(默认是启动的),还有让当前的Route在Router容器里排到最后的last()方法。
其中最重要的也是最复杂的,就是设置请求路径path。
public interface Route {
//省略若干代码... //一般不用此方法,直接在创建route的时候设置 Route path(String path);
//route的序号,一般就是用Router中的orderSequence累加生成的。 Route order(int order);
Route last();
//启用