我們該怎麽結合日誌做優化
1.前言
在日常工作中,我們常常會遇到隨著業務的發展,系統的性能逐漸無法滿足業務需求,這個時候就需要系統進行技術改造或者性能調優。技改可能會包括系統的重構甚至重寫,功能的重新劃分,但是只要是找了性能的瓶頸點,也許只是一些jvm參數或者常量的調優。
2.優化
為了尋找系統的性能瓶頸點,如果有一套完整圖形化的監控系統固然是好事,但是很多情況都可能無法滿足這個要求,更多的是通過日誌來完成。因此,在合理的地方打印日誌尤為重要。
上周某日下午,一個線上系統開始打印大量錯誤日誌,查看日誌發現錯誤基本為數據庫唯一鍵沖突,通過分析發現日誌發現該系統(下面以系統A稱呼)的獎勵發放功能在上一個獎罰發放還沒有完成的情況下,又觸發了新的獎勵發放調用,獎勵發放未半小時觸發一次。因為獎勵發放邏輯沒有做並發同步處理,導致數據落庫的時候發生唯一鍵沖突。因為是離線發放邏輯不考慮實時性,而且當前的數據量可以通過單機多線程並發處理,所以一方面通過redis鎖保證同一時間只有一個獎勵發放在處理,另一方面通過進行系統優化,盡可能的降低一次獎勵發放的耗時。
系統A的處理邏輯為,系統A會定時收到系統B的調用,一次消息代表一次獎勵發放任務,一次任務包含多個司機,需要分別對多個司機進行獎勵發放。基本的處理邏輯是系統A在被調用後,獲取任務所需基本信息,分批獲取相關司機,多線程執行發放任務,每一次submit任務後都會sleep50毫秒,最後通過countdown同步等待異步任務處理完成,在整個任務的開始和結束的時候都會打印日誌。為了確認單個任務的耗時,實現了下面的wrapper類
public class MarketingRunnableWrapper implements Runnable { private static final ILog logger = LogFactory.getLog("threadLogger");//任務名 private String taskName; //任務創建時間 private Long taskCreateTime; //日誌flag private String flag;
private Runnable runnable;
public MarketingRunnableWrapper(Runnable runnable) { this(runnable, ""); } public MarketingRunnableWrapper(Runnable runnable, String taskName) { this.runnable = runnable;this.taskName = taskName; this.taskCreateTime = System.nanoTime(); this.flag = LogFactory.getFlag(); } @Override public void run() { Long beginTime = System.nanoTime(); try { runnable.run(); } catch (Exception e) { String msg = "task: " + taskName + " run throw exception"; msgLog(msg, e); } finally { String msg = String.format( "task: %s executed, thread name = %s, delay time = %s ms, executing time = %s ms", taskName, Thread.currentThread().getName(), (beginTime - taskCreateTime) / 1000000, (System.nanoTime() - beginTime) / 1000000); msgLog(msg, null); } } private void msgLog(String msg, Exception e) { LogFactory.setFlag(flag); if (e == null) { logger.info(msg); } else { logger.error(msg, e); } LogFactory.removeFlag(); } }
該wrapper類實現了Runnable接口,打印一次task的執行的等待耗時和執行耗時。
根據日誌打印和程序常量得到如下數據表:
總耗時 Ttotal(s) | 任務數Nt | 總耗時 Ttotal/任務數Nt(ms) | 任務總耗時Ttt(ms,相加耗時) | 平均Taver/最大任務耗時Tmax(ms) | 核心Ntcore/最大線程數Ntm | 平均Taver/核心Ntcore(ms) | |
任務獎勵(獎勵) | 126.000 | 2437 | 51.72 | 27344 | 11.22/43 | 10/10(公用) | 1.1 |
上述表格中Ttt和Taver、Tmax都是從單線程的角度來說明的
- 數據計算
- 分析
- 優化方案
總耗時 Ttotal(s) | 任務數Nt |
實際平均任務耗時Trtapt (總耗時 Ttotal/任務數Nt(ms)) |
Tw | 任務總耗時Ttt(ms,相加耗時) | 平均Taver/最大任務耗時Tmax(ms) | 核心Ntcore/最大線程數Ntm | 理論平均任務耗時Tctapt (平均Taver/核心Ntcore(ms)) | |
任務獎勵(獎勵) | 415.088 | 36259 | 11.5 | 10 | 1253473 | 34.56/147 | 10/10(公用) | 3.5 |
我們該怎麽結合日誌做優化