当前位置:   article > 正文

UnderTow 核心配置_undertow配置详解

undertow配置详解

本系列代码地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford

Undertow 的配置可以参考 Undertow 的 Builder,并且其中也有一些默认的配置参数:

Undertow

  1. private Builder() {
  2. ioThreads = Math.max(Runtime.getRuntime().availableProcessors(), 2);
  3. workerThreads = ioThreads * 8;
  4. long maxMemory = Runtime.getRuntime().maxMemory();
  5. //smaller than 64mb of ram we use 512b buffers
  6. if (maxMemory < 64 * 1024 * 1024) {
  7. //use 512b buffers
  8. directBuffers = false;
  9. bufferSize = 512;
  10. } else if (maxMemory < 128 * 1024 * 1024) {
  11. //use 1k buffers
  12. directBuffers = true;
  13. bufferSize = 1024;
  14. } else {
  15. //use 16k buffers for best performance
  16. //as 16k is generally the max amount of data that can be sent in a single write() call
  17. directBuffers = true;
  18. bufferSize = 1024 * 16 - 20; //the 20 is to allow some space for protocol headers, see UNDERTOW-1209
  19. }
  20. }
  • ioThreads 大小为可用 CPU 数量 * 2,即 Undertow 的 XNIO 的读线程个数为可用 CPU 数量,写线程个数也为可用 CPU 数量。
  • workerThreads 大小为 ioThreads 数量 * 8.
  • 如果内存大小小于 64 MB,则不使用直接内存,bufferSize 为 512 字节
  • 如果内存大小大于 64 MB 小于 128 MB,则使用直接内存,bufferSize 为 1024 字节
  • 如果内存大小大于 128 MB,则使用直接内存,bufferSize 为 16 KB 减去 20 字节,这 20 字节用于协议头。

DefaultByteBufferPool 构造器:

  1. public DefaultByteBufferPool(boolean direct, int bufferSize, int maximumPoolSize, int threadLocalCacheSize, int leakDecetionPercent) {
  2. this.direct = direct;
  3. this.bufferSize = bufferSize;
  4. this.maximumPoolSize = maximumPoolSize;
  5. this.threadLocalCacheSize = threadLocalCacheSize;
  6. this.leakDectionPercent = leakDecetionPercent;
  7. if(direct) {
  8. arrayBackedPool = new DefaultByteBufferPool(false, bufferSize, maximumPoolSize, 0, leakDecetionPercent);
  9. } else {
  10. arrayBackedPool = this;
  11. }
  12. }

其中:

  • direct:是否使用直接内存,我们需要设置为 true,来使用直接内存。
  • bufferSize:每次申请的 buffer 大小,我们主要要考虑这个大小
  • maximumPoolSize:buffer 池最大大小,一般不用修改
  • threadLocalCacheSize:线程本地 buffer 池大小,一般不用修改
  • leakDecetionPercent:内存泄漏检查百分比,目前没啥卵用

对于 bufferSize,最好和你系统的 TCP Socket Buffer 配置一样。在我们的容器中,我们将微服务实例的容器内的 TCP Socket Buffer 的读写 buffer 大小成一模一样的配置(因为微服务之间调用,发送的请求也是另一个微服务接受,所以调整所有微服务容器的读写 buffer 大小一致,来优化性能,默认是根据系统内存来自动计算出来的)。

查看 Linux 系统 TCP Socket Buffer 的大小:

  • /proc/sys/net/ipv4/tcp_rmem (对于读取)
  • /proc/sys/net/ipv4/tcp_wmem (对于写入)

在我们的容器中,分别是:

  1. bash-4.2# cat /proc/sys/net/ipv4/tcp_rmem
  2. 4096 16384 4194304
  3. bash-4.2# cat /proc/sys/net/ipv4/tcp_wmem
  4. 4096 16384 4194304

从左到右三个值分别为:每个 TCP Socket 的读 Buffer 与写 Buffer 的大小的 最小值,默认值和最大值,单位是字节。

