Clark To Do The blog of Clark

Undertow 请求生命周期

之前项目是采用Spring Boot作为应用整体的框架, 其内置的应用容器是Tomcat, 后来在Spring Boot文档内发现了Undertow, 经过一段时间的理解和试用, 效率上较之前有了一定幅度的提升.

因此将Undertow对于Web端请求的生命周期总结如下.

当连接建立, XNIO会调用io.undertow.server.HttpOpenListener. 这个监听器用于创建一个全新的io.undertow.server.HttpServerConnection, 创建出的连接负责保持这个连接的状态, 进而调用io.undertow.server.HttpReadListener.

HttpReadListener负责解析请求, 并创建io.undertow.server.HttpServerExchange来存储请求状态.exchange对象同时包含请求和响应的状态.

这时候, 请求和响应的信道包装器会进行初始化, 它会负责对于请求和相应数据的编解码.

接下来, 根处理器会通过io.undertow.server.HttpHandlers#executeRootHandler被执行. 处理器间调用为链式结构, 每个处理器都可以修改exchange对象, 发送响应或者代理给其他处理器. 这个阶段会发生:

  • exchange的终止. 这会在请求和响应的信道被关闭时发生. 如果设置了content length, 那么当所有数据被写入后, 信道会被自动关闭. 也可以通过调用HttpServerExchange.endExchange()来强制关闭. 如果还没有数据被写入, 任一注册了exchange的默认的响应监听器会有机会去生成一个默认的响应, 比如: 错误页面. 一旦当前exchange终止, 结束监听器ExchangeCompletionListener就会被执行. 最后一个监听器通常会在连接上开启下一个请求的处理, 并会初始化HttpReadListener.

  • exchange通过调用HttpServerExchange.dispatch进行调度. 这个方法与startAsync()方法类似. 当调用栈返回, 调度任务(如果存在)会在提供好的执行器内执行(如果没提供执行器, 就会在XNIO worker内支线). 调度最常见的用法就是从在一个IO线程内执行(不允许阻塞操作)移动到一个可阻塞的worker线程. 这个模式类似:

public void handleRequest(final HttpServerExchange exchange) throws Exception {
    if (exchange.isInIoThread()) {
      exchange.dispatch(this);
      return;
    }
    //handler code
}
  • 读/写可以在一个请求或响应的信道上被恢复. 在内部, 这类似一次调度, 一旦调用栈返回, 相关联的信道会通过IO事件被通知. 直到调用栈返回操作才生效的原因是确保永远不会有多个线程参与到相同的exchange里.

  • 调用栈可以不返回正在被调度的exchange. 当这种情况出现, HttpServerExchange.endExchange()会被调用, 并且请求将会结束.

  • 异常可以被抛出. 如果异常一路被传播到调用栈顶, exchange会以一个500响应结束.

参见

[1] Undertow Online Docs-1.3.0

comments powered by Disqus