2020/3/1

Java常見日誌框架比較(SLF4J、Log4J、Log4J2、logback)



  • 概要
    1. 日誌框架歷史?
    2. 門面日誌框架 VS 具體實現日誌框架?
    3. SLF4J應用之設計模式?
    4. 遺留問題 : 各第三方框架採用的日製框架不同 , 如何統一日誌框架?
    5. Spring Boot日誌框架配置?
  • 日誌框架歷史?

  • 門面日誌框架
    為了讓接口與具體實現抽離,因此誕生了門面日誌框架,常見框架如下
    • Jarkarta Common Logging
      • 運行時動態綁定Log實現類
      • 缺點 : 多ClassLoader導致的問題 , 2014年已停止維護
    • SLF4J(Simple Logging Facade for Java)
      • 編譯時綁定Log實現類
      • 實現了項目與具體日誌框架解耦.統一了各種日誌框架的使用方法,降低重複的學習成本
      • SLF4J範例
        • 僅載入slf4j jar包
        • spring boot專案預設使用logback包,欲更換可進行排除
        • spring boot <=1.3 版本支援log4j 
        • spring boot >=1.4 版本支援log4j2
        • 執行slf4j程式與結果
          slf4j因找不到具體日誌框架實作類,拋出錯誤
        • org/slf4j/impl/StaticLoggerBinder.class會尋找所有SLF4J的實現類

      •  Log4J實現
        • 增加轉接包
        • 實現org/slf4j/impl/StaticLoggerBinder.class與log4j
      • Log4J2實現
        • 增加轉接包 
        • 實現org/slf4j/impl/StaticLoggerBinder.class與log4j2
      • 如果上述兩項接引用JAR包 , 系統會提示找到多個實現類、使用了哪一個JAR包

  • SLF4J應用之設計模式?
    • Facade模式
      • 外部一系统的通信必須通過一個統一的入口進行,使得子系统更易于使用
        • 用在自定義utils?
        • Netflix Zuul?  Spring Cloud Getwate?
          • Netflix 的閘道方案使用 Zuul,雖然最新版本是 Zuul 2,不過 Spring Cloud 整合的版本僅 Zuul 1,這中間的插曲是 Zuul 2 原本預計在 2016 年底左右發佈,然而卻拖到了 2018 年 4 月,在這段期間,Spring 就自己搞了個 Spring Cloud Gateway,不打算整合 Zuul 2 了
      • SLF4J提共了統一接口 , 使用者無需關心具體實現方式
    • Adapter模式
      • 如同轉接頭 , 將兩者功能能配合在一起使用
      • 例如 , 因為在Log4J的發展歷史較早 , Log4J1並未考慮到後來出現的SLF4J , 因此SLF4J提共許多Adapter包 , 能夠與Log4J配合 
  • 遺留問題 : 各第三方框架採用的日誌框架不同 , 如何統一日誌框架?
    • 情境如下
    • 現況調整前 , 多個設定檔,各別輸出
    • 調整後 , 統一設定、輸出
  • 另一圖片解釋

  • Spring Boot預設日誌配置?
    • 僅載入 spring-boot-starter的情形下
    • 使用 IntellJ 在pom.xml上按下右鍵開啟 Diamgram->Dependcy
    • 可察看到 , 官方預設使用了sslf4j+logback作為日誌框架實現 
    • 還加入其他日誌框架橋接包, 替換成slf4j , 依情形僅需排除第三庫依賴的日誌依賴,spring boot自動幫你配置起來

    2020/2/17

    GC優化步驟


    • 調優目標
      • GC時間足夠小
      • GC次數足夠少
      • Full GC週期足夠長
      • Full GC次數少
      • 移轉到老年代的對象少
    • 調優結果
      • 代碼減少使用全局變量和大對象
      • 調整新生代大小
      • 調整老年大大小
      • 選擇合適的GC收集器
    • 調優步驟
      1. 監控GC狀態
      2. 生成Dump文件
      3. 分析Dump文件
      4. 調整參數
    • 監控GC狀態
      • 開啟GC日誌(3選1)
        # -XX:+PrintGCDetails 
        # -XX:+PrintGCTimeStamps
        # -XX:+PrintGCDateStamps
      • 存放GC日誌檔
        # -Xloggc:/gc.log
      • 需注意之狀態
        • Minor時間超過50ms
        • Minor時間10秒內一次
        • Full GC時間超過1秒
        • Full GC時間10分鐘內一次
        • Full GC無效果
    • 生成Dump文件
      • 開啟OOM Dump文件
        # -XX:+HeapDumpOnOutOfMemoryError
      • 存放Dump文件 *.hprof
        # -XX:HeapDumpPath=D:\
    • 分析Dump文件 *.hprof
      • 中文開啟 .....jdk/bin/jvisualvm.exe
        # jvisualvm --locale zh:CN
      • D:\>Java\jdk1.8.0_241\bin\jvisualvm.exe  --locale zh:CN
      • 開啟dump文件 *.hprof

    • 調整參數
      • 其他參數
      • 減少GC次數
        • -Xmx = -Xms
        • -XX:newSize =  -XX:MaxNewSize
        • 提高閥值 -XX:MaxTenuringThreshold
          • 預設minor gc 達15次放入old區
      • 調整Old區
        • 觀察Old區之峰值 , 適當調高Young區 , 但會增加mior gc時間 , 關鍵字NewRatio
        • 硬體較好的設備可在Old區使用並行收集算法
          # -XX:+UseParallelOldPC 
      • 調整Stack
        • 每個線呈默認開啟 1M stack , 內存不變下可增加線呈數
      • 重複演練
    • 其他
      • 吞吐量優先的並行收集器與響應時間優先的併發收集器選擇

    2020/2/16

    JVM構造(下)


    • 參考網址
    • 何謂GC回收?
      • 分代收集算法
        • 根據各區特性使用不同算法
      • 特性
        • GC次數較頻繁收集於Heap Young區
        • GC次數較少收集於Heap Old區
        • 基本上不動的收集於Perm區
    • Heap Young
      • 複製算法( Copying )
        • 當Eden內存滿時 , Minor GC清空Eden區 , 將 From區與Eden區存放於To區
        • From區與To區的內存不斷進行複製、移動、整理
        • 優點 : 內存碎片減少、效率高
        • 缺點 : 需要2倍內存空間、
    • Heap Old
      • 標記清除( Mark-Swap )
        • 第一次掃描進行標記
        • 第二次掃描進行清除
        • 優點 : 不浪費內存空間(不用2倍內存)
        • 缺點 : 掃描兩次、產生內存碎片
      • 標記壓縮( Mark-Compact )
        • 第一次掃描標記
        • 第二次掃描移動(壓縮)
        • 優點 : 無內存碎片
        • 缺點 : 掃描兩次
      • 標記清除壓縮( Mark-Swap-Compact )
        •  Mark-Swap + Mark-Compact
        • 多次 Mark-Swap GC之後才做Mark-Compact
        • 優點 : 減少移動成本
    • 3種垃圾回收方式
      • 串行 serial 
        • 單線呈進行垃圾回收
      • 並行 
        • 多線呈進行垃圾回收, 回收期間暫停程序
      • 並發
        • 多線呈進行垃圾回收, 回收期間暫停程序

    JVM構造(中)


    • 參考網址
    • ClassLoader構造
      • 沙箱安全機制
        • 類別保護機制 , 呼叫類別優先從上層級別開始 , 因此開發人員無法複寫例如 java.lang.String類別
    • Stack構造
      • java.lang.StackOverFlowError
        • 原因為棧空間滿

    • Heap構造
      • OOM : Heap 
        • 當內存不足時 , JVM連續發生GC時
        • 每次回收小於2%
    • 永久區構造

    2020/1/31

    Redis資料庫+分佈鎖 - 基於Redis資料庫實現分佈鎖(二)

    • 資料來源
    • 基於Redis資料庫實現分佈鎖
      • 基於分佈式環境才有分佈鎖之問題
      • 需要以原子操作
      • 無法使用JVM級別的鎖
      • 將併發進行串行化(多路復用)操作,缺點就是無法支持高併發
      • 可使用ZK鎖以決定Redis主從資料庫的資料一致性
    • 情境如下
      • 庫存數量10
      • 下單後庫存-1
      • 使用jmeter壓測
      • 單例模式 (僅一個tomcat處理)
    • 演變1
      • 邏輯 : 單純將庫存-1
      • 缺點 : 扣減錯誤 , 單機環境也可使用 sychronize鎖解決 , 但分布式環境併發仍會扣減錯誤

    • 演變2
      • 邏輯 : 使用redis setnx() or incr() 決定線呈是否取得鎖=1 , 結束後還原=0
      • 缺點 : 部分線程雖優先順序較高 , 但無法取得鎖 ,造成後續扣減無法完成
      • 缺點 : 如果程式執行發生異常或web應用掛機 , 鎖無法取消

    • 演變3
      • 邏輯 : 將鎖設置超時時間
      • 缺點 : 後面線程將前面線程的鎖解除
      • 缺點 : 超時時間不好預估

    • 演變4
      • 邏輯 : 使用redission框架 ,  設置後台子線程 , 監測主線程是否還在執行, 如果還在執行則延遲超時時間 , 其他線程沒有取得鎖則循環嘗試加鎖
      • 缺點 : 自旋處理有其缺點及適用場景


    • Redission框架
      • 可立即使用在分布式鎖環境
      • 悲觀鎖 : 僅有取得鎖的線程可持續加鎖 , 其他線程要取得鎖必須等待 , 有性能問題
      • 實現方法


    2020/1/30

    多執行續處理


    • 參考網址
    • 多執行續演變
      • <= JDK 1.4
        • synchronize
        • volatile
        • 不變性
      • = JDK 1.5
        • Thread Pool
        • AIO
        • Excutor
        • Concurrent包
    • 創建執行續的4種方式
      • Thread
      • Runnable
      • Callable
        • 可以有返回值 , 由FutureTask支持接收結果 , get()->阻塞
      • ThreadPool
        • 如果沒有執行shutdown() , 執行續會持續進行
        • 使用runnable
        • 使用callable
        • 使用調度線程池
    • 多執行續的 可見性、原子性、有序性問題
      • 可見性 : 基於Java內存模型 , 每一個線程皆會從主存存取共用變數值至工作內存中進行運算 , 導致A線程已修改數據,B線程仍使用舊資料進行運算  , 因此有 Volatile修飾字的解決辦法
      • 原子性 : 多個線程存取同一共用變數 , 同時進行"修改"所導致的問題 (Concurrent包)
      • 有序性 : JVM因考量效能 , 在解析代碼會將沒有順序關係的變數進行順序調度 , 導致非預期結果
    • Volatile 參考
      • 保證共享(主)內存刷新
      • 符合有序性、可見性
      • 不符合原子性
    • Concurrent包
      • 用於處理多執行續的
      • 1.8之後符合CAS(Compare&Swap)演算法(原子性) , 使用native method硬體對併發的支持
        • CAS原理
          • 內存值V
          • 預估值A
          • 更新值B
          • If V==A , V=B
        • 屬於一種樂觀鎖機制
      • 含原子操作用的Atomic api
      • 常見之Class , 介於Collection之非執行續安全類別~執行續安全類別之間
        • ConcurrentHashMap
          • 默認分成16段 , 分段加鎖
        • ConcurrentSkipListMap
        • ConcurrentSkipListSet
        • CopyOnWriteArrayList
        • CopyOnWriteArraySet
    • 常見多執行續"鎖"之種類介紹
      • 悲觀鎖
        • 當對象被線程鎖上時,其餘線程必須等待鎖釋放才可存取
        • 例如 synchronize
        • 缺點沒控制好性能通常較差
      • 樂觀鎖
        • 當未完成操作時 , 進行重試
        • 例如 CAS進行硬件級別鎖
        • 缺點ABA問題 , 循環開銷大 , 一次只能保證一個共享變數(建議使用synchronize)
      • 閉鎖操作
        • CountDownLatch用於等待多線程操作完成
        • 當計數器=0時代表開鎖
      • 同步鎖
        • synchronize區塊(隱式鎖)
        • synchronize方法(隱式鎖)
        • Lock(顯示鎖、樂觀鎖)
          • 使用lock() 、 unlock()定義同步區塊
          • 搭配condition生成await()、signal()、signalAll()
          • Object原生的wait()如同await()建議放在while回圈內,防止虛假喚醒
        • 補充
          • synchronize屬於對象鎖 , 影響單一對象 , 如果另外生成對象則不影響 
          • static synchronize屬於類別鎖 , 影響全部對象 
          • synchronize與static synchronize兩者為獨立運行 , 各別控制運行
      • 讀寫鎖
        • 寫寫/讀寫兩種線程必須互斥 , 讀寫分離
        • 一個 readLock() , 一個 WriteLock()
    • Fork/Join框架




    2020/1/29

    MySQL SQL優化

      1. 開啟慢查詢日誌 , 觀察一天或重現事故
      2. Explain + 慢查詢SQL
      3. Show Profile分析SQL生命週期
      4. DB參數優化
    • 慢查詢日誌
      • 使用慢查詢日誌 , 紀錄查詢效能差的SQL
      • 當查詢時間超過long_query_time時寫入日誌,默認時間為10秒
      • 開啟慢查詢日誌 , 設置後須重新連線DB

      • 永久配置
      • 查看慢查詢累積筆數
      • 慢查詢分析日誌 mysqldumpslow
        • 參數
        • 範例
    • Explain + 慢查詢SQL
      • 使用Explain查看執行計畫 , 優化SQL
      • SQL優化策略 (Index索引為其重點)
        • 查詢執行計畫 : explain select ............最重要之4個欄位
          • Id : 執行順序 , id越大優先執行 , id值相等則上方列優先
          • type : 查詢等級System>const>eq_ref>ref>range>index>all
            • 大多是ref
            • 至少要range
            • all為全表掃描
          • rows : 預估要查詢的列數 , 越少越好
          • extra : 其他index>filesort>temporary
            • 至少要index
        • SQL優化策略
          • Index優化策略
            • select和where欄位皆可使用
            • MySQL CRUD皆會做查詢的動作, 因此Index索引決定效能
              • MyIsam引擎將資料與索引拆分成兩份檔案InnoDB為同一份檔案查詢效能較好
              • 建議PK使用數值或自動產生流水號查詢效能較文字高
              • 建議一定要加PK , 否則默認由DB自己判斷生成RowId
            • MySQL 預設使用 B+Tree資料結構儲存索引
              • 根據官方分析過,預設高度為3
              • 完成資料data存在於最後一層索引上 , 與其他資料結構僅儲存磁碟位置不同
              • Mysql另外有一種Hash資料結構, 較適合等值where條件
            • 組合索引必須依序讀取/設置
            • range索引(>= , > , < , <=)導致後續的索引失效 , 故可將此欄位放置在最後面
            • 欄位不使用函數、計算
            • 當使用left join時 , index加在右表 , 反之亦然
            • 少使用select * , 使用覆蓋索引(欄位順序個數跟Index完全相同)效能佳
            • 少用 OR
            • Varchar必用單引號 , 以免索引失效
            • 不使用 >< ,  != , 以免索引失效
            • 不使用 is null , is not null , 以免索引失效,  使用 ''代替null欄位
            • Like必須使用 'X%'以免索引失效 , 否則可用覆蓋索引彌補
          • 查詢優化策略
            • join buffer大小可以視情況調整
            • 小表(A)驅動大表(B)查詢 , B in (A) 或 A exist (B)
          • Order By優化策略
            • 可搭配Index左前戳開頭規則 , 建立組合索引
            • 不可使用 select *
            • 所有排序欄位必須一致遞升或遞減
            • sort_buffer_size不足會導致多次I/O臨時表多路合併 (這是每個進程都有一份)
            • max_length_for_sort_data不足會導致臨時表與多路合併
            • 當max_length_for_sort_data設置過大會導致sort_buffer_size不足
            • 當查詢筆數<max_length_for_sort_data且排序欄位並非text|blob , 使用單路排序
          • Group By優化策略
            • 實質上是先排序後分組
            • Order By索引規則相同
            • 當無法使用索引 , 則增加sort_buffer_size和max_length_for_sort_data
          • Order By優化策略
      • Show Profile分析SQL生命週期
        • 分析SQL執行細節和生命週期 , 查出問題點
        • 開啟profile功能
        • 查詢SQL執行明細過程
        • 可查詢欄位 , 以及Query Id
        • 診斷方式
          •  Status欄位不得出現之訊息 , 將嚴重影響效能

      test2