Undertow web容器

一、Undertow 介绍

Undertow 是红帽公司开发的一款基于 NIO 的高性能 Web 嵌入式服务器。

红帽公司(RedHat)的开源产品,且是 WildFly8(JBoos) 默认的 Web 服务器.;

  • 官网API给出一句话概述Undertow:
    Undertow is a flexible performant web server written in java, providing both blocking and non-blocking API’s based on NIO.
    译文: Undertow是一个用java编写的灵活的高性能Web服务器,提供基于NIO的阻塞和非阻塞API。

  • 官网API总结特点:

     Lightweight(轻量级)
        Undertow非常轻量级,Undertow核心jar包在1Mb以下。 它在运行时也是轻量级的,有一个简单的嵌入式服务器使用少于4Mb的堆空间
    
     HTTP Upgrade Support(支持http升级)、HTTP/2 Support
        支持HTTP升级,允许多个协议通过HTTP端口进行多路复用
        支持HTTP 2.0
    
     Web Socket Support(支持WebScoket)
        Undertow提供对Web Socket的全面支持,包括JSR-356支持
    
     Servlet 4.0  
         Undertow提供对Servlet 4.0的支持,并兼容之前版本,包括对嵌入式servlet的支持。 还可以在同一部署中混合Servlet和本机Undertow非阻塞处理程序
    
     Embeddable(可嵌入的)
         Undertow可以嵌入在应用程序中或独立运行,只需几行代码
    
     Flexible(灵活性)
       Undertow框架jar包: undertow-core.jar undertow-servlet.jar
    

总结:
1、Undertow是一个Web服务器,那么它就需要具备的现代Web服务器的基本特性,比如Servlet,JSP,文件服务器,代理服务器,安全认证等。undertow目前已经实现了绝大多数功能,并且因为wildfly通过了JavaEE7 TCK认证,即通过Servlet 3.1认证的Web服务器和容器,最新版本对servlet 4.0 也支持。
2、Undertow的一个设计目的就是为了嵌入当作web服务器使用。当前,很多Java和其他语言的开源项目,都内嵌一个小型的web server,来提供服务能力,可以是输出html,也可以是输出REST方式的json文本。支持HTTP(s)协议,对于很多应用程序已能够满足需要;
3、默认情况下 Spring Cloud 使用 Tomcat 作为内嵌 Servlet 容器,可启动一个 Tomcat 的 Spring Boot 程序与一个 Undertow 的 Spring Boot 程序,通过 VisualVM 工具进行比较,可看到 Undertow 性能优于 Tomcat;
4、官网参考:http://undertow.io

二、使用undertow

2.1、依赖添加

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

注意: undertow要引入要排在后面,否则会加载失败!

2.2、在启动类,配置undewtow-server bean

@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
@EntityScan("com.sunred.framework.domain.course")//扫描实体类
@ComponentScan(basePackages={"com.sunred.api"})//扫描接口
@ComponentScan(basePackages={"com.sunred.manage_course"})
@ComponentScan(basePackages={"com.sunred.framework"})//扫描common下的所有类
public class ManageCourseApplication {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(ManageCourseApplication.class, args);
    }

    @Bean // 此处相当于将undertow server注入到spring-context 中,这里相对于tomcat设置要多此动作
    public ServletWebServerFactory servletContainer() {
        UndertowServletWebServerFactory tomcat = new UndertowServletWebServerFactory();
        return tomcat;
    }
}

2.3、对比Tomcat启动日志输出

