第 15 章 在 JBoss EAP 中配置 Web 服务器(Undertow)
本章重点介绍配置 Undertow Web 服务器,即 JBoss EAP 中嵌入的默认服务器。在这里,您将找到有关为安全通信启用 SSL/TLS 的详细信息,利用 HTTP/2 来提高性能,并微调服务器设置以与您的操作要求保持一致。
15.1. Undertow 子系统概述 复制链接链接已复制到粘贴板!
在 JBoss EAP 8.0 中,Undertow 子系统充当应用服务器中的 Web 层。它提供核心 Web 服务器和 servlet 容器功能,支持 Jakarta Servlet 6.0 规格、websocket 和 HTTP 升级等高级功能。Undertow 也可以充当具有 mod_cluster 支持的高性能反向代理,为处理 Web 流量带来更高的可扩展性、效率和灵活性。
undertow 子系统允许您配置 Web 服务器和 servlet 容器设置。它实施 Jakarta Servlet 6.0 规范 和 websocket。它还支持 HTTP 升级,并在 servlet 部署中使用高性能非阻塞处理程序。undertow 子系统也能够充当支持 mod_cluster 的高性能反向代理。
在 undertow 子系统中,需要配置五个主要组件:
- 缓冲缓存
- 服务器
- Servlet 容器
- 处理程序(handler)
- 过滤器
虽然 JBoss EAP 提供了为每个组件更新配置的功能,但默认配置适用于大多数用例,并提供合理的性能设置。
默认 undertow 子系统配置
<subsystem xmlns="{UndertowSubsystemNamespace}" default-server="default-server" default-virtual-host="default-host" default-servlet-container="default" default-security-domain="other">
<buffer-cache name="default"/>
<server name="default-server">
<http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/>
<https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/>
<host name="default-host" alias="localhost">
<location name="/" handler="welcome-content"/>
<http-invoker security-realm="ApplicationRealm"/>
</host>
</server>
<servlet-container name="default">
<jsp-config/>
<websockets/>
</servlet-container>
<handlers>
<file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
</handlers>
</subsystem>
undertow 子系统还依赖于 io 子系统来提供 XNIO worker 和 buffer 池。io 子系统是单独配置的,提供默认配置,它可在大多数情形中提供最佳性能。
15.1.1. 将 Elytron 与 undertow 子系统搭配使用 复制链接链接已复制到粘贴板!
部署 Web 应用时,将识别该应用所需的安全域的名称。这将来自部署内部,或者如果部署没有安全域,则将假定 undertow 子系统中定义的 default-security-domain。默认情况下,default-security-domain 是 ApplicationDomain。为确保从应用所需的安全域名称正确映射到适当的 Elytron 配置,可以将 application-security-domain 资源添加到 undertow 子系统。
示例:添加一个映射。
/subsystem=undertow/application-security-domain=ApplicationDomain:add(security-domain=ApplicationDomain)
如果结果为:
<subsystem xmlns="{UndertowSubsystemNamespace}" ... default-security-domain="other">
...
<application-security-domains>
<application-security-domain name="ApplicationDomain" security-domain="ApplicationDomain"/>
</application-security-domains>
...
</subsystem>
- 如果此刻部署了部署,则应用安全域映射应重新加载应用服务器才能使应用安全域映射生效。
- 在当前的 Web 服务中,为保护 Web 服务端点和 Elytron 安全域名指定的安全域的名称必须相同。
这个简单形式适合部署使用 Servlet 规范中定义的标准 HTTP 机制,如 BASIC、CLIENT_CERT、DIGEST、FORM。在这里,身份验证将针对 ApplicationDomain 安全域执行。此形式也适用于应用程序不使用任何身份验证机制,而是使用编程身份验证,或者试图获取与部署关联的 SecurityDomain,并直接使用它。
示例:映射的高级形式:
/subsystem=undertow/application-security-domain=MyAppSecurity:add(http-authentication-factory=application-http-authentication)
如果结果为:
<subsystem xmlns="{UndertowSubsystemNamespace}" ... default-security-domain="other">
...
<application-security-domains>
<application-security-domain name="MyAppSecurity" http-authentication-factory="application-http-authentication"/>
</application-security-domains>
...
</subsystem>
在这种配置形式中,而不是引用安全域,而是引用 http-authentication-factory。这是用于获取身份验证机制实例并依次与安全域关联的工厂。
在使用自定义 HTTP 身份验证机制时,您应该引用 http-authentication-factory 属性,或者必须为主体转换器、凭证工厂和机制域等机制定义额外的配置。使用 Servlet 规格中描述的机制以外的机制时,最好使用 http-authentication-factory 属性。
当使用高级映射形式时,可以使用另一个配置选项 override-deployment-config。引用的 http-authentication-factory 可以返回一组完整的身份验证机制。默认情况下,会过滤这些,使其仅与应用程序请求的机制匹配。如果此选项设为 true,则工厂提供的机制将覆盖应用请求的机制。
application-security-domain 资源还有一个额外的选项 enable-jacc。如果设为 true,则会为与这个映射匹配的任何部署启用 Java 授权合同。
15.1.1.1. 运行时信息 复制链接链接已复制到粘贴板!
如果使用了 应用程序-security-domain 映射,重复检查部署是否按预期匹配会很有用。如果资源使用 include-runtime=true 读取,与映射关联的部署也会如下所示:
/subsystem=undertow/application-security-domain=MyAppSecurity:read-resource(include-runtime=true)
{
"outcome" => "success",
"result" => {
"enable-jacc" => false,
"http-authentication-factory" => undefined,
"override-deployment-config" => false,
"referencing-deployments" => ["simple-webapp.war"],
"security-domain" => "ApplicationDomain",
"setting" => undefined
}
}
在此输出中,reference-deployments 属性显示部署 simple-webapp.war 已使用映射进行部署。
15.1.2. 配置缓冲区缓存 复制链接链接已复制到粘贴板!
此流程指导您在 JBoss EAP 中配置缓冲区缓存,这有助于缓存静态资源以提高性能。不同的部署可以使用不同的缓存大小来优化资源管理。使用的空间总量乘以每个区域的缓冲区数量乘以区域的最大数量。缓冲区缓存的默认大小为 10MB。
JBoss EAP 默认提供单一缓存。
<subsystem xmlns="{UndertowSubsystemNamespace}" default-server="default-server" default-virtual-host="default-host" default-servlet-container="default" default-security-domain="other">
<buffer-cache name="default"/>
...
</subsystem>
先决条件
- 确保已安装 JBoss EAP,并且具有管理 CLI 的管理访问权限。
流程
更新现有缓冲区缓存:
修改缓冲区大小属性:
/subsystem=undertow/buffer-cache=default/:write-attribute(name=buffer-size,value=2048)reload
创建新缓冲缓存:
添加新缓冲缓存:
/subsystem=undertow/buffer-cache=new-buffer:add
删除缓冲区缓存:
删除现有的缓冲区缓存:
/subsystem=undertow/buffer-cache=new-buffer:removereload
15.1.3. 配置字节缓冲池 复制链接链接已复制到粘贴板!
Undertow 字节缓冲池用于分配池化 NIO ByteBuffer 实例。所有监听器都有一个字节缓冲区池,您可以为每个监听程序使用不同的缓冲池和 worker。字节缓冲区池可以在不同的服务器实例之间共享。
这些缓冲区用于 IO 操作,缓冲区大小会对应用程序性能有很大影响。对于大多数服务器,理想的大小为 16k。
先决条件
- 确保已安装 JBoss EAP,并且具有管理 CLI 的管理访问权限。
流程
更新现有目标缓冲区池.
修改缓冲区大小属性:
/subsystem=undertow/byte-buffer-pool=myByteBufferPool:write-attribute(name=buffer-size,value=1024)reload
创建一个新的字节缓冲池.
添加新的字节缓冲池:
/subsystem=undertow/byte-buffer-pool=newByteBufferPool:add
删除目标缓冲池.
删除现有的字节缓冲池:
/subsystem=undertow/byte-buffer-pool=newByteBufferPool:removereload
验证
- 通过检查管理控制台中的 buffer pool 设置来验证更改。
其他资源
- 有关字节缓冲区池的详细属性,请参阅 Byte Buffer Pool Attributes 部分。
15.1.4. 了解 undertow 中的服务器配置 复制链接链接已复制到粘贴板!
服务器代表 Undertow 实例,它由几个元素组成:
-
主机 -
http-listener -
https-listener -
ajp-listener
host 元素提供虚拟主机配置,而三个监听器向 Undertow 实例提供该类型的连接。
服务器的默认行为是在服务器启动时排队请求。您可以使用主机上的 queue-requests-on-start 属性来更改此默认行为。如果此属性设为 true (默认),则到达服务器启动时的请求将保持,直到服务器就绪为止。如果此属性设为 false,则在服务器完全启动前到达的请求将使用默认响应代码拒绝。
无论属性值如何,请求处理都不会启动,直到服务器完全启动为止。
您可以使用管理控制台配置 queue-requests-on-start 属性,方法是导航到 Configuration
可以配置多个服务器,允许完全隔离部署和服务器。这在某些情况下(如多租户环境)非常有用。
JBoss EAP 默认提供服务器:
15.1.5. 默认 undertow 子系统配置 复制链接链接已复制到粘贴板!
此参考提供了 Undertow 子系统的默认配置。
<subsystem xmlns="{UndertowSubsystemNamespace}" default-server="default-server" default-virtual-host="default-host" default-servlet-container="default" default-security-domain="other">
<buffer-cache name="default"/>
<server name="default-server">
<http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/>
<https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/>
<host name="default-host" alias="localhost">
<location name="/" handler="welcome-content"/>
<http-invoker security-realm="ApplicationRealm"/>
</host>
</server>
...
</subsystem>
15.1.6. 使用管理 CLI 配置服务器 复制链接链接已复制到粘贴板!
此流程解释了如何使用管理 CLI 管理 Undertow 子系统中的服务器。您可以根据需要更新现有服务器、创建新服务器或删除服务器。
您还可以使用管理控制台配置服务器,方法是导航到 Configuration
先决条件
- 您可以访问管理 CLI。
- 有修改服务器配置的权限。
流程
更新现有服务器
/subsystem=undertow/server=default-server:write-attribute(name=default-host,value=default-host)reload创建新服务器
/subsystem=undertow/server=new-server:addreload删除服务器
/subsystem=undertow/server=new-server:removereload
15.1.7. 访问日志 复制链接链接已复制到粘贴板!
您可以在您定义的每个主机上配置访问日志记录。
有两个访问日志记录选项可用:标准访问日志记录和控制台访问日志。
请注意,访问日志记录所需的额外处理可能会影响系统性能。
15.1.7.1. 标准访问日志记录 复制链接链接已复制到粘贴板!
标准访问日志记录将日志条目写入日志文件。
默认情况下,日志文件存储在 standalone/log/access_log.log 目录中。
要启用标准访问日志记录,请将 access-log 设置添加到您要捕获访问日志数据的主机。以下 CLI 命令演示了默认 JBoss EAP 服务器中的默认主机上的配置:
/subsystem=undertow/server=default-server/host=default-host/setting=access-log:add
您必须在启用标准访问日志记录后重新载入服务器。
默认情况下,访问日志记录包括以下数据:
- 远程主机名
- 远程逻辑用户名(始终 -)
- 已验证的远程用户
- 请求的日期和时间,采用通用日志格式
- 请求的第一行
- 响应的 HTTP 状态代码
- 发送的字节数,不包括 HTTP 标头
这组数据被定义为通用模式。另外,还可使用另一个模式。除了以通用模式记录的数据外,组合模式还包括传入标头中的 referer 和 user 代理。
您可以使用 pattern 属性更改记录的数据。以下 CLI 命令演示了更新 pattern 属性以使用 combined 模式:
/subsystem=undertow/server=default-server/host=default-host/setting=access-log:write-attribute(name=pattern,value="combined"
在更新 pattern 属性后,您必须重新加载服务器。
| pattern | 描述 |
|---|---|
| %a | 远程 IP 地址 |
| %A | 本地 IP 地址 |
| %b |
发送字节数,不包括 HTTP 标头,如果没有发送任何字节 |
| %B | 发送的字节数,不包括 HTTP 标头 |
| %H | 远程主机名 |
| %H | 请求协议 |
| %l |
来自 |
| %m | 请求方法 |
| %p | 本地端口 |
| %q |
查询字符串(不包括 |
| %R | 请求的第一行 |
| %s | 响应的 HTTP 状态代码 |
| %t | 日期和时间,通用日志格式 |
| %u | 已验证的远程用户 |
| %U | 请求的 URL 路径 |
| %v | 本地服务器名称 |
| %D | 处理请求的时间(以毫秒为单位) |
| %T | 处理请求的时间(以秒为单位) |
| %I | 当前 Request thread name (稍后可以与堆栈追踪进行比较) |
| Common |
|
| 组合 |
|
您还可以从 Cookie、传入标头和响应标头或会话写入信息。语法在 Apache 语法后建模:
-
用于传入标头的
%{I,xxx} -
%{O,xxx}用于传出响应标头 -
特定 Cookie 的
%{c,xxx} -
%{R,xxx},其中xxx是ServletRequest中的属性 -
%{s,xxx},其中xxx是HttpSession中的属性
此日志提供了其他配置选项。如需更多信息,请参阅附录中的"access-log 属性"。
15.1.7.2. 控制台访问日志 复制链接链接已复制到粘贴板!
控制台访问日志记录根据 JSON 数据将数据写入 stdout 结构化。
每个访问日志记录都是一行数据。您可以通过日志聚合系统捕获此数据以进行处理。
要配置控制台访问日志记录,请将 console-access-log 设置添加到您要捕获访问日志数据的主机。以下 CLI 命令演示了默认 JBoss EAP 服务器中的默认主机上的配置:
/subsystem=undertow/server=default-server/host=default-host/setting=console-access-log:add
默认情况下,控制台访问日志记录包括以下数据:
| 日志数据字段名称 | 描述 |
|---|---|
| eventSource | 请求中的事件源 |
| hostName | 处理请求的 JBoss EAP 主机 |
| bytesSent | 对请求发送的 JBoss EAP 服务器的字节数 |
| dateTime | JBoss EAP 服务器处理请求的日期和时间 |
| remoteHost | 请求源自的计算机的 IP 地址 |
| remoteUser | 与远程请求关联的用户名 |
| requestLine | 已提交的请求 |
| responseCode | JBoss EAP 服务器返回的 HTTP 响应代码 |
默认属性始终包含在日志输出中。您可以使用 attributes 属性更改默认日志数据的标签,在某些情况下可以更改数据配置。您还可以使用 attributes 属性在输出中添加额外的日志数据。
| 日志数据字段名称 | 描述 | 格式 |
|---|---|---|
| authentication-type | 用于验证与请求关联的用户的身份验证类型。默认标签: authenticationType 使用 key 选项更改此属性的标签。 | authentication-type{} authentication-type={key="authType"} |
| bytes-sent | 请求返回的字节数,不包括 HTTP 标头。默认标签: bytesSent 使用 key 选项更改此属性的标签。 | bytes-sent={} bytes-sent={key="sent-bytes"} |
| date-time | 接收和处理请求的日期和时间。默认 label: dateTime 使用 key 选项更改此属性的标签。使用 date-format 定义用于格式化 date-time 记录的模式。模式必须是 Java SimpleDateFormatter 模式。使用 time-zone 选项指定在定义了 date-format 选项时,用来格式化日期和时间数据的时区。这个值必须是有效的 java.util.TimeZone。 | date-time={key="<keyname>", date-format="<date-time format>"} date-time={key="@timestamp", date-format="yyyy-MM-dd'T'HH:mm:ssSSS"} |
| host-and-port | 请求查询的主机和端口。默认标签:hostAndPort 使用 key 选项更改此属性的标签。 | host-and-port{} host-and-port={key="port-host"} |
| local-ip | 本地连接的 IP 地址。使用 key 选项更改此属性的标签。默认标签:localIp 使用 key 选项更改此属性的标签。 | local-ip{} local-ip{key=”localIP”} |
| local-port | 本地连接的端口。默认标签:localPort 使用 key 选项更改此属性的标签。 | local-port{} local-port{key=”LocalPort”} |
| local-server-name | 处理请求的本地服务器的名称。默认标签:localServerName 使用 key 选项更改此属性的标签。 | local-server-name {} local-server-name {key=LocalServerName} |
| path-parameter | 请求中包含的一个或多个路径或 URI 参数。name 属性是一个以逗号分隔的名称列表,用于解析交换值。使用 key-prefix 属性使密钥是唯一的。如果指定了 key-prefix,前缀会添加到输出中每个 path 参数的名称中。 | path-parameter{names={store,section}} path-parameter{names={store,section}, key-prefix="my-"} |
| predicate | predicate 上下文的名称。name 属性是一个以逗号分隔的名称列表,用于解析交换值。使用 key-prefix 属性使密钥是唯一的。如果指定了 key-prefix,前缀会添加到输出中每个 path 参数的名称中。 | predicate{names={store,section}} predicate{names={store,section}, key-prefix="my-"} |
| query-parameter | 请求中包含的一个或多个查询参数。name 属性是一个以逗号分隔的名称列表,用于解析交换值。使用 key-prefix 属性使密钥是唯一的。如果指定了 key-prefix,前缀会添加到输出中每个 path 参数的名称中。 | query-parameter{names={store,section}} query-parameter{names={store,section}, key-prefix="my-"} |
| query-string | 请求的查询字符串。默认 label: queryString 使用 key 选项更改此属性的标签。使用 include-question-mark 属性来指定查询字符串应包含问号。默认情况下,不包括问号。 | query-string{} query-string{key=”QueryString”, include-question-mark=”true”} |
| relative-path | 请求的相对路径。默认标签: relativePath 使用 key 选项更改此属性的标签。 | relative-path{} relative-path{key="RelativePath"} |
| remote-host | 远程主机名。默认标签:remoteHost 使用 key 选项更改此属性的标签。 | remote-host{} remote-host{key=”RemoteHost”} |
| remote-ip | 远程 IP 地址。默认标签:remoteIp 使用 key 选项更改此属性的标签。使用 obfuscated 属性来模糊处理输出日志记录中的 IP 地址。默认值为 false。 | remote-ip{} remote-ip{key=”RemoteIP”, obfuscated=”true”} |
| remote-user | 已验证的远程用户。默认标签:remoteUser 使用密钥选项来更改此属性的标签。 | remote-user{} remote-user{key="RemoteUser"} |
| request-header | 请求标头的名称。结构化数据的键是标头的名称;值是命名标头的值。name 属性是一个以逗号分隔的名称列表,用于解析交换值。使用 key-prefix 属性使密钥是唯一的。如果指定了 key-prefix,前缀会添加到日志输出中的请求标头名称中。 | request-header{names={store,section}} request-header{names={store,section}, key-prefix="my-"} |
| 请求标头 | 请求行。默认标签:requestLine 使用 key 选项更改此属性的标签。 | request-line{} request-line{key="Request-Line"} |
| request-method | 请求方法。默认 label: requestMethod 使用 key 选项更改此属性的标签。 | request-method{} request-method{key="RequestMethod"} |
| request-path | 请求的相对路径。默认 label: requestPath 使用 key 选项更改此属性的标签。 | request-path{} request-path{key="RequestPath"} |
| request-protocol | 请求的协议。默认标签: requestProtocol 使用 key 选项更改此属性的标签。 | request-protocol{} request-protocol{key=”RequestProtocol”} |
| request-scheme | 请求的 URI 方案。默认标签: requestScheme 使用 key 选项更改此属性的标签。 | request-scheme{} request-scheme{key=”RequestScheme”} |
| request-url | 原始请求 URI。如果由客户端指定,包括主机名、协议等。默认 label: requestUrl 使用 key 选项更改此属性的标签。 | request-url{} request-url{key="RequestURL"} |
| resolved-path | 解析的路径。默认 Label: resolvedPath 使用 key 选项更改此属性的标签。 | resolved-path{} resolved-path{key=”ResolvedPath”} |
| response-code | 响应代码。默认标签: responseCode 使用 key 选项更改此属性的标签。 | response-code{} response-code{key=”ResponseCode”} |
| response-header | 响应标头的名称。结构化数据的键是标头的名称;值是命名标头的值。name 属性是一个以逗号分隔的名称列表,用于解析交换值。使用 key-prefix 属性使密钥是唯一的。如果指定了 key-prefix,前缀会添加到日志输出中的请求标头名称中。 | response-header{names={store,section}} response-header{names={store,section}, key-prefix="my-"} |
| response-reason-phrase | 响应代码的文本原因。默认标签: responseReasonPhrase 使用 key 选项更改此属性的标签。 | response-reason-phrase{} response-reason-phrase{key=”ResponseReasonPhrase”} |
| response-time | 处理请求的时间。默认 label: responseTime 使用 key 选项更改此属性的标签。默认时间单位是 MILLISECONDS。可用时间单元包括:* NANOSECONDS * MICROSECONDS * MILLISECONDS * SECONDS | response-time{} response-time{key="ResponseTime", time-unit=SECONDS} |
| secure-exchange | 指明交换是否安全。默认标签: secureExchange 使用 key 选项更改此属性的标签。 | secure-exchange{} secure-exchange{key=”SecureExchange”} |
| ssl-cipher | 请求的 SSL 密码。默认标签:sslCipher 使用 key 选项更改此属性的标签。 | ssl-cipher{} ssl-cipher{key="SSLCipher"} |
| ssl-client-cert | 请求的 SSL 客户端证书。默认标签:sslClientCert 使用 key 选项更改此属性的标签。 | ssl-client-cert{} ssl-client-cert{key=”SSLClientCert”} |
| ssl-session-id | 请求的 SSL 会话 ID。默认标签:sslSessionId 使用 key 选项更改此属性的标签。 | ssl-session-id{} stored-response |
| 存储的请求响应。默认标签: storedResponse 使用 key 选项来更改此属性的标签。 | stored-response{} stored-response{key=”StoredResponse”} | thread-name |
| 当前线程的线程名称。默认 label: threadName 使用 key 选项更改此属性的标签。 | thread-name{} thread-name{key="ThreadName"} | transport-protocol |
您可以使用 metadata 属性配置额外的任意数据,以包含在访问日志记录中。metadata 属性的值是一组 key:value 对,用于定义要在访问日志记录中包含的数据。对中的值可以是管理模型表达式。当服务器启动或重新加载时,管理模型表达式会被解析。键值对用逗号分开。
以下 CLI 命令演示了复杂的控制台日志配置示例,包括额外的日志数据、日志数据自定义和其他元数据:
/subsystem=undertow/server=default-server/host=default-host/setting=console-access-log:add(metadata={"@version"="1", "qualifiedHostName"=${jboss.qualified.host.name:unknown}}, attributes={bytes-sent={}, date-time={key="@timestamp", date-format="yyyy-MM-dd'T'HH:mm:ssSSS"}, remote-host={}, request-line={}, response-header={key-prefix="responseHeader", names=["Content-Type"]}, response-code={}, remote-user={}})
生成的访问日志记录类似以下额外 JSON 数据(请注意:以下示例输出被格式化为可读性);实际记录中,所有数据都将作为一行输出:
{
"eventSource":"web-access",
"hostName":"default-host",
"@version":"1",
"qualifiedHostName":"localhost.localdomain",
"bytesSent":1504,
"@timestamp":"2019-05-02T11:57:37123",
"remoteHost":"127.0.0.1",
"remoteUser":null,
"requestLine":"GET / HTTP/2.0",
"responseCode":200,
"responseHeaderContent-Type":"text/html"
}
以下命令演示了在激活控制台访问日志后对日志数据的更新:
/subsystem=undertow/server=default-server/host=default-host/setting=console-access-log:write-attribute(name=attributes,value={bytes-sent={}, date-time={key="@timestamp", date-format="yyyy-MM-dd'T'HH:mm:ssSSS"}, remote-host={}, request-line={}, response-header={key-prefix="responseHeader", names=["Content-Type"]}, response-code={}, remote-user={}})
以下命令演示了在激活控制台访问日志后对自定义元数据的更新:
/subsystem=undertow/server=default-server/host=default-host/setting=console-access-log:write-attribute(name=metadata,value={"@version"="1", "qualifiedHostName"=${jboss.qualified.host.name:unknown}})