Undertow是一个Web服务器,那么它就需要具备的现代Web服务器的基本特性,比如Servlet,JSP,文件服务器,代理服务器,安全认证等。undertow目前已经实现了绝大多数功能,并且因为wildfly通过了JavaEE7 TCK认证,所以可以说Undertow是一个通过Servlet 3.1认证的Web服务器和容器。这篇文章只分析Undertow的主干流程上的主要功能,即undertow-core和undertow-servlet。
在第一篇里介绍过,Undertow的一个设计目的就是为了嵌入当作web服务器使用。当前,很多Java和其他语言的开源项目,都内嵌一个小型的web server,来提供服务能力,可以是输出html,也可以是输出REST方式的json文本。支持HTTP(s)协议,对于很多应用程序已能够满足需要,所以很多框架如Netty支持到HTTP协议这一层,Undertow-core也是这样。
Servlet作为JavaEE重要规范,是目前Java端开发Web应用的首选技术,Undertow-servlet就是一个完备的Servlet容器。本文假设读者已经熟悉Servlet规范知识,并且对Websocket有所了解。
先看一个简单的内嵌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(); } }
和上篇XNIO相似,依然用了回调方式。在undertow里,最主要的接口就是HttpHandler,和XNIO中的ChannelListener概念相似。HttpHandler也是只有一个方法handleRequest,参数是HttpServerExchange。
这个程序内嵌一个Web服务器,打开本机的8080端口接受请求,当有浏览器连上之后,就发送一个纯文本"Hello World"。
HttpServerExchange携带所有的上下文状态信息,这个类也是目前undertow里面最长代码,有2000多行。同时包含了request和response的相关信息,可以通过getRequestHeaders()/getResponseHeaders()获取对应头部信息。
Undertow类是入口点,通过builder传入参数来构建Web容器。
再看一个稍微复杂些的构建例子:
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); 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();
看到上一篇的Xnio知识全部用上。这是Undertow类入口方法构造的过程,参数全部通过OptionMap构造并传递给XnioWorker。构建一个缓冲池Pool<ByteBuffer>,用来分配服务器接收信息缓冲区。
有两个Listener:
HttpOpenListener继承于ChannelListener,作用是当有连接连入时打开端口。rootHandler是一个HttpHandler,也就是上面提过最重要的接口。
acceptListener用来侦听端口,并把openListener引用传入。
然后就可以通过AcceptingChannel来启动Web服务器了。当连接请求到来,首先acceptListener的handleEvent方法被调用:
public void handleEvent(final AcceptingChannel<C> channel) { try { final C accepted = channel.accept(); if (accepted != null) { invokeChannelListener(accepted, openListener); } } catch (IOException e) { } } public static <T extends Channel> boolean invokeChannelListener(T channel, ChannelListener<? super T> channelListener) { if (channelListener != null) try { // 进入openListener handleEvent方法 channelListener.handleEvent(channel); } catch (Throwable t) { return false; } return true; }
接下来进入HttpOpenListener的handleEvent方法被调用:
private final Pool<ByteBuffer> bufferPool; private final int bufferSize; private volatile HttpHandler rootHandler; private volatile OptionMap undertowOptions; private volatile HttpRequestParser parser; public void handleEvent(final StreamConnection channel) { HttpServerConnection connection = new HttpServerConnection(channel, bufferPool, rootHandler, undertowOptions, bufferSize); HttpReadListener readListener = new HttpReadListener(connection, parser); connection.setReadListener(readListener); readListener.newRequest(); channel.getSourceChannel().setReadListener(readListener); readListener.handleEvent(channel.getSourceChannel()); }
这里对传入的HttpHandler进行层层封装,HttpServerConnection是一个Http连接的概念抽象,而HttpReadListener则进一步包含了HttpRequestParser,用来解析HTTP请求内容。在readListener.newRequest()方法执行时,创建了上下文信息类HttpServerExchange。这些就绪后,进一步调用HttpReadListener.handleEvent。
处理请求数据的代码在HttpReadListener.handleEventWithNoRunningRequest方法中,整个流程很清晰,就是从缓冲池中拿到可用缓存区,从channel中读取信息,用parser进行解析,并存放到HttpServerExchange中,最后调度给最开始的HttpHandler。
public static void executeRootHandler(final HttpHandler handler, final HttpServerExchange exchange) { try { exchange.setInCall(true); handler.handleRequest(exchange); //调用最初传入的HttpHandler exchange.setInCall(false); boolean resumed = exchange.runResumeReadWrite(); if (exchange.isDispatched()) { if(resumed) { throw new RuntimeException("resumed and dispatched"); } final Runnable dispatchTask = exchange.getDispatchTask(); Executor executor = exchange.getDispatchExecutor(); exchange.unDispatch(); if (dispatchTask != null) { executor = executor == null ? exchange.getConnection().getWorker() : executor; executor.execute(dispatchTask); } } else if(!resumed) { exchange.endExchange(); } } catch (Throwable t) { exchange.setInCall(false); if (!exchange.isResponseStarted()) { exchange.setResponseCode(500); } UndertowLogger.REQUEST_LOGGER.errorf(t, "Blocking request failed %s", exchange); exchange.endExchange(); } }
我们留意一下isDispatched分支是性能保障的关键,当这个调用可能阻塞时,则从线程池从获取可用线程,调配它来执行。这样就实现了异步操作,结果会放回exchange中。使用HttpServerExchange.dispatch()方法会把执行从IO线程转移到工作线程。另外需要注意exchange不是线程安全的。
如何装配一个支持Servlet的Web容器:
DeploymentInfo servletBuilder = deployment() .setClassLoader(ServletServer.class.getClassLoader()) .setContextPath(MYAPP) .setDeploymentName("test.war") .addServlets( servlet("MessageServlet", MessageServlet.class) .addInitParam("message", "Hello World") .addMapping("/*"), servlet("MyServlet", MessageServlet.class) .addInitParam("message", "MyServlet") .addMapping("/myservlet")); DeploymentManager manager = defaultContainer().addDeployment(servletBuilder); manager.deploy(); HttpHandler servletHandler = manager.start(); PathHandler path = Handlers.path(Handlers.redirect(MYAPP)) .addPrefixPath(MYAPP, servletHandler); Undertow server = Undertow.builder() .addListener(8080, "localhost") .setHandler(path) .build(); server.start();
Servlet元信息被保存到DeploymentInfo之中,这里的关键类是实现了DeploymentManager接口的DeploymentManagerImpl,在deploy方法中,将Servlet的各种信息,包括servlet,servletContext,listener,filter等都设置或者封装在对应的Handler之中。然后将其中的HttpHandler传入Undertow入口类中,完成了Servlet容器的构造。
Undertow设计之处就充分考虑了对于Websocket的支持,通过HTTP的Upgrage协议,返回101“继续”指令,Web服务器告诉浏览器要切换协议类型,随后进行Websocket的握手协商,进而完全在原有HTTP连接上同Websocket协议通信。Wildfly对于远程EJB调用,也是通过upgrade方式实现的。
目前undertow支持三种连接器:HTTP, HTTPS, 和AJP(apache httpd通信协议),预计很快会加入对SPDY的支持。
在io.undertow.server.handlers包中,有各种功能的Handler来完成不同的工作,Netty的Handler和Codec作用类似。
对于Undertow分析,还有更多的内容,比如Session, Security, Proxy等等,会逐步展开,学习其设计思路。
相关推荐
undertow-jaxrs-cdi 带有 Undertow 的独立服务器 - JAX-RS - CDI 例子 跑步: de.mustnotbenamed.quickstart.undertowserver.example.Main 在浏览器中打开或
总结来说,"Undertow-parser-generator-1.0.0.Beta18.zip"提供的开源项目不仅涉及到了高效的HTTP服务器组件Undertow,还涉及到自动化测试领域,特别是如何使用Java在Robot Framework中构建测试库。这个压缩包对于...
【标题】"undertow-core-1.0.0.Beta30.zip" 提供的是Undertow核心库的一个早期版本。Undertow是JBoss(现在是Red Hat)开发的一款高性能、轻量级的Java web服务器和Servlet容器。这个Beta30版本包含了Undertow的主要...
Undertow Parser Generator是Undertow的一部分,Undertow是一个轻量级、高性能的Java Web服务器,用于构建低延迟、高吞吐量的网络应用。Parser Generator主要负责生成HTTP解析器,这部分工作至关重要,因为它直接...
Spring Boot实现Undertow服务器同时支持HTTP2、HTTPS的方法 在本文中,我们将讨论如何让Spring Boot应用程序同时支持HTTP和HTTPS两种协议。首先,我们需要了解什么是HTTP2和HTTPS,以及它们的优点和缺点,然后我们...
undertow-jaxrs-测试展示如何运行 JAX-RS 应用程序的小示例项目,该应用程序嵌入 Undertow 中,在main()方法中启动。入门只需构建项目: mvn clean install并启动服务器: java -jar target/server-test-1.0-...
【标题】"Undertow-examples-1.0.0.Alpha2.zip" 是一个与 Undertow 相关的开源项目示例代码包,版本为1.0.0.Alpha2。Undertow 是一个轻量级、高性能的Java Web服务器和Servlet容器,由JBoss(Red Hat)开发并维护。...
Undertow是一个高性能、轻量级的Java web服务器和Servlet容器,由Red Hat开发并维护。它是JBoss AS和WildFly应用服务器的核心组件之一。在2.2.18.Final版本中,Undertow提供了最新的功能更新和性能优化。 Undertow...
【标题】"undertow-servlet-1.0.0.Beta26.zip" 提供的是Undertow Servlet容器的一个早期版本。Undertow是由JBoss(Red Hat的一部分)开发的一款高性能、轻量级的Java web服务器和Servlet容器。它支持HTTP/2,...
综上所述,"undertow-examples-1.0.0.Alpha10.zip"和"mighty-csv.zip"两个压缩包分别代表了Web服务器开发和数据处理领域的开源实践,对于Java和Scala开发者来说,这些都是宝贵的资源和学习材料。通过探索这些项目,...
Undertow-cors-filter 一个过滤器,用于在基于Underwow的服务器(Wildfly,JBOSS EAP)中正确处理CORS标头Java EE的过滤器处理似乎有所疏忽,因为当容器配置了容器管理的授权并且尚未(尚未)通过身份验证的用户尝试...
Undertow是一个基于非阻塞IO的Java Web服务器。它由几个不同的部分组成:
【标题】: Undertow-2016.11.0 【描述】: PARSEQ与Undertow Undertow是一款轻量级、高性能的Java Web服务器和Servlet容器,由JBoss(现为Red Hat的一部分)开发并维护。这款服务器以事件驱动的方式构建,特别适合...
在Spring Boot项目中,将Tomcat替换为性能更优的Undertow服务器的方法涉及到一系列配置与依赖的修改。以下是详细的知识点阐述: ### 1. 依赖管理 在Spring Boot项目中使用Maven作为构建工具时,依赖管理是通过`pom...
- JFinal-Undertow提供了作为Web服务器的实现,使用JFinal集成Undertow服务器需要添加相应的依赖。 - 添加依赖后,可以通过配置类来设置和定制Undertow服务器。 ### 注意事项 - 在某些低版本的Eclipse中,新建...
在Java中,虽然标准库提供了一些基础的WebSocket支持,但往往对于复杂或特定需求的项目,开发者会选择使用第三方库,如本例中的基于Undertow的java-websocket-client。 Undertow是一款轻量级、高性能的Java web...
一分钟的使用寿命) 通过infinispan进行的jpa 2级缓存缓存包装器(用于在服务器集群中进行缓存,以减轻mysql的负载) 弹簧控制器(自定义Web操作) 与io的异步连接器。 百里香模板。 Reactjs示例启动服务器:mvn ...
嵌入式 Lucee Undertow 服务器 Java 库提供了一个简单的接口,用于构建具有已部署 Lucee 应用程序的嵌入式 Undertow servlet 容器。 用法: import org.lucee // ... LuceeUndertowServer server = new ...