========= undertow =========================
2019-07-23 22:37:21.926 [main] DEBUG o.s.b.w.s.f.OrderedRequestContextFilter - Initializing filter 'requestContextFilter'
2019-07-23 22:37:21.928 [main] DEBUG o.s.b.w.s.f.OrderedRequestContextFilter - Filter 'requestContextFilter' configured successfully
2019-07-23 22:37:21.928 [main] DEBUG o.s.b.w.s.f.OrderedHttpPutFormContentFilter - Initializing filter 'httpPutFormContentFilter'
2019-07-23 22:37:21.928 [main] DEBUG o.s.b.w.s.f.OrderedHttpPutFormContentFilter - Filter 'httpPutFormContentFilter' configured successfully
2019-07-23 22:37:21.928 [main] DEBUG o.s.b.w.s.f.OrderedHiddenHttpMethodFilter - Initializing filter 'hiddenHttpMethodFilter'
2019-07-23 22:37:21.928 [main] DEBUG o.s.b.w.s.f.OrderedHiddenHttpMethodFilter - Filter 'hiddenHttpMethodFilter' configured successfully
2019-07-23 22:37:21.928 [main] DEBUG o.s.b.w.s.f.OrderedCharacterEncodingFilter - Initializing filter 'characterEncodingFilter'
2019-07-23 22:37:21.928 [main] DEBUG o.s.b.w.s.f.OrderedCharacterEncodingFilter - Filter 'characterEncodingFilter' configured successfully
2019-07-23 22:37:21.992 [main] INFO  o.s.b.w.e.u.UndertowServletWebServer - Undertow started on port(s) 31200 (http) with context path ''
2019-07-23 22:37:21.993 [main] INFO  o.s.c.n.e.s.EurekaAutoServiceRegistration - Updating port to 31200
2019-07-23 22:37:21.995 [main] INFO  c.x.m.ManageCourseApplication - Started ManageCourseApplication in 9.859 seconds (JVM running for 10.944)
2019-07-23 22:37:35.612 [XNIO-2 task-1] INFO  io.undertow.servlet - Initializing Spring FrameworkServlet 'dispatcherServlet'
2019-07-23 22:37:35.613 [XNIO-2 task-1] INFO  o.s.web.servlet.DispatcherServlet - FrameworkServlet 'dispatcherServlet': initialization started
2019-07-23 22:37:35.632 [XNIO-2 task-1] INFO  o.s.web.servlet.DispatcherServlet - FrameworkServlet 'dispatcherServlet': initialization completed in 19 ms
2019-07-23 22:37:35.640 [XNIO-2 task-1] DEBUG o.s.b.w.s.f.OrderedRequestContextFilter - Bound request context to thread: HttpServletRequestImpl [ GET / ]
2019-07-23 22:37:35.668 [XNIO-2 task-1] DEBUG o.s.b.w.s.f.OrderedRequestContextFilter - Cleared thread-bound request context: HttpServletRequestImpl [ GET / ]
2019-07-23 22:37:35.803 [XNIO-2 task-2] DEBUG o.s.b.w.s.f.OrderedRequestContextFilter - Bound request context to thread: HttpServletRequestImpl [ GET /favicon.ico ]

