<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>

          性能問題從發(fā)現(xiàn)到優(yōu)化一般思路

          共 18250字,需瀏覽 37分鐘

           ·

          2022-08-12 00:11


          JAVA前線 


          歡迎大家關(guān)注公眾號(hào)「JAVA前線」查看更多精彩分享,主要內(nèi)容包括源碼分析、實(shí)際應(yīng)用、架構(gòu)思維、職場(chǎng)分享、產(chǎn)品思考等等,同時(shí)也非常歡迎大家加我微信「java_front」一起交流學(xué)習(xí)




          1 文章概述

          技術(shù)系統(tǒng)有一個(gè)發(fā)展過程,在業(yè)務(wù)初期主要是實(shí)現(xiàn)業(yè)務(wù)功能和目標(biāo),由于數(shù)據(jù)和訪問量都不大,所以性能問題并不作為首要考慮。

          但是隨著業(yè)務(wù)發(fā)展,隨著數(shù)據(jù)和訪問量增大甚至激增,造成例如首頁五秒鐘才能展示等問題,這種不佳體驗(yàn)會(huì)造成用戶流失,此時(shí)性能就是必須面對(duì)之問題。我們把技術(shù)系統(tǒng)分為早期、中期、后期三個(gè)階段:

          • 早期:主要實(shí)現(xiàn)業(yè)務(wù)需求,性能非重點(diǎn)考慮
          • 中期:性能問題注解顯現(xiàn),影響業(yè)務(wù)發(fā)展
          • 后期:技術(shù)迭代性能與業(yè)務(wù)必須同時(shí)考慮

          如何發(fā)現(xiàn)性能問題,并且最終如何解決性能問題就是本文討論之要點(diǎn)。


          2 什么是性能

          我們可以從四個(gè)維度介紹什么是性能:

          • 兩個(gè)維度定義性能:
            • 速度慢
            • 壓力大
          • 兩個(gè)維度描述性能:
            • 定性:直觀感受
            • 定量:指標(biāo)分析

          3 發(fā)現(xiàn)性能問題

          3.1 定性 + 速度

          一個(gè)頁面需要長時(shí)間打開,一個(gè)列表很慢才能加載完成,一個(gè)接口訪問導(dǎo)致超時(shí)異常,這些顯而易見之問題可以歸為此類。


          3.2 定量 + 速度

          3.2.1 速度指標(biāo)

          一個(gè)公司有7200名員工,每天上班打卡時(shí)間是早上8點(diǎn)到8點(diǎn)30分,每次打卡時(shí)間系統(tǒng)執(zhí)行時(shí)長5秒,那么RT、QPS、并發(fā)量分別是多少?

          RT表示響應(yīng)時(shí)間,問題已經(jīng)包含答案:

          • RT = 5秒

          QPS表示每秒訪問量,假設(shè)行為平均分布:

          • QPS = 7200 / (30 * 60) = 4

          并發(fā)量表示系統(tǒng)同時(shí)處理請(qǐng)求數(shù):

          • 并發(fā)量 = QPS x RT = 4 x 5 = 20

          根據(jù)上述實(shí)例引出公式:

          • 并發(fā)量 = QPS x RT

          3.2.2 QPS VS TPS

          QPS(Queries Per Second):每秒查詢量

          TPS(Transactions Per Second):每秒事務(wù)數(shù)

          需要注意此事務(wù)并不是指數(shù)據(jù)庫事務(wù),而是包括以下三個(gè)階段:

          • 接收請(qǐng)求
          • 處理業(yè)務(wù)
          • 返回結(jié)果
          QPS = N * TPS (N>=1)

          N=1表示接口有一個(gè)事務(wù):

          public class OrderService {

              public Order queryOrderById(String orderId) {
                  return orderMapper.selectById(orderId);
              }
          }

          N>1表示接口有多個(gè)事務(wù):

          public class OrderService {

              public void updateOrder(Order order) {
                  // transaction1
                  orderMapper.update(order);
                  // transaction2
                  sendOrderUpdateMessage(order);
              }
          }

          3.2.3 發(fā)現(xiàn)問題

          (1) 打印日志

          public class FastTestService {

              public void test01() {
                  long start = System.currentTimeMillis();
                  biz1();
                  biz2();
                  long costTime = System.currentTimeMillis() - start;
                  System.out.println("costTime=" + costTime);
              }

              private void biz1() {
                  try {
                      System.out.println("biz1");
                      Thread.sleep(500L);
                  } catch (Exception ex) {
                      log.error("error", ex);
                  }
              }

              private void biz2() {
                  try {
                      System.out.println("biz2");
                      Thread.sleep(1000L);
                  } catch (Exception ex) {
                      log.error("error", ex);
                  }
              }
          }

          (2) StopWatch

          import org.springframework.util.StopWatch;
          import org.springframework.util.StopWatch.TaskInfo;

          public class FastTestService {

              public void test02() {
                  StopWatch sw = new StopWatch("testWatch");
                  sw.start("biz1");
                  biz1();
                  sw.stop();

                  sw.start("biz2");
                  biz2();
                  sw.stop();

                  // 簡單輸出耗時(shí)
                  System.out.println("costTime=" + sw.getTotalTimeMillis());
                  System.out.println();

                  // 輸出任務(wù)信息
                  TaskInfo[] taskInfos = sw.getTaskInfo();
                  for (TaskInfo task : taskInfos) {
                      System.out.println("taskInfo=" + JSON.toJSONString(task));
                  }
                  System.out.println();

                  // 格式化任務(wù)信息
                  System.out.println(sw.prettyPrint());
              }
          }

          輸出結(jié)果:

          costTime=1526

          taskInfo={"taskName":"biz1","timeMillis":510,"timeNanos":510811200,"timeSeconds":0.5108112}
          taskInfo={"taskName":"biz2","timeMillis":1015,"timeNanos":1015439700,"timeSeconds":1.0154397}

          StopWatch 'testWatch': running time = 1526250900 ns
          ---------------------------------------------
          ns         %     Task name
          ---------------------------------------------
          510811200  033%  biz1
          1015439700  067%  biz2

          (3) trace

          Arthas是阿里開源Java診斷工具:

          Arthas是一款線上監(jiān)控診斷產(chǎn)品,通過全局視角實(shí)時(shí)查看應(yīng)用 load、內(nèi)存、gc、線程的狀態(tài)信息,并能在不修改應(yīng)用代碼的情況下,對(duì)業(yè)務(wù)問題進(jìn)行診斷,包括查看方法調(diào)用的出入?yún)ⅰ惓#O(jiān)測(cè)方法執(zhí)行耗時(shí),類加載信息等,大大提升線上問題排查效率

          Arthas trace命令監(jiān)控鏈路每個(gè)節(jié)點(diǎn)耗時(shí):

          https://arthas.aliyun.com/doc/trace.html

          我們通過實(shí)例說明,首先編寫并運(yùn)行代碼:

          package java.front.optimize;

          public class FastTestService {

              public static void main(String[] args) {
                  FastTestService service = new FastTestService();
                  while (true) {
                      service.test03();
                  }
              }

              public void test03() {
                  biz1();
                  biz2();
              }

              private void biz1() {
                  try {
                      System.out.println("biz1");
                      Thread.sleep(500L);
                  } catch (Exception ex) {
                      log.error("error", ex);
                  }
              }

              private void biz2() {
                  try {
                      System.out.println("biz2");
                      Thread.sleep(1000L);
                  } catch (Exception ex) {
                      log.error("error", ex);
                  }
              }
          }

          第一步進(jìn)入arthas控制臺(tái):

          $ java -jar arthas-boot.jar
          [INFO] arthas-boot version: 3.6.2
          [INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER
          * [1]: 14121
            [2]: 20196 java.front.optimize.FastTestService

          第二步輸入監(jiān)控進(jìn)程號(hào)并回車:

          2

          第三步trace命令監(jiān)控相應(yīng)方法:

          trace java.front.optimize.FastTestService test03

          第四步查看鏈路耗時(shí):

          `---[1518.7362ms] java.front.optimize.FastTestService:test03()
              +---[33.66% 511.2817ms ] java.front.optimize.FastTestService:biz1() #54
              `---[66.32% 1007.2962ms ] java.front.optimize.FastTestService:biz2() #55

          3.3 定性 + 壓力

          系統(tǒng)壓力大也會(huì)表現(xiàn)出速度慢之特征,但是這種慢不僅僅是需要幾秒后才能打開網(wǎng)頁,而是網(wǎng)頁一直處于加載狀態(tài)最終白屏。


          3.4 定量 + 壓力

          服務(wù)器常見壓力指標(biāo)如下:

          • 內(nèi)存
          • CPU
          • 磁盤
          • 網(wǎng)絡(luò)

          服務(wù)端開發(fā)比較容易引發(fā)內(nèi)存和CPU問題,所以我們重點(diǎn)關(guān)注。


          3.4.1 發(fā)現(xiàn)CPU問題

          首先編寫一段造成CPU飆高之代碼并運(yùn)行:

          public class FastTestService {

              public static void main(String[] args) {
                  FastTestService service = new FastTestService();
                  while (true) {
                      service.test();
                  }
              }

              public void test() {
                  biz();
              }

              private void biz() {
                  System.out.println("biz");
              }
          }

          (1) dashboard + thread

          dashboard查看當(dāng)前系統(tǒng)實(shí)時(shí)面板,發(fā)現(xiàn)線程ID=1 CPU占用非常高(這個(gè)ID不可以與jstack nativeID相對(duì)應(yīng)):

          $ dashboard
          ID    NAME    GROUP  PRIORI   STATE    %CPU   DELTA TIME   TIME    INTERRU   DAEMON
          1     main    main     5      RUNNA    96.06    4.812     2:41.2    false    false

          thread查看最忙前N個(gè)線程:

          $ thread -n 1

          "main" Id=1 deltaTime=203ms time=1714000ms RUNNABLE
              at app//java.front.optimize.FastTestService.biz(FastTestService.java:83)
              at app//java.front.optimize.FastTestService.test(FastTestService.java:61)
              at app//java.front.optimize.FastTestService.main(FastTestService.java:17)

          3.4.2 發(fā)現(xiàn)內(nèi)存問題

          (1) free

          $ free -h
                        total        used        free      shared  buff/cache   available
          Mem:            10G        5.5G        3.1G         28M        1.4G        4.4G
          Swap:          2.0G        435M        1.6G

          total
          服務(wù)器總內(nèi)存

          used
          已使用內(nèi)存

          free
          未被任何應(yīng)用使用空閑內(nèi)存

          shared
          被共享物理內(nèi)存

          cache
          IO設(shè)備讀緩存(Page Cache)

          buff
          IO設(shè)備寫緩存(Buffer Cache)

          available
          可以被程序應(yīng)用之內(nèi)存

          (2) memory

          Arthas memory命令查看JVM內(nèi)存信息:

          https://arthas.aliyun.com/doc/heapdump.html
          • 查看JVM內(nèi)存信息(官方實(shí)例)
          $ memory
          Memory                           used      total      max        usage
          heap                             32M       256M       4096M      0.79%
          g1_eden_space                    11M       68M        -1         16.18%
          g1_old_gen                       17M       184M       4096M      0.43%
          g1_survivor_space                4M        4M         -1         100.00%
          nonheap                          35M       39M        -1         89.55%
          codeheap_'non-nmethods'          1M        2M         5M         20.53%
          metaspace                        26M       27M        -1         96.88%
          codeheap_'profiled_nmethods'     4M        4M         117M       3.57%
          compressed_class_space           2M        3M         1024M      0.29%
          codeheap_'non-profiled_nmethods' 685K      2496K      120032K    0.57%
          mapped                           0K        0K         -          0.00%
          direct                           48M       48M        -          100.00%

          (3) jmap

          • 查看JAVA程序進(jìn)程號(hào)
          jps -l
          • 查看實(shí)時(shí)內(nèi)存占用
          jhsdb jmap --heap --pid 20196
          • 導(dǎo)出快照文件
           jmap -dump:format=b,file=/home/tmp/my-dump.hprof 20196
          • 內(nèi)存溢出自動(dòng)導(dǎo)出堆快照
          -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath==/home/tmp/my-dump.hprof

          (4) heapdump

          Arthas heapdump命令支持導(dǎo)出堆快照:

          https://arthas.aliyun.com/doc/heapdump.html
          • dump至指定文件
          heapdump /home/tmp/my-dump.hprof
          • dump live對(duì)象至指定文件
          heapdump --live /home/tmp/my-dump.hprof
          • dump至臨時(shí)文件
          heapdump

          (5) 垃圾回收

          jstat可以查看垃圾回收情況,觀察程序是否頻繁GC或者GC用時(shí)是否過長:

          jstat -gcutil <pid> <interval(ms)>
          • 每秒查看垃圾回收情況
          $ jstat -gcutil 20196 1000
            S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT    CGC    CGCT     GCT
            0.00   0.00  57.69   0.00      -      -      0    0.000     0    0.000     0    0.000    0.000
            0.00   0.00  57.69   0.00      -      -      0    0.000     0    0.000     0    0.000    0.000
            0.00   0.00  57.69   0.00      -      -      0    0.000     0    0.000     0    0.000    0.000

          各參數(shù)說明如下:

          S0:新生代中Survivor 0區(qū)占已使用空間比例

          S1:新生代中Survivor 1區(qū)占已使用空間比例

          E:新生代占已使用空間比例

          O:老年代占已使用空間比例

          P:永久帶占已使用空間比例

          YGC:應(yīng)用程序啟動(dòng)至今,發(fā)生Young GC次數(shù)

          YGCT:應(yīng)用程序啟動(dòng)至今,Young GC所用時(shí)間(秒)

          FGC:應(yīng)用程序啟動(dòng)至今,發(fā)生Full GC次數(shù)

          FGCT:應(yīng)用程序啟動(dòng)至今,F(xiàn)ull GC所用時(shí)間(秒)

          GCT:應(yīng)用程序啟動(dòng)至今,所用垃圾回收總時(shí)間(秒)

          3.4.3 綜合發(fā)現(xiàn)問題

          (1) 壓力測(cè)試

          進(jìn)行系統(tǒng)壓測(cè)可以主動(dòng)暴露系統(tǒng)問題,評(píng)估系統(tǒng)容量,簡單常用參數(shù)如下:

          • 常用工具:JMeter
          • 階梯發(fā)壓線程數(shù)10、20、30遞增至瓶頸
          • 持續(xù)時(shí)間:持續(xù)1分鐘,Ramp-Up=0
          • TPS:Throughput
          • 響應(yīng)時(shí)間:重點(diǎn)關(guān)注95Line

          (2) 監(jiān)控系統(tǒng)

          監(jiān)控系統(tǒng)可以更加友好展示相關(guān)指標(biāo),如果公司具有一定技術(shù)實(shí)力可以自研,否則可以選擇使用業(yè)界通用方案。


          4 優(yōu)化性能問題

          4.1 四個(gè)方法

          • 減少請(qǐng)求

          • 空間換時(shí)間

          • 任務(wù)并行化

          • 任務(wù)異步化


          4.2 五個(gè)層面

          • 代理層
          • 前端層
          • 服務(wù)層
          • 緩存層
          • 數(shù)據(jù)層

          4.3 優(yōu)化說明

          一說到性能優(yōu)化不難想到例如加索引、加緩存等方案,這也許是正確的,但是這樣思考可能會(huì)造成遺漏,因?yàn)檫@只是緩存層和數(shù)據(jù)層的方案。

          如果可以將無效流量在最外層拒絕,那么這是對(duì)系統(tǒng)更好地好保護(hù)。四個(gè)方法可以應(yīng)用在每一個(gè)層面,我們不妨舉一些例子:


          (1) 減少請(qǐng)求 + 前端層

          在秒殺場(chǎng)景中設(shè)置前置驗(yàn)證碼


          (2) 減少請(qǐng)求 + 服務(wù)層

          多次RPC是否可以轉(zhuǎn)換為一次批量RPC


          (3) 空間換時(shí)間 + 服務(wù)層

          引入緩存


          (4) 空間換時(shí)間 + 緩存層

          引入多級(jí)緩存


          (5) 空間換時(shí)間 + 數(shù)據(jù)層

          新增索引


          (6) 任務(wù)并行化 + 服務(wù)層

          如果多個(gè)調(diào)用互不依賴,使用Future并行化


          (7) 任務(wù)異步化 + 服務(wù)層

          如果無需等待返回結(jié)果,可以異步執(zhí)行


          5 文章總結(jié)

          第一本文討論了系統(tǒng)早期、中期、后期如何看待性能問題,第二討論了什么是性能,第三討論了如果發(fā)現(xiàn)性能問題,第四討論了如何優(yōu)化性能問題,希望本文對(duì)大家有所幫助。




          JAVA前線 


          歡迎大家關(guān)注公眾號(hào)「JAVA前線」查看更多精彩分享,主要內(nèi)容包括源碼分析、實(shí)際應(yīng)用、架構(gòu)思維、職場(chǎng)分享、產(chǎn)品思考等等,同時(shí)也非常歡迎大家加我微信「java_front」一起交流學(xué)習(xí)


          瀏覽 37
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  月亚洲综合在线 | 美女黄色操逼网站 | 久久久久逼 | 黄色无码视频在线客服 | 大鸡巴久久 |