我们设置我们 Undertow 的 buffer size 为 TCP Socket Buffer 的默认值,即 16 KB。Undertow 的 Builder 里面,如果内存大于 128 MB,buffer size 为 16 KB 减去 20 字节(为协议头预留)。所以,我们使用默认的即可

application.yml 配置:

  1. server.undertow:
  2. # 是否分配的直接内存(NIO直接分配的堆外内存),这里开启,所以java启动参数需要配置下直接内存大小,减少不必要的GC
  3. # 在内存大于 128 MB 时,默认就是使用直接内存的
  4. directBuffers: true
  5. # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作
  6. # 如果每次需要 ByteBuffer 的时候都去申请,对于堆内存的 ByteBuffer 需要走 JVM 内存分配流程(TLAB -> 堆),对于直接内存则需要走系统调用,这样效率是很低下的。
  7. # 所以,一般都会引入内存池。在这里就是 `BufferPool`。
  8. # 目前,UnderTow 中只有一种 `DefaultByteBufferPool`,其他的实现目前没有用。
  9. # 这个 DefaultByteBufferPool 相对于 netty 的 ByteBufArena 来说,非常简单,类似于 JVM TLAB 的机制
  10. # 对于 bufferSize,最好和你系统的 TCP Socket Buffer 配置一样
  11. # `/proc/sys/net/ipv4/tcp_rmem` (对于读取)
  12. # `/proc/sys/net/ipv4/tcp_wmem` (对于写入)
  13. # 在内存大于 128 MB 时,bufferSize 为 16 KB 减去 20 字节,这 20 字节用于协议头
  14. buffer-size: 16384 - 20

Worker 配置其实就是 XNIO 的核心配置,主要需要配置的即 io 线程池以及 worker 线程池大小。

默认情况下,io 线程大小为可用 CPU 数量 * 2,即读线程个数为可用 CPU 数量,写线程个数也为可用 CPU 数量。worker 线程池大小为 io 线程大小 * 8.

微服务应用由于涉及的阻塞操作比较多,所以可以将 worker 线程池大小调大一些。我们的应用设置为 io 线程大小 * 32.

application.yml 配置:

  1. server.undertow.threads:
  2. # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个读线程和一个写线程
  3. io: 16
  4. # 阻塞任务线程池, 当执行类似servlet请求阻塞IO操作, undertow会从这个线程池中取得线程
  5. # 它的值设置取决于系统线程执行任务的阻塞系数,默认值是IO线程数*8
  6. worker: 128