======= tomcat =======
2019-07-23 22:45:42.156 [main] INFO  o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-31200"]
2019-07-23 22:45:42.164 [main] INFO  o.a.tomcat.util.net.NioSelectorPool - Using a shared selector for servlet write/read
2019-07-23 22:45:42.182 [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 31200 (http) with context path ''
2019-07-23 22:45:42.183 [main] INFO  o.s.c.n.e.s.EurekaAutoServiceRegistration - Updating port to 31200
2019-07-23 22:45:42.185 [main] INFO  c.x.m.ManageCourseApplication - Started ManageCourseApplication in 10.445 seconds (JVM running for 11.686)
2019-07-23 22:46:55.193 [http-nio-31200-exec-1] INFO  o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring FrameworkServlet 'dispatcherServlet'
2019-07-23 22:46:55.194 [http-nio-31200-exec-1] INFO  o.s.web.servlet.DispatcherServlet - FrameworkServlet 'dispatcherServlet': initialization started
2019-07-23 22:46:55.235 [http-nio-31200-exec-1] INFO  o.s.web.servlet.DispatcherServlet - FrameworkServlet 'dispatcherServlet': initialization completed in 41 ms
2019-07-23 22:46:55.251 [http-nio-31200-exec-1] DEBUG o.s.b.w.s.f.OrderedRequestContextFilter - Bound request context to thread: org.apache.catalina.connector.RequestFacade@44c8bbdb
2019-07-23 22:46:55.327 [http-nio-31200-exec-1] DEBUG o.s.b.w.s.f.OrderedRequestContextFilter - Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@44c8bbdb
2019-07-23 22:46:55.342 [http-nio-31200-exec-2] DEBUG o.s.b.w.s.f.OrderedRequestContextFilter - Bound request context to thread: org.apache.catalina.connector.RequestFacade@44c8bbdb
2019-07-23 22:46:55.353 [http-nio-31200-exec-2] DEBUG o.s.b.w.s.f.OrderedRequestContextFilter - Cleared thread-bound request context: org.apache.catalina.connector.RequestFacade@44c8bbdb
2019-07-23 22:46:55.362 [http-nio-31200-exec-5] DEBUG o.s.b.w.s.f.OrderedRequestContextFilter - Bound request context to thread: org.apache.catalina.connector.RequestFacade@44c8bbdb

可以看一个重要信息:undertow启动 19ms, tomcat 启动 41ms. 这也从一个面看出,undertow要轻量级很多!

2.4、对HTTP 2.0的支持

参考:https://segmentfault.com/a/1190000013644784

2.5、配置undertow

# Undertow 日志存放目录
server.undertow.accesslog.dir
# 是否启动日志
server.undertow.accesslog.enabled=false 
# 日志格式
server.undertow.accesslog.pattern=common
# 日志文件名前缀
server.undertow.accesslog.prefix=access_log
# 日志文件名后缀
server.undertow.accesslog.suffix=log
# HTTP POST请求最大的大小
server.undertow.max-http-post-size=0 
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
server.undertow.io-threads=4
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
server.undertow.worker-threads=20
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
# 每块buffer的空间大小,越小的空间被利用越充分
server.undertow.buffer-size=1024
# 每个区分配的buffer数量 , 所以pool的大小是buffer-size * buffers-per-region
server.undertow.buffers-per-region=1024
# 是否分配的直接内存
server.undertow.direct-buffers=true

三、undertow 的深入

先看一个简单的内嵌WebServer的代码例子:

public class HelloWorldServer {  
  
    public static void main(final String[] args) {  
        Undertow server = Undertow.builder()  
                .addHttpListener(8080, "localhost")  
                .setHandler(new HttpHandler() { //设置HttpHandler回调方法  
                    @Override  
                    public void handleRequest(final HttpServerExchange exchange) throws Exception {  
                        exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain");  
                        exchange.getResponseSender().send("Hello World");  
                    }  
                }).build();  
        server.start();  
    }  
}  

在undertow里,最主要的接口就是HttpHandler,和XNIO中的ChannelListener概念相似。HttpHandler也是只有一个方法handleRequest,参数是HttpServerExchange。

这个程序内嵌一个Web服务器,打开本机的8080端口接受请求,当有浏览器连上之后,就发送一个纯文本"Hello World"。

HttpServerExchange携带所有的上下文状态信息,这个类也是目前undertow里面最长代码,有2000多行。同时包含了request和response的相关信息,可以通过getRequestHeaders()/getResponseHeaders()获取对应头部信息。

Undertow类是入口点,通过builder传入参数来构建Web容器。

上面例子:是一种最简单的是使用API即io.undertow.Undertow;而另外一种,是直接使用XNIO和Undertow侦听器类来组装服务器。若使用此法需要更多的代码,但是给出更多的灵活性。
大多数情况下,通过API构建就行了。

参考:
http://blog.hubwiz.com/2016/12/01/webserver-Undertow/
https://blog.csdn.net/fayeyiwang/article/details/54729514

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页