本文分享自華為雲社群《Java Chassis 3技術解密:實用的可觀測性》,作者:liubao68。
狹義的可觀測性,指日誌、呼叫鏈和指標,廣義的可觀測性則包含更多的內容,一般的,應用程式暴露出來的便於理解其執行狀態、執行軌跡、內部結構和功能集合的資訊,都是可觀測性的範圍,本文只討論狹義的可觀測性。日誌揭露了應用程式內部執行的軌跡,透過異常日誌,可以理解錯誤產生的原因;呼叫鏈反映的是一次業務操作經過的關鍵處理節點,可以幫助快速確定問題發生的邊界;指標反映錯誤發生時應用程式的當前或者歷史狀態,幫助分析需要一定的時間或者流量積累才會發生的問題,比如過載問題、效能問題等。可以看出,為了分析故障,具備可觀測效能力非常重要。
微服務系統具備複雜的呼叫關係和分散式部署特徵,為了更好的分析和處理日誌、呼叫鏈和指標,通常會部署ELK、SkyWalking和Prometheus等外部系統。 這些系統完全搭建起來,會花費數十萬每年的計算成本,而且很可能並沒有顯著提升日常問題定位的效率,不恰當的使用還可能會引入效能問題。針對問題定位難的情況,Java Chassis 3提供了非常簡單高效,而且低成本的解決方案。由於採集的資料,都是和Java Chassis執行過程和系統架構強相關的,也避免了採集海量無關資料,使得資料對於問題分析更具有針對性,能夠更加快速識別問題根因。
在下面的部分,我們首先解密如何使用可觀測能力來快速定位問題,然後再解密這個能力是如何構建起來的。
問題定位流程
在很多組織裡面,問題定位都是由不太熟悉系統結構和技術細節的運維人員開始的,或者是由工作交接後剛剛接觸系統的新人開始的,這給快速定界問題,收集和問題相關的資訊帶來了巨大的挑戰。一個問題從發現到傳遞給責任模組,數個小時的時間就過去了。 設計一個簡單的問題定位流程,快速定界問題和收集關聯資訊,是可觀測系統搭建的起點。
當使用者識別到一個故障,比如交易失敗,在系統層面,會對應到一次系統請求的失敗。在系統設計之初,會採用一個請求標識將使用者故障和系統請求關聯起來,即 TraceId, 這個是所有呼叫鏈系統設計的基礎。 通常建議前端在傳送請求的時候,都攜帶 TraceId, 便於將前後端請求進行關聯。在前端未按照要求攜帶 TraceId 的情況下,Java Chassis會在應用閘道器 Edge Service生成 TraceId, 並在給前端響應的HTTP頭中攜帶 TraceId。 當使用者識別到一個故障,可以透過瀏覽器等前端工具獲取到 TraceId。 問題定位的起點是獲取TraceId。
在管理控制檯,輸入TraceId 和問題發生大概時間,可以檢索出關鍵的呼叫鏈資訊和關鍵日誌資訊。 透過呼叫鏈資訊,可以知道請求的執行軌跡和發生問題的節點,透過關鍵日誌資訊,能夠快速確定問題根因。 對於一些簡單常見的問題,經過這個簡單的步驟,就能夠確定問題根因。
對於一些複雜的問題,需要獲取上下文日誌或者指標來進行深入的分析,運維人員可以在檢索結果裡面將完整的日誌檔案和指標資訊下載下來,提供給故障服務的技術人員。
從上面的過程可以看出,運維人員在不理解系統實現細節的情況下,也能快速定界和定位一些簡單問題,並能夠快速收集詳細的和問題強相關的資訊提供給技術人員做進一步處理。
實現原理
Java Chassis在設計之初,就內建了大量的可觀測能力。使用上述流程,無需部署ELK、SkyWalking和Prometheus去採集資料,也不需要整合這些工具的SDK或者Agent。 透過一些開發規範約束和可觀測API就能夠實現一個簡單高效和易用的定位系統。
動手試試: 可以透過下載和執行 fence 專案,體驗上述問題定位流程和了解本章節介紹的實現原理。 也可以在實際的業務系統中,參考該專案構築業務需要的可觀測能力。
Java Chassis透過整合 應用效能監控(https://servicecomb.apache.org/references/java-chassis/zh_CN/general-development/metrics.html) 、 微服務呼叫鏈(https://servicecomb.apache.org/references/java-chassis/zh_CN/general-development/microservice-invocation-chain.html) 來生成呼叫鏈和指標,日誌則使用 slf4j 來記錄。 這些資料構成了可觀測的基礎, 接下來就是如何儲存和採集這些資料。
透過配置 log4j2 , 可以將日誌、呼叫鏈和指標都輸出到日誌檔案。 特別的,該日誌配置約束了資料儲存的規則、路徑,為可觀測API提供了簡單的實現方案。
<Configuration> <Properties> <property name="FILE_PATH" value="./logs/admin-website"/> </Properties> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%-d{yyyy-MM-dd HH:mm:ss} [%X{SERVICECOMB_TRACE_ID}][%p][%t][%c:%L] %m%n"/> </Console> <RollingFile name="RootLog" fileName="${FILE_PATH}/root.log" filePattern="${FILE_PATH}/root-%d{yyyy-MM-dd-HH}.log"> <PatternLayout pattern="%-d{yyyy-MM-dd-HH:mm:ss} [%X{SERVICECOMB_TRACE_ID}][%p][%t][%c:%L] %m%n"/> <Policies> <TimeBasedTriggeringPolicy interval="3"/> </Policies> <DefaultRolloverStrategy max="100"/> </RollingFile> <RollingFile name="TraceLog" fileName="${FILE_PATH}/trace.log" filePattern="${FILE_PATH}/trace-%d{yyyy-MM-dd-HH}.log"> <PatternLayout pattern="%-d{yyyy-MM-dd HH:mm:ss} %m%n"/> <Policies> <TimeBasedTriggeringPolicy interval="3"/> </Policies> <DefaultRolloverStrategy max="100"/> </RollingFile> <RollingFile name="MetricsLog" fileName="${FILE_PATH}/metrics.log" filePattern="${FILE_PATH}/metrics-%d{yyyy-MM-dd-HH}.log"> <PatternLayout pattern="%-d{yyyy-MM-dd HH:mm:ss} %m%n"/> <Policies> <TimeBasedTriggeringPolicy interval="3"/> </Policies> <DefaultRolloverStrategy max="100"/> </RollingFile> </Appenders> <Loggers> <Logger name="scb-trace" level="INFO" additivity="false"> <AppenderRef ref="TraceLog"/> </Logger> <Logger name="scb-metrics" level="INFO" additivity="false"> <AppenderRef ref="MetricsLog"/> </Logger> <Root level="INFO"> <AppenderRef ref="Console"/> <AppenderRef ref="RootLog"/> </Root> </Loggers> </Configuration>
每個微服務都整合和實現可觀測API。
@Path("/v1/scb/observability") public interface ObservabilityService { String NAME = "scb-observability"; @Path("/searchTrace") @GET SearchTraceResponse searchTrace(@NotNull @QueryParam("timestamp") String timestamp, @NotNull @QueryParam("traceId") String traceId); @Path("/searchLog") @GET SearchLogResponse searchLog(@NotNull @QueryParam("timestamp") String timestamp, @NotNull @QueryParam("traceId") String traceId); @Path("/downloadLog") @GET Part downloadLog(@NotNull @QueryParam("timestamp") String timestamp); @Path("/downloadMetrics") @GET Part downloadMetrics(@NotNull @QueryParam("timestamp") String timestamp); }
最後,我們可以開發一個管理控制服務,實現管理面可觀測API, 就完成了可觀測能力的構建:
@Path("/v1/scb/admin/observability") public interface AdminObservabilityService { String NAME = "scb-admin-observability"; @Path("/searchTrace") @GET List<SearchTraceResponse> searchTrace(@NotNull @QueryParam("timestamp") String timestamp, @NotNull @QueryParam("traceId") String traceId); @Path("/searchLog") @GET List<SearchLogResponse> searchLog(@NotNull @QueryParam("timestamp") String timestamp, @NotNull @QueryParam("traceId") String traceId); @Path("/downloadLog") @GET Part downloadLog(@NotNull @QueryParam("timestamp") String timestamp, @NotNull @QueryParam("serviceName") String serviceName, @NotNull @QueryParam("instanceId") String instanceId); @Path("/downloadMetrics") @GET Part downloadMetrics(@NotNull @QueryParam("timestamp") String timestamp, @NotNull @QueryParam("serviceName") String serviceName, @NotNull @QueryParam("instanceId") String instanceId); }
和傳統方案的對比分析
與部署ELK、SkyWalking和Prometheus去採集資料的傳統方案對比,上述方案非常簡單和實用,能夠幫助實時線上分析問題,該方案也無需將日誌、呼叫鏈和指標等資料集中儲存下來,可以節省大量的儲存裝置空間。 當然它的缺點也是顯而易見的,對於已經下線的服務,或者對於歷史問題需要追溯的情況,則採集不到相關的資訊。 站在問題定位的角度,儲存海量的日誌、呼叫鏈和指標資料,大量資料都是和問題無關的,並且多數情況是要在第一時間完成問題定界和資訊收集,因此上述方案相比於傳統方案就有了非常大的競爭力優勢。
客戶故事:很多客戶花了大量成本構建可觀測能力,依然無法指導運維人員快速定界和定位問題。透過建立一個簡單實用的問題定界流程和採集資料的手段,可以幫助提升問題定位效率。
點選關注,第一時間瞭解華為雲新鮮技術~