Spring Boot 中对于 Undertow 相关配置的抽象是 ServerProperties 这个类。目前 Undertow 涉及的所有配置以及说明如下(不包括 accesslog 相关的,accesslog 会在下一节详细分析):

  1. server:
  2. undertow:
  3. # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作
  4. # 如果每次需要 ByteBuffer 的时候都去申请,对于堆内存的 ByteBuffer 需要走 JVM 内存分配流程(TLAB -> 堆),对于直接内存则需要走系统调用,这样效率是很低下的。
  5. # 所以,一般都会引入内存池。在这里就是 `BufferPool`。
  6. # 目前,UnderTow 中只有一种 `DefaultByteBufferPool`,其他的实现目前没有用。
  7. # 这个 DefaultByteBufferPool 相对于 netty 的 ByteBufArena 来说,非常简单,类似于 JVM TLAB 的机制
  8. # 对于 bufferSize,最好和你系统的 TCP Socket Buffer 配置一样
  9. # `/proc/sys/net/ipv4/tcp_rmem` (对于读取)
  10. # `/proc/sys/net/ipv4/tcp_wmem` (对于写入)
  11. # 在内存大于 128 MB 时,bufferSize 为 16 KB 减去 20 字节,这 20 字节用于协议头
  12. buffer-size: 16364
  13. # 是否分配的直接内存(NIO直接分配的堆外内存),这里开启,所以java启动参数需要配置下直接内存大小,减少不必要的GC
  14. # 在内存大于 128 MB 时,默认就是使用直接内存的
  15. directBuffers: true
  16. threads:
  17. # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个读线程和一个写线程
  18. io: 4
  19. # 阻塞任务线程池, 当执行类似servlet请求阻塞IO操作, undertow会从这个线程池中取得线程
  20. # 它的值设置取决于系统线程执行任务的阻塞系数,默认值是IO线程数*8
  21. worker: 128
  22. # http post body 大小,默认为 -1B ,即不限制
  23. max-http-post-size: -1B
  24. # 是否在启动时创建 filter,默认为 true,不用修改
  25. eager-filter-init: true
  26. # 限制路径参数数量,默认为 1000
  27. max-parameters: 1000
  28. # 限制 http header 数量,默认为 200
  29. max-headers: 200
  30. # 限制 http header 中 cookies 的键值对数量,默认为 200
  31. max-cookies: 200
  32. # 是否允许 / 与 %2F 转义。/ 是 URL 保留字,除非你的应用明确需要,否则不要开启这个转义,默认为 false
  33. allow-encoded-slash: false
  34. # 是否允许 URL 解码,默认为 true,除了 %2F 其他的都会处理
  35. decode-url: true
  36. # url 字符编码集,默认是 utf-8
  37. url-charset: utf-8
  38. # 响应的 http header 是否会加上 'Connection: keep-alive',默认为 true
  39. always-set-keep-alive: true
  40. # 请求超时,默认是不超时,我们的微服务因为可能有长时间的定时任务,所以不做服务端超时,都用客户端超时,所以我们保持这个默认配置
  41. no-request-timeout: -1
  42. # 是否在跳转的时候保持 path,默认是关闭的,一般不用配置
  43. preserve-path-on-forward: false
  44. options:
  45. # spring boot 没有抽象的 xnio 相关配置在这里配置,对应 org.xnio.Options 类
  46. socket:
  47. SSL_ENABLED: false
  48. # spring boot 没有抽象的 undertow 相关配置在这里配置,对应 io.undertow.UndertowOptions 类
  49. server:
  50. ALLOW_UNKNOWN_PROTOCOLS: false

Spring Boot 并没有将所有的 Undertow 与 XNIO 配置进行抽象,如果你想自定义一些相关配置,可以通过上面配置最后的 server.undertow.options 进行配置。server.undertow.options.socket 对应 XNIO 的相关配置,配置类是 org.xnio.Options;server.undertow.options.server 对应 Undertow 的相关配置,配置类是 io.undertow.UndertowOptions

我们这一节详细介绍了 Undertow 的核心配置,主要包括线程池以及 buffer 配置,以及关于 http 协议的一些配置。并且我们还介绍了这些配置在 spring boot 下该如何配置。下一节,我们将详细介绍如何配置 Undertow 的 accesslog。

  1. server:
  2. undertow:
  3. # access log相关配置
  4. accesslog:
  5. # 存放目录,默认为 logs
  6. dir: ./log
  7. # 是否开启
  8. enabled: true
  9. # 格式,各种占位符后面会详细说明
  10. pattern: '{
  11. "transportProtocol":"%{TRANSPORT_PROTOCOL}",
  12. "scheme":"%{SCHEME}",
  13. "protocol":"%{PROTOCOL}",
  14. "method":"%{METHOD}",
  15. "reqHeaderUserAgent":"%{i,User-Agent}",
  16. "cookieUserId": "%{c,userId}",
  17. "queryTest": "%{q,test}",
  18. "queryString": "%q",
  19. "relativePath": "%R, %{REQUEST_PATH}, %{RESOLVED_PATH}",
  20. "requestLine": "%r",
  21. "uri": "%U",
  22. "thread": "%I",
  23. "hostPort": "%{HOST_AND_PORT}",
  24. "localIp": "%A",
  25. "localPort": "%p",
  26. "localServerName": "%v",
  27. "remoteIp": "%a",
  28. "remoteHost": "%h",
  29. "bytesSent": "%b",
  30. "time":"%{time,yyyy-MM-dd HH:mm:ss.S}",
  31. "status":"%s",
  32. "reason":"%{RESPONSE_REASON_PHRASE}",
  33. "respHeaderUserSession":"%{o,userSession}",
  34. "respCookieUserId":"%{resp-cookie,userId}",
  35. "timeUsed":"%Dms, %Ts, %{RESPONSE_TIME}ms, %{RESPONSE_TIME_MICROS} us, %{RESPONSE_TIME_NANOS} ns",
  36. }'
  37. # 文件前缀,默认为 access_log
  38. prefix: access.
  39. # 文件后缀,默认为 log
  40. suffix: log
  41. # 是否另起日志文件写 access log,默认为 true
  42. # 目前只能按照日期进行 rotate,一天一个日志文件
  43. rotate: true

