SpringCloud升級之路2020.0.x版-13.UnderTow 核心配置

乾貨滿滿張雜湊發表於2021-08-17

本系列程式碼地址:https://github.com/HashZhang/spring-cloud-scaffold/tree/master/spring-cloud-iiford

image

Undertow 的配置可以參考 Undertow 的 Builder,並且其中也有一些預設的配置引數:

Undertow

private Builder() {
    ioThreads = Math.max(Runtime.getRuntime().availableProcessors(), 2);
    workerThreads = ioThreads * 8;
    long maxMemory = Runtime.getRuntime().maxMemory();
    //smaller than 64mb of ram we use 512b buffers
    if (maxMemory < 64 * 1024 * 1024) {
        //use 512b buffers
        directBuffers = false;
        bufferSize = 512;
    } else if (maxMemory < 128 * 1024 * 1024) {
        //use 1k buffers
        directBuffers = true;
        bufferSize = 1024;
    } else {
        //use 16k buffers for best performance
        //as 16k is generally the max amount of data that can be sent in a single write() call
        directBuffers = true;
        bufferSize = 1024 * 16 - 20; //the 20 is to allow some space for protocol headers, see UNDERTOW-1209
    }

}
  • 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 位元組用於協議頭。

image

DefaultByteBufferPool 構造器:

public DefaultByteBufferPool(boolean direct, int bufferSize, int maximumPoolSize, int threadLocalCacheSize, int leakDecetionPercent) {
    this.direct = direct;
    this.bufferSize = bufferSize;
    this.maximumPoolSize = maximumPoolSize;
    this.threadLocalCacheSize = threadLocalCacheSize;
    this.leakDectionPercent = leakDecetionPercent;
    if(direct) {
        arrayBackedPool = new DefaultByteBufferPool(false, bufferSize, maximumPoolSize, 0, leakDecetionPercent);
    } else {
        arrayBackedPool = this;
    }
}

其中:

  • 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 (對於寫入)

在我們的容器中,分別是:

bash-4.2# cat /proc/sys/net/ipv4/tcp_rmem
4096    16384   4194304 
bash-4.2# cat /proc/sys/net/ipv4/tcp_wmem
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 配置:

server.undertow:
    # 是否分配的直接記憶體(NIO直接分配的堆外記憶體),這裡開啟,所以java啟動引數需要配置下直接記憶體大小,減少不必要的GC
    # 在記憶體大於 128 MB 時,預設就是使用直接記憶體的
    directBuffers: true
    # 以下的配置會影響buffer,這些buffer會用於伺服器連線的IO操作
    # 如果每次需要 ByteBuffer 的時候都去申請,對於堆記憶體的 ByteBuffer 需要走 JVM 記憶體分配流程(TLAB -> 堆),對於直接記憶體則需要走系統呼叫,這樣效率是很低下的。
    # 所以,一般都會引入記憶體池。在這裡就是 `BufferPool`。
    # 目前,UnderTow 中只有一種 `DefaultByteBufferPool`,其他的實現目前沒有用。
    # 這個 DefaultByteBufferPool 相對於 netty 的 ByteBufArena 來說,非常簡單,類似於 JVM TLAB 的機制
    # 對於 bufferSize,最好和你係統的 TCP Socket Buffer 配置一樣
    # `/proc/sys/net/ipv4/tcp_rmem` (對於讀取)
    # `/proc/sys/net/ipv4/tcp_wmem` (對於寫入)
    # 在記憶體大於 128 MB 時,bufferSize 為 16 KB 減去 20 位元組,這 20 位元組用於協議頭
    buffer-size: 16384 - 20

image

Worker 配置其實就是 XNIO 的核心配置,主要需要配置的即 io 執行緒池以及 worker 執行緒池大小。

預設情況下,io 執行緒大小為可用 CPU 數量 * 2,即讀執行緒個數為可用 CPU 數量,寫執行緒個數也為可用 CPU 數量。worker 執行緒池大小為 io 執行緒大小 * 8.

微服務應用由於涉及的阻塞操作比較多,所以可以將 worker 執行緒池大小調大一些。我們的應用設定為 io 執行緒大小 * 32.

application.yml 配置:

