<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          「谷歌實踐」Spring Boot 微服務(wù)容器化優(yōu)化

          共 7001字,需瀏覽 15分鐘

           ·

          2020-09-04 15:57

          譯者注

          本篇文章雖然介紹的是 Google Cloud Run 中的 java 優(yōu)化,大部分建議對于dockerk8s等容器化同樣適用,希望對大家有所幫助。

          前言

          本指南介紹了谷歌對使用 Java 編寫的 Cloud Run 服務(wù)的優(yōu)化,將幫助你了解 Spring Boot 常見優(yōu)化方式。本文內(nèi)容是對常規(guī)優(yōu)化提示[1]的補(bǔ)充,這些建議同樣適用于傳統(tǒng) Java 應(yīng)用。

          傳統(tǒng)的 Java 網(wǎng)頁應(yīng)用優(yōu)化旨在高并發(fā)和低延遲地處理請求,并穩(wěn)定的長期運行應(yīng)用。JVM 自身還會通過JIT 優(yōu)化執(zhí)行代碼,使熱點代碼得到優(yōu)化,并使應(yīng)用運行更加高效。

          這些傳統(tǒng)的 Java Web 應(yīng)用中的許多最佳做法和優(yōu)化都圍繞著以下內(nèi)容:

          • 處理并發(fā)請求(基于線程的 I/O 和非阻塞 I/O)
          • 通過使用連接池和后臺任務(wù)減少響應(yīng)延遲時間,例如將跟蹤記錄和指標(biāo)發(fā)送到后臺任務(wù)。

          許多傳統(tǒng)優(yōu)化非常適合于長時間運行的應(yīng)用,但對于 Cloud Run 服務(wù)可能效果不佳,后者僅在主動處理請求時運行。本頁面介紹了一些不同的 Cloud Run 優(yōu)化和權(quán)衡,可用于減少啟動時間和內(nèi)存使用量。

          優(yōu)化容器鏡像

          通過優(yōu)化容器鏡像,您可以縮短加載時間和啟動時間。您可以通過以下方式優(yōu)化鏡像:

          • 盡可能減小容器鏡像大小
          • 避免使用依賴壓縮 JAR
          • 使用 Jib 構(gòu)建

          盡可能減小容器鏡像大小

          請閱讀《如何優(yōu)化構(gòu)建鏡像》[2]了解關(guān)于此問題的詳細(xì)解決方案。總結(jié)下來優(yōu)化套路如下:

          確保容器鏡像不包含:

          • 源代碼
          • Maven 構(gòu)建工件
          • 構(gòu)建工具
          • Git 目錄
          • 未使用的二進(jìn)制文件/程序

          對于基礎(chǔ)鏡像,請考慮將Distroless Java 基礎(chǔ)鏡像[3]用于最簡 Java 基礎(chǔ)鏡像,這也是使用 Jib 構(gòu)建容器鏡像[4]時選擇的默認(rèn)基礎(chǔ)鏡像。

          如果您是從Dockerfile內(nèi)構(gòu)建代碼,請使用Docker多階段構(gòu)建,以使最終容器鏡像僅具有 JRE 和應(yīng)用 JAR 文件本身。

          避免使用依賴壓縮 JAR

          一些流行的框架(如 Spring Boot)會創(chuàng)建一個應(yīng)用 (JAR) 文件,其中包含其他庫 JAR 文件(依賴 JAR)。這些文件需要在啟動期間解壓縮,影響 Cloud Run 啟動速度。所以建議通過 jib 自動化創(chuàng)建精簡 JAR 來構(gòu)建鏡像

          譯者注:同樣也可以使用Spring boot 2.3 的新特性分層 JAR[5]

          使用 Jib 構(gòu)建

          您可以使用Jib 插件[6]創(chuàng)建最小容器并自動解壓應(yīng)用依賴。Jib 同時支持MavenGradle,并且可以為 Spring Boot 應(yīng)用提供開箱即用的支持。某些應(yīng)用框架可能需要額外的 Jib 配置。

          JVM 優(yōu)化

          優(yōu)化 Cloud Run 服務(wù)的 JVM 可以提高性能和內(nèi)存使用率。

          使用容器感知的 JVM 版本

          在虛擬機(jī)和機(jī)器中,對于 CPU 和內(nèi)存分配,JVM 會從常見位置(例如,Linux 中的/proc/cpuinfo/proc/meminfo)查找其可以使用的 CPU 和內(nèi)存。但是,在容器中運行時,CPU 和內(nèi)存限制條件存儲在/proc/cgroups/...中。較舊版本的 JDK 會繼續(xù)在/proc(而不是/proc/cgroups)中查找,這可能會導(dǎo)致 CPU 和內(nèi)存用量超出分配的上限。這可能會導(dǎo)致:

          • 線程過多,因為線程池大小由Runtime.availableProcessors()配置
          • 超出容器內(nèi)存上限的默認(rèn)最大堆。JVM 在進(jìn)行垃圾回收之前大量使用內(nèi)存。這很容易導(dǎo)致容器超出容器內(nèi)存限制,并導(dǎo)致 OOMKilled。

          因此,請使用容器感知的 JVM 版本。默認(rèn)情況下,容器可以自動感知版本大于或等于 8u192 的 OpenJDK。

          了解 JVM 內(nèi)存用量

          Java 內(nèi)存使用量由本機(jī)內(nèi)存使用量和堆內(nèi)存使用量組成。應(yīng)用的工作內(nèi)存通常位于堆中。堆的大小受最大堆內(nèi)存配置的限制。使用 Cloud Run 256MB RAM 實例時,您無法將所有 256 MB 分配給最大堆,因為 JVM 和操作系統(tǒng)也需要本機(jī)內(nèi)存,例如線程棧、代碼緩存、文件處理程序、緩沖區(qū)等。如果應(yīng)用發(fā)生 OOMKilled,并且您需要了解 JVM 內(nèi)存用量(原生內(nèi)存 + 堆),請開啟 Native Memory Tracking,以便在應(yīng)用異常退出時查看使用量

          注意:無法直接通過JAVA_TOOL_OPTIONS環(huán)境變量開啟 Native Memory Tracking。您需要將 Java 命令行啟動參數(shù)添加到容器鏡像入口點,以便您的應(yīng)用使用以下參數(shù)啟動應(yīng)用:

          java -XX:NativeMemoryTracking=summary \
          -XX:+UnlockDiagnosticVMOptions \
          -XX:+PrintNMTStatistics \
          ...

          可以根據(jù)要加載的類的數(shù)量來估算本機(jī)內(nèi)存用量。請考慮使用開源Java 內(nèi)存計算器[7]來估算內(nèi)存需求。譯者注:同樣也可以使用 alibaba 開源的 arthas[8]

          關(guān)閉優(yōu)化編譯器

          默認(rèn)情況下,JVM 有多個階段的 JIT 編譯。雖然這些階段可以逐漸提高應(yīng)用的效率,但它們也會增加內(nèi)存使用的開銷,并增加啟動時間。

          對于短期運行的serverless(無服務(wù))應(yīng)用(例如函數(shù)),請考慮關(guān)閉此優(yōu)化,以犧牲長期效率換取更短的啟動時間。

          對于 Cloud Run 服務(wù),請配置以下環(huán)境變量:

          JAVA_TOOL_OPTIONS="-XX:+TieredCompilation -XX:TieredStopAtLevel=1"

          關(guān)閉類加載驗證

          當(dāng) JVM 將類加載到內(nèi)存中以供執(zhí)行時,它會驗證該類未被篡改并且沒有惡意修改或損壞。如果您完全信任容器鏡像中的字節(jié)碼,并且您的應(yīng)用未從任意遠(yuǎn)程來源加載類,則您可以考慮關(guān)閉驗證。如果在啟動時加載大量類,則關(guān)閉驗證可能會提高啟動速度。

          對于 Cloud Run 服務(wù),請配置以下環(huán)境變量:

          JAVA_TOOL_OPTIONS="-noverify"

          注意此選項已被 OpenJDK 13 及更高版本棄用[9]

          減小線程棧大小

          大多數(shù) Java Web 應(yīng)用都是基于每個連接一個線程的模式。每個 Java 線程都會消耗本機(jī)內(nèi)存(而不是堆內(nèi)存)。這稱為線程棧,并且每個線程默認(rèn)為 1 MB。如果您的應(yīng)用處理 80 個并發(fā)請求,則它可能至少有 80 個線程,這相當(dāng)于使用了 80 MB 的線程棧空間。該內(nèi)存不計入堆大小。默認(rèn)值可能大于必要值。您可以減小線程棧大小。

          如果減小得太多,則將出現(xiàn)java.lang.StackOverflowError。您可以對應(yīng)用進(jìn)行分析,并找到要配置的最佳線程棧大小。

          對于 Cloud Run 服務(wù),請配置以下環(huán)境變量:

          JAVA_TOOL_OPTIONS="-Xss256k"

          減少線程

          您可以通過使用非阻塞反應(yīng)式和避免無效后臺進(jìn)程來減少線程數(shù)量,從而優(yōu)化內(nèi)存。

          減少線程數(shù)量

          由于線程棧,每個 Java 線程都可能會增加內(nèi)存用量。Cloud Run 允許最多 80 個并發(fā)請求。使用每個連接一個線程模式時,您最多需要 80 個線程來處理所有并發(fā)請求。大多數(shù) Web 服務(wù)器和框架都允許您配置線程數(shù)和連接數(shù)上限。例如,在 Spring Boot 中,您可以在applications.properties文件中設(shè)置最大連接數(shù):

          server.tomcat.max-threads=80

          編寫非阻塞反應(yīng)式代碼以優(yōu)化內(nèi)存和啟動

          要真正減少線程數(shù)量,請考慮采用非阻塞反應(yīng)式編程模型,以便在處理更多并發(fā)請求時可以顯著減少線程數(shù)量。Spring Boot Webflux、MicrosoftNavt 和 Quarkus 等應(yīng)用框架支持反應(yīng)式 Web 應(yīng)用。

          Spring Boot Webflux、Micronaut、Quarkus 等反應(yīng)式框架通常具有更快的啟動時間。

          如果您繼續(xù)在非阻塞框架中寫入阻塞代碼,則 Cloud Run 服務(wù)中的吞吐量和錯誤率會顯著惡化。這是因為非阻塞框架將只有幾個線程,例如 2 或 4。如果您的代碼被阻塞,則僅可以處理極少的并發(fā)請求。

          這些非阻塞框架還可以將阻塞代碼分流到無界限線程池,這意味著,雖然它可以接受許多并發(fā)請求,但阻塞代碼將在新線程中執(zhí)行。如果線程以無界限的方式累積,則會耗盡 CPU 資源并開始抖動。延遲時間將受到嚴(yán)重影響。

          譯者注:如果使用非阻塞框架,請務(wù)必了解線程池模型,不合理的代碼將會帶來災(zāi)難

          避免后臺活動進(jìn)程

          當(dāng)該實例不再收到請求時,Cloud Run 會限制實例 CPU[10]。具有傳統(tǒng)任務(wù)的傳統(tǒng)工作負(fù)載在 Cloud Run 中運行時需要特別注意。

          例如,如果您要收集應(yīng)用指標(biāo)并在后臺批處理指標(biāo)以進(jìn)行定期發(fā)送,則在 CPU 受到限制時,這些指標(biāo)不會發(fā)送。如果您的應(yīng)用不斷收到請求,您可能會看到較少的問題。如果您的應(yīng)用具有較低的 QPS,則后臺任務(wù)可能永遠(yuǎn)不會執(zhí)行。

          以下是您需要注意的一些在后臺運行的常見模式:

          • JDBC 連接池 - 清理和連接檢查在后臺進(jìn)行
          • 分布式跟蹤記錄發(fā)送器 - 分布式跟蹤記錄通常會定期或在后臺緩沖區(qū)已滿時進(jìn)行批處理和發(fā)送。
          • 指標(biāo)發(fā)送器 - 指標(biāo)通常會在后臺進(jìn)行批量處理和發(fā)送。
          • 對于 Spring Boot,任何帶有@Async注釋的方法
          • 計時器 - 任何基于計時器的觸發(fā)器(例如,ScheduledThreadPoolExecutor、Quartz 或@ScheduledSpring 注釋)可能無法在 CPU 受到限制時執(zhí)行。
          • 消息接收器 - 例如,Pub/Sub 流式拉取客戶端、JMS 客戶端或 Kafka 客戶端,通常在后臺線程中運行,無需請求。當(dāng)您的應(yīng)用沒有請求時,它們將不起作用。在 Cloud Run 中不建議以這種方式接收消息。

          應(yīng)用優(yōu)化

          在 Cloud Run 服務(wù)代碼中,您也可以進(jìn)行優(yōu)化以減少啟動時間和內(nèi)存使用量。

          減少啟動任務(wù)

          傳統(tǒng)的 Java Web 應(yīng)用會在啟動期間完成許多任務(wù),例如預(yù)加載數(shù)據(jù)、預(yù)熱緩存、建立連接池等。依次執(zhí)行這些任務(wù)會很慢。但是,如果您希望它們并行執(zhí)行,則應(yīng)增加 CPU 核心數(shù)。

          Cloud Run 目前會發(fā)送一個實際用戶請求以觸發(fā)冷啟動實例。其請求被分配到新啟動實例的用戶可能會遇到較長的延遲。Cloud Run 目前沒有“就緒”檢查來避免向未就緒的應(yīng)用發(fā)送請求。

          使用連接池

          如果您使用連接池,請注意,連接池可能會在后臺逐出不需要的連接(請參閱避免后臺任務(wù)[11])。如果應(yīng)用的 QPS 較低,并且可以容忍高延遲,請考慮為每個請求打開和關(guān)閉連接。如果應(yīng)用的 QPS 較高,則只要存在活躍請求,后臺就可能會繼續(xù)執(zhí)行。

          在這兩種情況下,應(yīng)用的數(shù)據(jù)庫訪問都將在數(shù)據(jù)庫允許的連接數(shù)上限方面遭遇瓶頸。計算每個 Cloud Run 實例可建立的最大連接數(shù),并配置 Cloud Run 實例數(shù)上限[12],以使實例數(shù)上限與每個實例的連接數(shù)的乘積小于允許的連接數(shù)上限。

          使用 Spring Boot

          如果您使用 Spring Boot,則需要考慮以下優(yōu)化

          使用 Spring Boot 2.2 或更高版本

          從 2.2 版開始,Spring Boot 已針對啟動速度進(jìn)行了大量優(yōu)化。如果您使用的是低于 2.2 版的 Spring Boot,請考慮升級或手動應(yīng)用各項優(yōu)化[13]

          使用延遲初始化

          在 Spring Boot 2.2 及更高版本中,可以開啟一個全局延遲初始化標(biāo)志。這將提高啟動速度,但代價是第一個請求的延遲時間可能變長,因為需要等待組件首次初始化。

          您可以在application.properties中開啟延遲初始化:

          spring.main.lazy-initialization=true

          或者,使用以下環(huán)境變量:

          SPRING_MAIN_LAZY_INITIATIALIZATION=true

          但是,如果您使用的是 min-instances,由于 min-instance 啟動時應(yīng)已執(zhí)行了初始化,因此延遲初始化沒有什么用處。

          避免類掃描

          類掃描會在 Cloud Run 中導(dǎo)致額外的磁盤讀取,因為在 Cloud Run 中,磁盤訪問速度通常比常規(guī)機(jī)器慢。請確保進(jìn)行有限的組件掃描或完全不進(jìn)行組件掃描。考慮使用Spring Context Indexer來預(yù)生成索引。這是否會提高啟動速度取決于您的應(yīng)用。

          例如,在 Mavenpom.xml中添加索引器依賴項(實際上是注釋處理器):

          <dependency>
          ??<groupId>org.springframeworkgroupId>
          ??<artifactId>spring-context-indexerartifactId>
          ??<optional>trueoptional>
          dependency>

          不要在生產(chǎn)環(huán)境使用 Spring Boot Devtools

          如果您在開發(fā)過程中使用Spring Boot Devtools[14],請確保未將其打包到生產(chǎn)容器鏡像中。如果您在沒有 Spring Boot 構(gòu)建插件(例如,使用 Shade 插件或使用 Jib 進(jìn)行容器化)的情況下構(gòu)建 Spring Boot 應(yīng)用,則可能會發(fā)生這種情況。

          在這種情況下,請確保構(gòu)建工具排除 Spring Boot Devtools。或者關(guān)閉 Spring Boot Devtools[15]

          后續(xù)步驟

          如需獲得更多提示,請參閱

          • 如何高效的進(jìn)行 Java 優(yōu)化[16]
          • 如何遷移現(xiàn)有服務(wù)[17]

            翻譯:冷冷、如夢技術(shù)(DreamLu)

          原文鏈接:https://cloud.google.com/run/docs/tips/java#appcds[18]

          參考資料

          [1]

          常規(guī)優(yōu)化提示: https://cloud.google.com/run/docs/tips

          [2]

          《如何優(yōu)化構(gòu)建鏡像》: https://cloud.google.com/run/docs/tips#minimize-container

          [3]

          Distroless Java 基礎(chǔ): https://github.com/GoogleContainerTools/distroless/tree/master/java

          [4]

          使用 Jib 構(gòu)建容器: https://cloud.google.com/java/getting-started/jib

          [5]

          Spring boot 2.3 的新特性分層 JAR: https://juejin.im/post/6844904167710916615

          [6]

          Jib 插件: https://github.com/GoogleContainerTools/jib

          [7]

          Java 內(nèi)存計算器: https://github.com/cloudfoundry/java-buildpack-memory-calculator

          [8]

          arthas: https://alibaba.github.io/arthas/

          [9]

          此選項已被 OpenJDK 13 及更高版本棄用: https://www.oracle.com/java/technologies/javase/13all-relnotes.html

          [10]

          限制實例 CPU: https://cloud.google.com/run/docs/reference/container-contract#cpu-request

          [11]

          避免后臺任務(wù): https://cloud.google.com/run/docs/tips/java#background

          [12]

          配置 Cloud Run 實例數(shù)上限: https://cloud.google.com/run/docs/configuring/max-instances

          [13]

          手動應(yīng)用各項優(yōu)化: https://spring.io/blog/2018/12/12/how-fast-is-spring

          [14]

          Spring Boot Devtools: https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-devtools

          [15]

          關(guān)閉 Spring Boot Devtools: https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-devtools

          [16]

          如何高效的進(jìn)行 Java 優(yōu)化: https://cloud.google.com/run/docs/tips/general

          [17]

          如何遷移現(xiàn)有服務(wù): https://cloud.google.com/run/docs/migrating

          [18]

          https://cloud.google.com/run/docs/tips/java#appcds: https://cloud.google.com/run/docs/tips/java#appcds


          瀏覽 52
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  午夜操一操网 | 五月婷婷深夜影院 | 殴美肏屄视频免费看 | www.三级| 这里只有精品2001 |