zoukankan      html  css  js  c++  java
  • 高性能非阻塞 Web 服务器 Undertow

    Undertow 简介

    Undertow是一个用java编写的、灵活的、高性能的Web服务器,提供基于NIO的阻塞和非阻塞API。

    Undertow的架构是组合式的,可以通过组合各种小型的目的单一的处理程序来构建Web服务器。所以可以很灵活地的选择完整的Java EE servlet 3.1容器或初级非阻塞程序处理。

    Undertow的设计是可以完全可嵌入的,具有简单易用的编译接口。Undertow的生命周期完全由嵌入的应用程序控制。

    Undertow是JBoss赞助的一个Web服务器,是Wildfly应用程序服务器中的默认Web服务器。

    Undertow的特点:

    • 非常轻量级,Undertow核心瓶子在1Mb以下。它在运行时也是轻量级的,有一个简单的嵌入式服务器使用少于4Mb的堆空间。
    • 支持HTTP升级,允许多个协议通过HTTP端口进行多路复用。
    • 提供对Web套接字的全面支持,包括JSR-356支持。
    • 提供对Servlet 3.1的支持,包括对嵌入式servlet的支持。还可以在同一部署中混合Servlet和本机Undertow非阻塞处理程序。
    • 可以嵌入在应用程序中或独立运行,只需几行代码。
    • 通过将处理程序链接在一起来配置Undertow服务器。它可以对各种功能进行配置,方便灵活。

    内核

    通常情况下有两种方法来引导Undertow。 第一种也是最简单的是使用API即io.undertow.Undertow。第二种方式是直接使用XNIO和Undertow侦听器类来组装服务器。第二种方法需要更多的代码,但是给出更多的灵活性。大多数情况下,通过API构建就行了。

    重点需要理解,Undertow中没有任何容器的概念。Undertow应用程序是由多个处理程序组合而来的,它通过嵌入的方式来管理所有这些处理程序的生命周期。这是一个专门的设计决定,以便给予嵌入应用更多的控制权。当然这种设计会产生一个问题,如果你有处理程序,需要在服务器停止时清理一下它使用的资源。

    代码示例,一个使用Async IO的简单Hello World服务器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class HelloWorldServer {
    public static void main(final String[] args) {
    Undertow server = Undertow.builder()
    .addHttpListener(8080, "localhost")
    .setHandler(new 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();
    }
    }

    上面的示例启动一个简单的服务器,它向所有请求返回Hello World。服务器将监听端口8080上的本地主机地址,直到调用server.stop()方法。当请求到达时,它们将由处理程序链中的第一个(也是唯一的)处理程序处理,在这种情况下,只需设置一个标题并写入一些内容。

    手动配置一个服务器

    如果不想使用构建器API,则需要执行以下几个步骤来创建服务器:

    • 创建XNIO Worker。 此工作程序管理服务器的IO和工作线程。
    • 创建XNIO SSL实例(可选,仅在使用HTTPS时需要)
    • 创建相关Undertow侦听器类的实例
    • 使用XNIO打开服务器套接字并设置其接受侦听器

    HTTP,HTTPS和AJP侦听器的代码如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    Xnio xnio = Xnio.getInstance();
    XnioWorker worker = xnio.createWorker(OptionMap.builder()
    .set(Options.WORKER_IO_THREADS, ioThreads)
    .set(Options.WORKER_TASK_CORE_THREADS, workerThreads)
    .set(Options.WORKER_TASK_MAX_THREADS, workerThreads)
    .set(Options.TCP_NODELAY, true)
    .getMap());
    OptionMap socketOptions = OptionMap.builder()
    .set(Options.WORKER_IO_THREADS, ioThreads)
    .set(Options.TCP_NODELAY, true)
    .set(Options.REUSE_ADDRESSES, true)
    .getMap();
    Pool<ByteBuffer> buffers = new ByteBufferSlicePool(BufferAllocator.DIRECT_BYTE_BUFFER_ALLOCATOR,bufferSize, bufferSize * buffersPerRegion);
    if (listener.type == ListenerType.AJP) {
    AjpOpenListener openListener = new AjpOpenListener(buffers, serverOptions, bufferSize);
    openListener.setRootHandler(rootHandler);
    ChannelListener<AcceptingChannel<StreamConnection>> acceptListener = ChannelListeners.openListenerAdapter(openListener);
    AcceptingChannel<? extends StreamConnection> server = worker.createStreamConnectionServer(new InetSocketAddress(Inet4Address.getByName(listener.host), listener.port), acceptListener, socketOptions);
    server.resumeAccepts();
    } else if (listener.type == ListenerType.HTTP) {
    HttpOpenListener openListener = new HttpOpenListener(buffers, OptionMap.builder().set(UndertowOptions.BUFFER_PIPELINED_DATA, true).addAll(serverOptions).getMap(), bufferSize);
    openListener.setRootHandler(rootHandler);
    ChannelListener<AcceptingChannel<StreamConnection>> acceptListener = ChannelListeners.openListenerAdapter(openListener);
    AcceptingChannel<? extends StreamConnection> server = worker.createStreamConnectionServer(new InetSocketAddress(Inet4Address.getByName(listener.host), listener.port), acceptListener, socketOptions);
    server.resumeAccepts();
    } else if (listener.type == ListenerType.HTTPS){
    HttpOpenListener openListener = new HttpOpenListener(buffers, OptionMap.builder().set(UndertowOptions.BUFFER_PIPELINED_DATA, true).addAll(serverOptions).getMap(), bufferSize);
    openListener.setRootHandler(rootHandler);
    ChannelListener<AcceptingChannel<StreamConnection>> acceptListener = ChannelListeners.openListenerAdapter(openListener);
    XnioSsl xnioSsl;
    if(listener.sslContext != null) {
    xnioSsl = new JsseXnioSsl(xnio, OptionMap.create(Options.USE_DIRECT_BUFFERS, true), listener.sslContext);
    } else {
    xnioSsl = xnio.getSslProvider(listener.keyManagers, listener.trustManagers, OptionMap.create(Options.USE_DIRECT_BUFFERS, true));
    }
    AcceptingChannel <SslConnection> sslServer = xnioSsl.createSslConnectionServer(worker, new InetSocketAddress(Inet4Address.getByName(listener.host), listener.port), (ChannelListener) acceptListener, socketOptions);
    sslServer.resumeAccepts();
    }

    如你所见,上述代码内容不少,而不仅仅是使用构建器,但它提供了一些灵活性:

    • 完全控制所有选项
    • 能够为每个侦听器使用不同的缓冲池和工作程序
    • XnioWorker实例可以在不同的服务器实例之间共享
    • 缓冲池可以在不同的服务器实例之间共享
    • 监听器可以给予不同的根处理程序

    在大多数情况下,这个级别的控制不是必需的,最好是简单地使用构建器API:io.undertow.Undertow。

    创建Servlet的部署

    有关如何创建Servlet部署的一个简单示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    DeploymentInfo servletBuilder = Servlets.deployment()
    .setClassLoader(ServletServer.class.getClassLoader())
    .setContextPath("/myapp")
    .setDeploymentName("test.war")
    .addServlets(
    Servlets.servlet("MessageServlet", MessageServlet.class)
    .addInitParam("message", "Hello World")
    .addMapping("/*"),
    Servlets.servlet("MyServlet", MessageServlet.class)
    .addInitParam("message", "MyServlet")
    .addMapping("/myservlet"));
    DeploymentManager manager = Servlets.defaultContainer().addDeployment(servletBuilder);
    manager.deploy();
    PathHandler path = Handlers.path(Handlers.redirect("/myapp"))
    .addPrefixPath("/myapp", manager.start());
    Undertow server = Undertow.builder()
    .addHttpListener(8080, "localhost")
    .setHandler(path)
    .build();
    server.start();

    基本过程是创建一个DeploymentInfo结构(通过使用io.undertow.servlets.Servlets中的方法),将需要的Servlet和其他信息添加到此结构,然后将其部署到Servlet容器。

    部署之后,就可以在DeploymentManager上调用start()方法,该方法返回一个HttpHandler,然后可以安装在Undertow服务器处理程序链接中。

    DeploymentInfo结构有很多数据,并且大部分数据直接对应于web.xml中的数据。

    JSP

    JSP可以通过使用Jastow项目在Undertow中使用,Jastow项目是Apache Jasper的一个分支,用于Undertow。

    Jasper通过Servlet提供了所有的功能,因此可以通过将JSP servlet添加到*.jsp映射来将其添加到标准Undertow servlet部署中。

    JSP还需要一些额外的上下文参数,Jastow提供了一个帮助类来设置它们。

    下面显示了如何设置JSP部署的示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    final PathHandler servletPath = new PathHandler();
    final ServletContainer container = ServletContainer.Factory.newInstance();
    DeploymentInfo builder = new DeploymentInfo()
    .setClassLoader(SimpleJspTestCase.class.getClassLoader())
    .setContextPath("/servletContext")
    .setClassIntrospecter(TestClassIntrospector.INSTANCE)
    .setDeploymentName("servletContext.war")
    .setResourceManager(new TestResourceLoader(SimpleJspTestCase.class))
    .addServlet(JspServletBuilder.createServlet("Default Jsp Servlet", "*.jsp"));
    JspServletBuilder.setupDeployment(builder, new HashMap<String, JspPropertyGroup>(), new HashMap<String, TagLibraryInfo>(), new MyInstanceManager());
    DeploymentManager manager = container.addDeployment(builder);
    manager.deploy();
    servletPath.addPrefixPath(builder.getContextPath(), manager.start());

    这里JSP标签是使用Jasper InstanceManager接口的实例创建的。如果你不需要注入标签,那么这个接口可以直接使用反射创建一个新的实例。

    Undertow.js

    Undertow.js是一个独立的项目,使得使用Undertow编写服务器端Javascript变得很容易。 它支持以下:

    • Java EE integration, including dependency injection
    • REST
    • Templates
    • Declarative security
    • Filters
    • Websockets
    • Hot reload
    • JDBC

    首先,您需要在应用程序中包含最新的Undertow.js。如果你使用Wildfly 10就不用了,因为Wildfly 10提供了这个功能。如果你使用maven,你可以在pom.xml中包含以下内容:

    1
    2
    3
    4
    5
    <dependency>
            <groupId> io.undertow.js </ groupId>
            <artifactId> undertow-js </ artifactId>
            <version> 1.0.0.Alpha3 </ version>
    </ dependency>

    您也可以从链接:http://mvnrepository.com/artifact/io.undertow.js/undertow-js下载jars。

    创建一个文件WEB-INF/undertow-scripts.conf。在此文件中,列出服务器端JavaScript文件,每行一个,这些文件将按指定的顺序执行。

    即使服务器JavaScript文件位于Web上下文中,也不允许直接访问他们。如果用户请求服务器端JS文件,则将返回404。

    我们现在可以创建一个简单的端点。创建一个javascript文件,将其添加到undertow-scripts.conf并添加以下内容:

    1
    2
    3
    4
    5
    6
    $ undertow
        .onGet(“/hello”,
            {headers:{“content-type”:“text/plain”}},
            [function$ exchange{
                return“Hello World”;
            }])

    访问部署中的 http://localhost:8080/hello 路径现在应该返回Hello World响应。

    更多内容可以访问官网:

    http://undertow.io/undertow-docs/undertow-docs-1.3.0/index.html

  • 相关阅读:
    学习进度条2
    构建之法阅读笔记04
    团队项目——班级派发布视频
    四则运算3
    软件工程结对作业02四则运算四
    构建之法阅读笔记01
    构建之法阅读笔记02
    学习进度条4
    学习进度条1
    返回一个二维整数数组中最大联通子数组的和
  • 原文地址:https://www.cnblogs.com/jpfss/p/11320733.html
Copyright © 2011-2022 走看看