高性能非阻塞 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
高性能非阻塞 Web 服务器 Undertow的更多相关文章
- 150行代码搭建异步非阻塞Web框架
最近看Tornado源码给了我不少启发,心血来潮决定自己试着只用python标准库来实现一个异步非阻塞web框架.花了点时间感觉还可以,一百多行的代码已经可以撑起一个极简框架了. 一.准备工作 需要的 ...
- 200行自定义异步非阻塞Web框架
Python的Web框架中Tornado以异步非阻塞而闻名.本篇将使用200行代码完成一个微型异步非阻塞Web框架:Snow. 一.源码 本文基于非阻塞的Socket以及IO多路复用从而实现异步非阻塞 ...
- Tornado----自定义异步非阻塞Web框架:Snow
Python的Web框架中Tornado以异步非阻塞而闻名.本篇将使用200行代码完成一个微型异步非阻塞Web框架:Snow. 一.源码 本文基于非阻塞的Socket以及IO多路复用从而实现异步非阻塞 ...
- WildFly8(JBoss)默认web服务器-------Undertow
Java微服务框架之Undertow 一.Undertow简介: Undertow 是红帽公司(RedHat)的开源产品,是 WildFly8(JBoos) 默认的 Web 服务器. 官网API给出一 ...
- PHP异步非阻塞fsockopen(本地可以非阻塞请求,服务器就一直执行异步的不成功) (未解决)
index.php /** * php异步请求 * * @param $host string 主机地址 * @param $path string 路径 * @param $param array ...
- Java NIO 非阻塞Socket服务器构建
推荐阅读IBM developerWorks中NIO的入门教程,尤其是对块I/O和流I/O不太清楚的开发者. 说到socket服务器,第一反应是java.net.Socket这个类.事实上在并发和响应 ...
- 非阻塞tcp服务器与阻塞的tcp服务器对比
一般的tcp服务器(阻塞)是使用的如下 [erlang] gen_tcp传输文件原型 http://www.cnblogs.com/bluefrog/archive/2012/09/10/267904 ...
- 简易非阻塞http服务器
说明 需要理解阻塞和非阻塞的区别,特别要注意非阻塞和异步不是一个概念,这个很容易弄错.云盘里面netty的书会讲这几个方面的区别,nodejs深入浅出关于异步编程章节里面 ...
- Tornado之自定义异步非阻塞的服务器和客户端
一.自定义的异步非阻塞的客户端 #!/usr/bin/env python # -*- coding: utf8 -*- # __Author: "Skiler Hao" # da ...
随机推荐
- python环境变量配置 - CSDN博客
一.下载: 1.官网下载python3.0系列(https://www.python.org/) 2.下载后图标为: 二.安装: Window下: 1.安装路径: 默认安装路径:C:\python35 ...
- 我最恨ubuntu的自动升级内核功能
总是提示我boot分区空间不足, 怎么办, 删除原有不用的内核呗,手动来做. 1.查看当前使用内核版本号.输入 uname -a 查看.uname -a 2.删除旧内核. 切换root: su 输入命 ...
- vue 图片懒加载v-lazy
搬运自:https://blog.csdn.net/twodogya/article/details/80223331 vue v-lazy官方API:https://www.npmjs.com/pa ...
- JS规则 我与你同在(逻辑与操作符)数学中的“b大于a,b小于c”是“a<b<c”,那么在JavaScript中可以用&&表示
我与你同在(逻辑与操作符) 数学里面的"a>b",在JavaScript中还表示为a>b:数学中的"b大于a,b小于c"是"a<b& ...
- Gabor filter for image processing and computer vision
介绍 我们已经知道,傅里叶变换是一种信号处理中的有力工具,可以帮助我们将图像从空域转换到频域,并提取到空域上不易提取的特征.但是经过傅里叶变换后,图像在不同位置的频度特征往往混合在一起,但是Gabor ...
- 快速沃尔什变换(FWT) 与 快速莫比乌斯变换 与 快速沃尔什变换公式推导
后面的图片将会告诉: 如何推出FWT的公式tf 如何推出FWT的逆公式utf 用的是设系数,求系数的方法! ============================================== ...
- 用shell编写小九九乘法表程序
1.使用for循环 运行结果: 2.方法二:for循环 运行结果: 备注: 1. echo -n 的意思是不自动换行,因为在linux shell中 echo到最后一个字符时会自动换行的,所以echo ...
- wget: command not found 解决方案
wget: command not found 解决方案 wget command not found 解决方案 问题分析 解决方案 方法一yum安装wget 方法二rpm安装 问题分析 安装的是Ce ...
- JavaScript中字符串类型
字符串类型 字符串介绍 这是程序里面使用最为广泛的一-种类型.在JavaScript里面, 可以使用单引号,也可以使用双引号: 字符串这种数据类型非常霸道,它和其他数据类型相加都会被转换后才为字符串类 ...
- 19-11-2-M
最后一个当然要模自己辣. %%%Miemengsb ZJ一下: 三道题没有一道会的,唯一的20还是T2输出$n/2$得的 咝…… T1一看,只会暴力. T2一看,像是状压,但是我是$dpsb$,于是弃 ...