Undertow 的 accesslog 处理核心类抽象是 io.undertow.server.handlers.accesslog.AccesslogReceiver。由于目前 Undertow 的 AccesslogReceiver只有一种实现在使用,也就是 io.undertow.server.handlers.accesslog.DefaultAccessLogReceiver

查看 DefaultAccessLogReceiver 的 rotate 时机:

DefaultAccessLogReceiver

  1. /**
  2. * 计算 rotate 时间点
  3. */
  4. private void calculateChangeOverPoint() {
  5. Calendar calendar = Calendar.getInstance();
  6. calendar.set(Calendar.SECOND, 0);
  7. calendar.set(Calendar.MINUTE, 0);
  8. calendar.set(Calendar.HOUR_OF_DAY, 0);
  9. //当前时间日期 + 1,即下一天
  10. calendar.add(Calendar.DATE, 1);
  11. SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
  12. currentDateString = df.format(new Date());
  13. // if there is an existing default log file, use the date last modified instead of the current date
  14. if (Files.exists(defaultLogFile)) {
  15. try {
  16. currentDateString = df.format(new Date(Files.getLastModifiedTime(defaultLogFile).toMillis()));
  17. } catch(IOException e){
  18. // ignore. use the current date if exception happens.
  19. }
  20. }
  21. //rotate 时机是下一天的 0 点
  22. changeOverPoint = calendar.getTimeInMillis();
  23. }

其实 Undertow 中的 accesslog 占位符,就是之前我们提到的 Undertow Listener 解析请求后抽象的 HTTP server exchange 的属性。

官网文档的表格并不是最全的,并且注意点并没有说明,例如某些占位符必须打开某些 Undertow 特性才能使用等等。这里我们列出下。

首先先提出一个注意点,参数占位符,例如 %{i,你要看的header值} 查看 header 的某个 key 的值。逗号后面注意不要有空格,因为这个空格会算入 key 里面导致拿不到你想要的 key

请求相关属性


注意:

  1. 路径参数 PathVariable 由于并没有被 Undertow 的 Listener 或者 Handler 解析处理,所以拦截不到,无法确认是否是一个 PathVariable 还是就是 url 路径。所以,PathVariable 的占位符是不会起作用的

请求地址相关


注意:

  1. 请求的远程地址我们一般不从请求连接获取,而是通过 Http Header 里面的 X-forwarded-for 或者 X-real-ip 等获取,因为现在请求都是通过各种 VPN,负载均衡器发上来的。

响应相关属性


