我们在前面的文章分析了Vertx核心单机部分的源码。今天轮到Vert.x-Web,由于Web的内容比较多,因此分为上下两部分。
- 上:分析Vert.x-Web核心类及其主要能力。
- 下:分析HttpServer的线程调度以及Web OpenAPI的工作原理
一个基本的VertxWeb代码片段如下:
1 | fun main() { |
上面创建了一个Http服务,暴露8080端口,并注册了一个path为hello,方法为GET的接口。上面涉及到Vert.x-Web的类有:Router、Route、RoutingContext、HttpServer。我们介绍前三个。
Router
Router
首先是Router接口,它主要有以下几个方面组成。
Router.router(vertx) 创建一个Router对象
routeXXX() 各种路由方法,可以根据路径、方法、媒体类型、正则表达式等各种方式路由,返回一个Route对象,用于指定处理器。
getRoutes() 获取所有route
clear() 清除所有Route
mountSubRouter() 挂载子router
exceptionHandler() 指定一个router级别的异常处理器,当handler中抛出异常时,它会捕获。但它不会影响正常业务逻辑。
但它目前已被废弃,应该使用errorHandler(),它对应的是500的错误
errorHandler(code, handler) 当发生特定错误码时会调用它。当RoutingContext失败(fail) 或 一些handler失败但没有写响应 或 handler中抛出了异常,都会调用它。需要注意的是它的500有特殊的意义,代表通用错误。
handleContext(context) 将一个RoutingContext传进来,当一个Router被挂载到某个route时会用:将该route的RoutingContext传入本router进行处理。
handleFailure(RoutingContext) 使用场景同上,处理失败
modifiedHandler(handler) 当本Router的Route发生变化时该方法会被触发。
首先说,Router只是Handler的子类,因此本质上还只是一个处理器,是被动调用的。
1 |
|
RouterImpl
众多route生成方法的实现都大同小异,都是创建RouteImpl
1 | public synchronized Route route(String path) { |
另外一个重点是handle,既然Router是Handler的子类,因此本类最初被调用的一定是handle方法(在请求进来时调用)
1 |
|
啥也没干,就创建了一个RoutingContextImpl,并调用了next(),开启处理逻辑。
RouterState
一个RouterImpl包含一个RouterState,在初始化时创建,用于管理Router的状态
1 | public RouterImpl(Vertx vertx) { |
共有如下几种状态
- Route集合:Router内注册的Route全在这里
- errorHandler映射:失败的处理器,其中key对应的错误码,如400
- modifiedHandler:用于在Router发生变化时做出响应的处理器
1 | private final Set<RouteImpl> routes; |
Route
Route
一个接口,对路由信息的描述,包含如下几种信息,见名思义。
- method
- path
- regex
- consumes
- produces
同时包含了对匹配到的请求的处理方式
- handler():为Route添加一个处理器,多个handler之间会连缀调用。
- failureHandler():为Route添加一个失败处理器,多个handler之间会连缀调用。
- blockingHandler():为Route添加一个能够执行阻塞操作的处理器。
- subRouter():为Route添加一个子Router,符合要求的请求都会被转发给该Router。
RouteImpl
就是对上面各个方法的实现,这里不赘述。
RouteState
对一个路由状态的持有,相对RouterState而言,它持有的状态复杂得多。
1 | // 路径 |
对Route最重要的方法当然是判断是否匹配,该方法也是在RouteState中给出
io.vertx.ext.web.impl.RouteState#matches,逻辑比较复杂,有兴趣可以去看看
RoutingContext
RoutingContext是Router上下文的抽象,代表一个请求从进入到响应全阶段的状态。由于在Router.handle()中,直接创建了RoutingContextImpl对象,并调用了RoutingContext.next(),因此我们重点关注它。
1 | // RoutingContext的实现类RoutingContextImpl的构造方法 |
仔细看iterateNext()
1 | boolean iterateNext() { |
再来仔细看
1 | private void checkHandleNoMatch() { |
总结
到现在,抛开Router是被谁调用这问题。我们知道整个RoutingContext的执行起始是Router的handle方法:它创建RoutingContextImpl对象,并调用next()启动route的匹配操作,并在匹配后调用对应route的正常handler链或失败handler链。
这里掌握三个关键点
- RoutingContext对象是在Router的handle中创建的,并在匹配到的route的handler中流转。
- 遍历所有route的过程会在每次请求进来被处理。逻辑在iterateNext()方法的下半部分。
- route的handler链调用比较特殊,需要开发者手动调用RoutingContext的next()方法处理。逻辑在iterateNext()方法的上半部分。
- 在从Router的handle开始,都是在当前线程不阻塞地执行,可以一镜到底。
对于Web的整个处理链,我们弄得差不多了。现在还存在如下问题,我们留待下篇再来看。
- 谁构建HttpServerRequest并调用了Router的handler?
- 调用Router时线程分配是如何的?