server.undertow.threads:
    # 設定IO執行緒數, 它主要執行非阻塞的任務,它們會負責多個連線, 預設設定每個CPU核心一個讀執行緒和一個寫執行緒
    io: 16
    # 阻塞任務執行緒池, 當執行類似servlet請求阻塞IO操作, undertow會從這個執行緒池中取得執行緒
    # 它的值設定取決於系統執行緒執行任務的阻塞係數,預設值是IO執行緒數*8
    worker: 128

image

Spring Boot 中對於 Undertow 相關配置的抽象是 ServerProperties 這個類。目前 Undertow 涉及的所有配置以及說明如下(不包括 accesslog 相關的,accesslog 會在下一節詳細分析):

server:
  undertow:
    # 以下的配置會影響buffer,這些buffer會用於伺服器連線的IO操作
    # 如果每次需要 ByteBuffer 的時候都去申請,對於堆記憶體的 ByteBuffer 需要走 JVM 記憶體分配流程(TLAB -> 堆),對於直接記憶體則需要走系統呼叫,這樣效率是很低下的。
    # 所以,一般都會引入記憶體池。在這裡就是 `BufferPool`。
    # 目前,UnderTow 中只有一種 `DefaultByteBufferPool`,其他的實現目前沒有用。
    # 這個 DefaultByteBufferPool 相對於 netty 的 ByteBufArena 來說,非常簡單,類似於 JVM TLAB 的機制
    # 對於 bufferSize,最好和你係統的 TCP Socket Buffer 配置一樣
    # `/proc/sys/net/ipv4/tcp_rmem` (對於讀取)
    # `/proc/sys/net/ipv4/tcp_wmem` (對於寫入)
    # 在記憶體大於 128 MB 時,bufferSize 為 16 KB 減去 20 位元組,這 20 位元組用於協議頭
    buffer-size: 16364
    # 是否分配的直接記憶體(NIO直接分配的堆外記憶體),這裡開啟,所以java啟動引數需要配置下直接記憶體大小,減少不必要的GC
    # 在記憶體大於 128 MB 時,預設就是使用直接記憶體的
    directBuffers: true
    threads:
      # 設定IO執行緒數, 它主要執行非阻塞的任務,它們會負責多個連線, 預設設定每個CPU核心一個讀執行緒和一個寫執行緒
      io: 4
      # 阻塞任務執行緒池, 當執行類似servlet請求阻塞IO操作, undertow會從這個執行緒池中取得執行緒
      # 它的值設定取決於系統執行緒執行任務的阻塞係數,預設值是IO執行緒數*8
      worker: 128
    # http post body 大小,預設為 -1B ,即不限制
    max-http-post-size: -1B
    # 是否在啟動時建立 filter,預設為 true,不用修改
    eager-filter-init: true
    # 限制路徑引數數量,預設為 1000
    max-parameters: 1000
    # 限制 http header 數量,預設為 200
    max-headers: 200
    # 限制 http header 中 cookies 的鍵值對數量,預設為 200
    max-cookies: 200
    # 是否允許 / 與 %2F 轉義。/ 是 URL 保留字,除非你的應用明確需要,否則不要開啟這個轉義,預設為 false
    allow-encoded-slash: false
    # 是否允許 URL 解碼,預設為 true,除了 %2F 其他的都會處理
    decode-url: true
    # url 字元編碼集,預設是 utf-8
    url-charset: utf-8
    # 響應的 http header 是否會加上 'Connection: keep-alive',預設為 true
    always-set-keep-alive: true
    # 請求超時,預設是不超時,我們的微服務因為可能有長時間的定時任務,所以不做服務端超時,都用客戶端超時,所以我們保持這個預設配置
    no-request-timeout: -1
    # 是否在跳轉的時候保持 path,預設是關閉的,一般不用配置
    preserve-path-on-forward: false
    options:
      # spring boot 沒有抽象的 xnio 相關配置在這裡配置,對應 org.xnio.Options 類
      socket:
        SSL_ENABLED: false
      # spring boot 沒有抽象的 undertow 相關配置在這裡配置,對應 io.undertow.UndertowOptions 類
      server:
        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。

微信搜尋“我的程式設計喵”關注公眾號,每日一刷,輕鬆提升技術,斬獲各種offer

相關文章