<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                本課時主要講解如何在大流量高并發場景下進行估算和調優。 我們知道,垃圾回收器一般使用默認參數,就可以比較好的運行。但如果用錯了某些參數,那么后果可能會比較嚴重,我不只一次看到有同學想要驗證某個剛剛學到的優化參數,結果引起了線上 GC 的嚴重問題。 所以你的應用程序如果目前已經滿足了需求,那就不要再隨便動這些參數了。另外,優化代碼獲得的性能提升,遠遠大于參數調整所獲得的性能提升,你不要純粹為了調參數而走了彎路。 那么,GC 優化有沒有可遵循的一些規則呢?這些“需求”又是指的什么?我們可以將目標歸結為三點: 1. 系統容量(Capacity) 2. 延遲(Latency) 3. 吞吐量(Throughput) #### 考量指標 ![](https://img.kancloud.cn/20/9d/209da0d137f94c18afab804df82dd179_758x263.jpg) #### 系統容量 系統容量其實非常好理解。比如,領導要求你每個月的運維費用不能超過 x 萬,那就決定了你的機器最多是 2C4G 的。 舉個比較極端的例子。假如你的內存是無限大的,那么無論是存活對象,還是垃圾對象,都不需要額外的計算和回收,你只需要往里放就可以了。這樣,就沒有什么吞吐量和延遲的概念了。 但這畢竟是我們的一廂情愿。越是資源限制比較嚴格的系統,對它的優化就會越明顯。通常在一個資源相對寬松的環境下優化的參數,平移到另外一個限制資源的環境下,并不是最優解。 #### 吞吐量-延遲 接下來我們看一下吞吐量和延遲方面的概念。 假如你開了一個面包店,你的首要目標是賣出更多的面包,因為賺錢來說是最要緊的。 為了讓客人更快買到面包,你引進了很多先進的設備,使得制作面包的間隔減少到 30 分鐘,一批面包可以有 100 個。 工人師傅是拿工資的,并不想和你一樣加班。按照一天 8 小時工作制,每天就可以制作 8x2x100=1600 個面包。 但是你很不滿意,因為每天的客人都很多,需求大約是 2000 個面包。 你只好再引進更加先進的設備,這種設備可以一次做出 200 個面包,一天可以做 2000~3000 個面包,但是每運行一段時間就需要冷卻一會兒。 原來每個客人最多等 30 分鐘就可以拿到面包,現在有的客人需要等待 40 分鐘。客人通常受不了這么長的等待時間,第二天就不來了。 考慮到我們的營業目標,就可以抽象出兩個概念。 * 吞吐量,也就是每天制作的面包數量。 * 延遲,也就是等待的時間,涉及影響顧客的滿意度。 ![](https://img.kancloud.cn/3d/b3/3db3015ebb2bd750819fd9322183f8ac_758x404.jpg) ? ? ? ?? ? ? 吞吐量大不代表響應能力高,吞吐量一般這么描述:在一個時間段內完成了多少個事務操作;在一個小時之內完成了多少批量操作。 響應能力是以最大的延遲時間來判斷的,比如:一個桌面按鈕對一個觸發事件響應有多快;需要多長時間返回一個網頁;查詢一行 SQL 需要多長時間,等等。 這兩個目標,在有限的資源下,通常不能夠同時達到,我們需要做一些權衡。 #### 選擇垃圾回收器 接下來,再回顧一下前面介紹的垃圾回收器,簡單看一下它們的應用場景。 * 如果你的堆大小不是很大(比如 100MB),選擇串行收集器一般是效率最高的。參數:-XX:+UseSerialGC。 * 如果你的應用運行在單核的機器上,或者你的虛擬機核數只有 1C,選擇串行收集器依然是合適的,這時候啟用一些并行收集器沒有任何收益。參數:-XX:+UseSerialGC。 * 如果你的應用是“吞吐量”優先的,并且對較長時間的停頓沒有什么特別的要求。選擇并行收集器是比較好的。參數:-XX:+UseParallelGC。 * 如果你的應用對響應時間要求較高,想要較少的停頓。甚至 1 秒的停頓都會引起大量的請求失敗,那么選擇 G1、ZGC、CMS 都是合理的。雖然這些收集器的 GC 停頓通常都比較短,但它需要一些額外的資源去處理這些工作,通常吞吐量會低一些。參數:-XX:+UseConcMarkSweepGC、-XX:+UseG1GC、-XX:+UseZGC 等。 從上面這些出發點來看,我們平常的 Web 服務器,都是對響應性要求非常高的。選擇性其實就集中在 CMS、G1、ZGC 上。 而對于某些定時任務,使用并行收集器,是一個比較好的選擇。 #### 大流量應用特點 這是一類對延遲非常敏感的系統。吞吐量一般可以通過堆機器解決。 如果一項業務有價值,客戶很喜歡,那億級流量很容易就能達到了。假如某個接口一天有 10 億次請求,每秒的峰值大概也就 5~6 w/秒,雖然不算是很大,但也不算小。最直接的影響就是:可能你發個版,幾萬用戶的請求就抖一抖。 一般達到這種量級的系統,承接請求的都不是一臺服務器,接口都會要求快速響應,一般不會超過 100ms。 這種系統,一般都是社交、電商、游戲、支付場景等,要求的是短、平、快。長時間停頓會堆積海量的請求,所以在停頓發生的時候,表現會特別明顯。我們要考量這些系統,有很多指標。 * 每秒處理的事務數量(TPS); * 平均響應時間(AVG); * TP 值,比如 TP90 代表有 90% 的請求響應時間小于 x 毫秒。 可以看出來,它和 JVM 的某些指標很像。 尤其是 TP 值,最能代表系統中到底有多少長尾請求,這部分請求才是影響系統穩定性的元兇。大多數情況下,GC 增加,長尾請求的數量也會增加。 我們的目標,就是減少這些停頓。本課時假定使用的是 CMS 垃圾回收器。 #### 估算 在《編程珠璣》第七章里,將估算看作程序員的一項非常重要的技能。這是一種化繁為簡的能力,不要求極度精確,但對問題的分析有著巨大的幫助。 拿一個簡單的 Feed 業務來說。查詢用戶在社交網站上發送的帖子,還需要查詢第一頁的留言(大概是 15 條),它們共同組成了每次查詢后的實體。 ``` class?Feed{ ???private?User?user; ???private?List<Comment>?commentList; ???private?String?content; } ``` 這種類型的數據結構,一般返回體都比較大,大概會有幾 KB 到幾十 KB 不等。我們就可以對這些數據進行以大體估算。具體的數據來源可以看日志,也可以分析線上的請求。 ![](https://img.kancloud.cn/b9/55/b9553016b39fce2db1805273d81b2cf0_757x403.jpg) 這個接口每天有 10 億次請求,假如每次請求的大小有 20KB(很容易達到),那么一天的流量就有 18TB 之巨。假如高峰請求 6w/s,我們部署了 10 臺機器,那么每個 JVM 的流量就可以達到 120MB/s,這個速度算是比較快的了。 如果你實在不知道怎么去算這個數字,那就按照峰值的 2 倍進行準備,一般都是 OK 的。 #### 調優 問題是這樣的,我們的機器是 4C8GB 的,分配給了 JVM 1024*8GB/3*2= 5460MB 的空間。那么年輕代大小就有 5460MB/3=1820MB。進而可以推斷出,Eden 區的大小約 1456MB,那么大約只需要 12 秒,就會發生一次 Minor GC。不僅如此,每隔半個小時,會發生一次 Major GC。 不管是年輕代還是老年代,這個 GC 頻率都有點頻繁了。 提醒一下,你可以算一下我們的 Survivor 區大小,大約是 182MB 左右,如果稍微有點流量偏移,或者流量突增,再或者和其他接口共用了 JVM,那么這個 Survivor 區就已經裝不下 Minor GC 后的內容了。總有一部分超出的容量,需要老年代來補齊。這些垃圾信息就要保存更長時間,直到老年代空間不足。 ![](https://img.kancloud.cn/27/9b/279b6e51f08e9dd81a8578adab2aaf9e_757x336.jpg) 我們發現,用戶請求完這些信息之后,很快它們就會變成垃圾。所以每次 MinorGC 之后,剩下的對象都很少。 也就是說,我們的流量雖然很多,但大多數都在年輕代就銷毀了。如果我們加大年輕代的大小,由于 GC 的時間受到活躍對象數的影響,回收時間并不會增加太多。 如果我們把一半空間給年輕代。也就是下面的配置: ``` -XX:+UseConcMarkSweepGC -Xmx5460M -Xms5460M -Xmn2730M ``` 重新估算一下,發現 Minor GC 的間隔,由 12 秒提高到了 18 秒。 線上觀察: ``` [ParNew: 2292326K‐>243160K(2795520K), 0.1021743 secs] 3264966K‐>10880154K(1215800K), 0.1021417 secs] [Times: user=0.52 sys=0.02, real=0.2 secs] ``` Minor GC 有所改善,但是并沒有顯著的提升。相比較而言,Major GC 的間隔卻增加到了 3 小時,是一個非常大的性能優化。這就是在容量限制下的初步調優方案。 此種場景,我們可以更加激進一些,調大年輕代(順便調大了幸存區),讓對象在年輕代停留的時間更長一些,有更多的 buffer 空間。這樣 Minor GC 間隔又可以提高到 23 秒。參數配置: ``` -XX:+UseConcMarkSweepGC -Xmx5460M -Xms5460M -Xmn3460M ``` 一切看起來很美好,但還是有一個瑕疵。 問題如下:由于每秒的請求都非常大,如果應用重啟或者更新,流量瞬間打過來,JVM 還沒預熱完畢,這時候就會有大量的用戶請求超時、失敗。 為了解決這種問題,通常會逐步的把新發布的機器進行放量預熱。比如第一秒 100 請求,第二秒 200 請求,第三秒 5000 請求。大型的應用都會有這個預熱過程。 ![](https://img.kancloud.cn/2e/96/2e9653831bae8e2c3f31f7b87ac15360_757x377.jpg) 如圖所示,負載均衡器負責服務的放量,server4 將在 6 秒之后流量正常流通。但是奇怪的是,每次重啟大約 20 多秒以后,就會發生一次詭異的 Full GC。 注意是 Full GC,而不是老年代的 Major GC,也不是年輕代的 Minor GC。 事實上,經過觀察,此時年輕代和老年代的空間還有很大一部分,那 Full GC 是怎么產生的呢? 一般,Full GC 都是在老年代空間不足的時候執行。但不要忘了,我們還有一個區域叫作 Metaspace,它的容量是沒有上限的,但是每當它擴容時,就會發生 Full GC。 使用下面的命令可以看到它的默認值: ``` java -XX:+PrintFlagsFinal 2>&1 | grep Meta ``` 默認值如下: ``` size_t MetaspaceSize = 21807104 ? ? ?{pd product} {default} size_t MaxMetaspaceSize = 18446744073709547520 ? ? ?{product} {default} ``` 可以看到 MetaspaceSize 的大小大約是 20MB。這個初始值太小了。 現在很多類庫,包括 Spring,都會大量生成一些動態類,20MB 很容易就超了,我們可以試著調大這個數值。 按照經驗,一般調整成 256MB 就足夠了。同時,為了避免無限制使用造成操作系統內存溢出,我們同時設置它的上限。配置參數如下: ``` -XX:+UseConcMarkSweepGC -Xmx5460M -Xms5460M -Xmn3460M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M ``` 經觀察,啟動后停頓消失。 這種方式通常是行之有效的,但也可以通過擴容機器內存或者擴容機器數量的辦法,顯著地降低 GC 頻率。這些都是在估算容量后的優化手段。 我們把部分機器升級到 8C16GB 的機器,使用如下的參數: ``` -XX:+UseConcMarkSweepGC -Xmx10920M -Xms10920M -Xmn5460M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M ``` 相比較其他實例,系統運行的特別棒,系統平均 1 分鐘左右發生一次 MinorGC,老年代觀察了一天才發生 GC,響應水平明顯提高。 這是一種非常簡單粗暴的手段,但是有效。我們看到,對 JVM 的優化,不僅僅是優化參數本身。我們的目的是解決問題,尋求多種有用手段。 #### 總結 其實,如果沒有明顯的內存泄漏問題和嚴重的性能問題,專門調優一些 JVM 參數是非常沒有必要的,優化空間也比較小。 所以,我們一般優化的思路有一個重要的順序: 1. 程序優化,效果通常非常大; 2. 擴容,如果金錢的成本比較小,不要和自己過不去; 3. 參數調優,在成本、吞吐量、延遲之間找一個平衡點。 本課時主要是在第三點的基礎上,一步一步地增加 GC 的間隔,達到更好的效果。 我們可以再加一些原則用以輔助完成優化。 1. 一個長時間的壓測是必要的,通常我們使用 JMeter 工具。 2. 如果線上有多個節點,可以把我們的優化在其中幾個節點上生效。等優化真正有效果之后再全面推進。 3. 優化過程和目標之間可能是循環的,結果和目標不匹配,要推翻重來。? ![](https://img.kancloud.cn/92/90/9290dbf3b7f52501ec363daeec3ed97e_757x335.jpg) 我們的業務場景是高并發的。對象誕生的快,死亡的也快,對年輕代的利用直接影響了整個堆的垃圾收集。 足夠大的年輕代,會增加系統的吞吐,但不會增加 GC 的負擔。 容量足夠的 Survivor 區,能夠讓對象盡可能的留在年輕代,減少對象的晉升,進而減少 Major GC。 我們還看到了一個元空間引起的 Full GC 的過程,這在高并發的場景下影響會格外突出,尤其是對于使用了大量動態類的應用來說。通過調大它的初始值,可以解決這個問題。 #### 課后問答、 * 1、老是, 針對nginx放量有沒有具體算法可以參考? 首次100請求, 第2秒200請求, 以此類推300/400...這是我們邏輯上的, 具體應該在那個地方實現怎么實現呢? 答案:對于nginx,可以使用lua腳本。最笨的辦法,手動改動nginx upstream的weight權重。對springclound來說,可以重寫Robbin的負載均衡策略。自研框架也是,主要是在負載均衡層面進行控制。關于預熱算法,可以參考Guava的RateLimiter,它是線性增長的算法。 * 2、問題是這樣的,我們的機器是 4C8GB 的,分配給了 JVM 1024*8GB/3*2= 5460MB 的空間。那么年輕代大小就有 5460MB/3=1820MB。”這里的JVM 的空間“1024*8GB/3*2 ” 除以3 是為什么? 再乘以 2 是什么意思??意思是說 JVM的總內存是 服務器物理內存的 三分之二??默認的不是 四分之一嗎?請指點,謝謝! 答案:這里不是默認的,是推薦比例。 * 3、老師,調優那部分5460m是通過參數指定的吧,還是根據系統內存默認配置的,另外每半小時majorgc這個時間是怎么估算的啊 答案: 是的,通過手動指定的。這里的MajorGC的頻率不是估算的,是實際觀測的。老年代的估算可以根據年輕代的提升速率算一下(參考GCLOG)。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看