注意:默认 undertow 没有开启请求时间内统计,需要打开才能统计响应时间,如何开启呢?通过注册一个 WebServerFactoryCustomizer 到 Spring ApplicationContext 中即可。请看下面的代码(项目地址:https://github.com/HashZhang/spring-cloud-scaffold/blob/master/spring-cloud-iiford/):

spring.factories(省略无关代码)

  1. # AutoConfiguration
  2. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  3. com.github.hashjang.spring.cloud.iiford.service.common.auto.UndertowAutoConfiguration

UndertowAutoConfiguration

  1. //设置proxyBeanMethods=false,因为没有 @Bean 的方法互相调用需要每次返回同一个 Bean,没必要代理,关闭增加启动速度
  2. @Configuration(proxyBeanMethods = false)
  3. @Import(WebServerConfiguration.class)
  4. public class UndertowAutoConfiguration {
  5. }

WebServerConfiguration

  1. //设置proxyBeanMethods=false,因为没有 @Bean 的方法互相调用需要每次返回同一个 Bean,没必要代理,关闭增加启动速度
  2. @Configuration(proxyBeanMethods = false)
  3. public class WebServerConfiguration {
  4. @Bean
  5. public WebServerFactoryCustomizer<ConfigurableUndertowWebServerFactory> undertowWebServerAccessLogTimingEnabler(ServerProperties serverProperties) {
  6. return new DefaultWebServerFactoryCustomizer(serverProperties);
  7. }
  8. }

DefaultWebServerFactoryCustomizer

  1. public class DefaultWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableUndertowWebServerFactory> {
  2. private final ServerProperties serverProperties;
  3. public DefaultWebServerFactoryCustomizer(ServerProperties serverProperties) {
  4. this.serverProperties = serverProperties;
  5. }
  6. @Override
  7. public void customize(ConfigurableUndertowWebServerFactory factory) {
  8. String pattern = serverProperties.getUndertow().getAccesslog().getPattern();
  9. // 如果 accesslog 配置中打印了响应时间,则打开记录请求开始时间配置
  10. if (logRequestProcessingTiming(pattern)) {
  11. factory.addBuilderCustomizers(builder -> builder.setServerOption(UndertowOptions.RECORD_REQUEST_START_TIME, true));
  12. }
  13. }
  14. private boolean logRequestProcessingTiming(String pattern) {
  15. if (StringUtils.isBlank(pattern)) {
  16. return false;
  17. }
  18. //判断 accesslog 是否配置了查看响应时间
  19. return pattern.contains(ResponseTimeAttribute.RESPONSE_TIME_MICROS)
  20. || pattern.contains(ResponseTimeAttribute.RESPONSE_TIME_MILLIS)
  21. || pattern.contains(ResponseTimeAttribute.RESPONSE_TIME_NANOS)
  22. || pattern.contains(ResponseTimeAttribute.RESPONSE_TIME_MILLIS_SHORT)
  23. || pattern.contains(ResponseTimeAttribute.RESPONSE_TIME_SECONDS_SHORT);
  24. }
  25. }

其他


还有安全相关的属性(SSL 相关,登录认证 Authentication 相关),微服务内部调用一般用不到,我们这里就不赘述了。 其它内置的属性,在 Spring Boot 环境下一般用不到,我们这里就不讨论了。

举例


我们最开始配置的 accesslog 的例子请求返回如下( JSON 格式化之后的结果):

  1. {
  2. "transportProtocol": "http/1.1",
  3. "scheme": "http",
  4. "protocol": "HTTP/1.1",
  5. "method": "GET",
  6. "reqHeaderUserAgent": "PostmanRuntime/7.26.10",
  7. "cookieUserId": "testRequestCookieUserId",
  8. "queryTest": "1",
  9. "queryString": "?test=1&query=2",
  10. "relativePath": "/test, /test, -",
  11. "requestLine": "GET /test?test=1&query=2 HTTP/1.1",
  12. "uri": "/test",
  13. "thread": "XNIO-2 task-1",
  14. "hostPort": "127.0.0.1:8102",
  15. "localIp": "127.0.0.1",
  16. "localPort": "8102",
  17. "localServerName": "127.0.0.1",
  18. "remoteIp": "127.0.0.1",
  19. "remoteHost": "127.0.0.1",
  20. "bytesSent": "26",
  21. "time": "2021-04-08 00:07:50.410",
  22. "status": "200",
  23. "reason": "OK",
  24. "respHeaderUserSession": "testResponseHeaderUserSession",
  25. "respCookieUserId": "testResponseCookieUserId",
  26. "timeUsed": "3683ms, 3.683s, 3683ms, 3683149 us, 3683149200 ns",
  27. }

我们这一节详细介绍了如何配置 Undertow 的 accesslog,将 accesslog 各种占位符都罗列了出来,用户可以根据这些信息配置出自己想要的 accesslog 信息以及格式。下一节,我们将详细介绍我们框架中针对 Undertow 的定制代码

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/240507?site
推荐阅读
相关标签
  

闽ICP备14008679号