本系列代码地址:https://github.com/HashZhang/…
在咱们的我的项目中,咱们没有采纳默认的 Tomcat 容器,而是应用了 UnderTow 作为咱们的容器。其实性能上的差别并没有那么显著 ,然而应用 UnderTow 咱们能够利用间接内存作为网络传输的 buffer,缩小业务的 GC,优化业务的体现。其实 Tomcat 也有应用间接内存作为网络传输的 buffer 的配置,即 Connector 应用 NIO 或者 NIO2,还有 APR 这种基于 JNI 的优化文件与申请传输的形式,然而 tomcat 随着一直迭代与倒退,性能越来越欠缺以及组件化的同时,架构也越来越简单,这也带来了代码设计与品质上的一些升高。比照 Tomcat Connector 那里的源代码与设计,我最终抉择了更为 轻量设计的 Undertow。至于不选 Jetty 的起因和 Tomcat 相似,不选 reactor-netty 的次要起因是 我的项目还是比拟新并且不太成熟,并且基于异步回调,很多时候异样解决不全面,导致最初诡异的响应并且异样定位老本比拟高。
Undertow 的官网:https://undertow.io/
- 红帽开源产品,目前是 WildFly(JBoss AS)的默认 Web 容器。
- 在不须要非常复杂的配置状况下,能够展现出很高的性能以及稳定性。
- 十分轻量,只需通过 API 即可疾速搭建 Web 服务器。
- 底层基于 XNIO,和 Netty 设计相似,应用 NIO 作为网络交互的形式,并且应用间接内存作为网络传输的 buffer,缩小业务的 GC。
- 因为基于这种异步框架,所以配置也是交由链式 Handler 配置和解决申请,能够最小化按需加载模块,毋庸加载多余性能
- 反对 Servlet 4.0 以及向下兼容,反对 WebSocket
然而,Undertow 有一些 令人担忧 的中央:
- 官网一股贫困的气味,背靠红帽这个不太靠谱的爹
- NIO 框架采纳的是 XNIO,在官网 3.0 roadmap 申明中提到了将会在 3.0 版本开始,从 XNIO 迁徙到 netty,参考:Undertow 3.0 Announcement。然而,目前曾经过了快两年了,3.0 还是没有公布,并且 github 上 3.0 的分支曾经一年多没有更新了。目前,还是在用 2.x 版本的 Undertow。不晓得是 3.0 目前没必要开发,还是胎死腹中了呢?目前国内的环境对于 netty 应用更加宽泛并且大部分人对于 netty 更加相熟一些,XNIO 利用并不是很多。不过,XNIO 的设计与 netty 大同小异。
- 官网文档的更新比较慢,可能会慢 1~2 个小版本 ,导致 Spring Boot 粘合 Undertow 的时候,配置显得不会那么优雅。 参考官网文档的同时,最好还是看一下源码,至多看一下配置类,能力搞懂到底是怎么设置的。
- 认真看 Undertow 的源码,会发现有很多防御性编程的设计或者功能性设计 Undertow 的作者想到了,然而就是没实现,有很多没有实现的半成品代码。这也令人担心 Underow 是否开发能源有余,哪一天会忽然死掉?
不过,幸好有 spring-boot,在 spring-boot 我的项目中,切换容器的老本不大,批改依赖即可。同时要留神,不同 web 容器的配置。
Undertow 目前(2.x) 还是基于 Java XNIO,Java XNIO 是一个对于 JDK NIO 类的扩大,和 netty 的基本功能是一样的,然而 netty 更像是对于 Java NIO 的封装,Java XNIO 更像是扩大封装。次要是 netty 中根本传输承载数据的并不是 Java NIO 中的 ByteBuffer
,而是本人封装的 ByteBuf
,而 Java XNIO 各个接口设计还是基于 ByteBuffer
为传输处理单元。设计上也很类似,都是 Reactor 模型的设计。其构造如下所示:
Java XNIO 次要包含如下几个概念:
- Java NIO
ByteBuffer
:Buffer
是一个具备状态的数组,用来承载数据,能够追踪记录曾经写入或者曾经读取的内容。次要属性包含:capacity(Buffer 的容量),position(下一个要读取或者写入的地位下标),limit(以后能够写入或者读取的极限地位)。程序必须通过将数据放入 Buffer,能力从 Channel 读取或者写入数据 。ByteBuffer
是更加非凡的 Buffer,它能够以间接内存调配,这样 JVM 能够间接利用这个 Bytebuffer 进行 IO 操作,省了一步复制(具体能够参考我的一篇文章:Java 堆外内存、零拷贝、间接内存以及针对于 NIO 中的 FileChannel 的思考)。也能够通过文件映射内存间接调配,即 Java MMAP(具体能够参考我的一篇文章:JDK 外围 JAVA 源码解析(5)– JAVA File MMAP 原理解析)。所以,个别的 IO 操作都是通过 ByteBuffer 进行的。 - Java NIO
Channel
:Channel 是 Java 中对于关上和某一内部实体(例如硬件设施,文件,网络连接 socket 或者能够执行 IO 操作的某些组件)连贯的形象。Channel 次要是 IO 事件源,所有写入或者读取的数据都必须通过 Channel。对于 NIO 的 Channel,会通过Selector
来告诉事件的就绪(例如读就绪和写就绪),之后通过 Buffer 进行读取或者写入。 -
XNIO
Worker
: Worker 是 Java XNIO 框架中的根本网络处理单元,一个 Worker 蕴含两个不同的线程池类型,别离是:-
IO 线程池 ,次要调用
Selector.start()
解决对应事件的各种回调,原则上不能解决任何阻塞的工作,因为这样会导致其余连贯无奈解决。IO 线程池包含两种线程(在 XNIO 框架中,通过设置 WORKER_IO_THREADS 来设置这个线程池大小,默认是一个 CPU 一个 IO 线程):- 读线程:解决读事件的回调
- 写线程:解决写事件的回调
- Worker 线程池,解决阻塞的工作,在 Web 服务器的设计中,个别将调用 servlet 工作放到这个线程池执行(在 XNIO 框架中,通过设置 WORKER_TASK_CORE_THREADS 来设置这个线程池大小)
-
- XNIO
ChannelListener
:ChannelListener 是用来监听解决 Channel 事件的形象,包含:channel readable
,channel writable
,channel opened
,channel closed
,channel bound
,channel unbound
Undertow 是基于 XNIO 的 Web 服务容器。在 XNIO 的根底上,减少:
- Undertow
BufferPool
: 如果每次须要 ByteBuffer 的时候都去申请,对于堆内存的 ByteBuffer 须要走 JVM 内存调配流程(TLAB -> 堆),对于间接内存则须要走零碎调用,这样效率是很低下的。所以,个别都会引入内存池。在这里就是BufferPool
。目前,UnderTow 中只有一种DefaultByteBufferPool
,其余的实现目前没有用。这个 DefaultByteBufferPool 绝对于 netty 的 ByteBufArena 来说,非常简单,相似于 JVM TLAB 的机制(能够参考我的另一系列:全网最硬核 JVM TLAB 剖析),然而简化了很多。咱们只须要配置 buffer size,并开启应用间接内存即可。 - Undertow
Listener
: 默认内置有 3 种 Listener,别离是 HTTP/1.1、AJP 和 HTTP/2 别离对应的 Listener(HTTPS 通过对应的 HTTP Listner 开启 SSL 实现),负责所有申请的解析,将申请解析后包装成为HttpServerExchange
并交给后续的Handler
解决。 - Undertow
Handler
: 通过 Handler 解决响应的业务,这样组成一个残缺的 Web 服务器。
咱们这一节具体介绍了 Undertow 的构造,并且阐明了 Undertow 的长处以及让咱们比拟放心的中央,并且与其余的 Web 容器做了比照。下一节咱们将会剖析 Undertow 的具体配置。
微信搜寻“我的编程喵”关注公众号,每日一刷,轻松晋升技术,斩获各种 offer: