隨着微服務架構的流行,一次請求每每須要涉及到多個服務,所以服務性能監控和排查就變得更復雜。APM所以而生。java
目前,市面上的APM服務端已經有了很是多的實現。好比Zipkin、Jaeger、SkyWalking、Elastic APM等(Pinpoint並不支持OpenTracing,因此咱們不介紹)。git
本教程僅提供一些開發思路,對技術要求較高,由於大部分須要涉及到對相應組件源碼的理解。所有源碼均可以在github上找到。github
本次選擇Jaeger做爲服務端示例。主要內容以下。spring
第一部分api
在第一部分,咱們來看一下apm的關鍵信息和一些概念性的東西,爲後面的示例作準備。在這裏,咱們簡單的描述了jaeger是如何安裝的,也是一些準備工做。bash
第二部分架構
使用一個簡單的java程序,來講明如何使用OpenTracing api進行trace的生成,並演示一個帶有二個節點的調用鏈生成。併發
第三部分app
使用OkHttp3和SpringBoot,來演示如何實現一個分佈式調用。本文依然是使用底層的api進行構建,對開發感受好的到此應該可以應對各類場景。分佈式
第四部分
以SpringCloud爲例,說明微服務的調用鏈生成邏輯。因爲各類調用內容較多,咱們僅以比較流行的Feign調用來講明其原理。 SpringCloud的實現已經有了比較好的輪子,考慮的也比較全面,能夠參考其源碼進行分析。
若是你在開發本身的中間件,或者作一些集成性的工做,本教程可以讓你快速給本身的組件加入apm功能。前提是,你的api兼容OpenTracing協議。
對於監控體系和apm鏈路,小姐姐味道公衆號有更加詳細的描述,建議先讀一下。
如下僅簡要說明一下調用鏈的主要因素。
主要用於多樣化的數據收集,爲數據分析作準備。要求易用好用侵入儘可能小(開發工做量),而且在極端狀況下(如收集組件不可用)不能對業務有任何影響。能夠看到此部分的開發量是巨大的,尤爲是須要集成Nginx上下游、基礎組件多樣、技術棧多樣的狀況下。
主要有實時分析與線下分析。通常,實時分析的價值更大一些,主要產出如秒級別的調用量、平均響應時間、TP值等。另外,調用鏈(Trace)須要存儲全量數據,一些高併發大埋點的請求,會有性能問題。
此部分利用數據分析的產出,經過短信郵件等形式,通知訂閱人關注。監控報警平臺應儘可能向devops平臺靠攏,包括自主化服務平臺。
jaeger的開發較爲活躍,並且它的模塊劃分是比較靈活的。在數據量很是大的狀況下,數據是能夠先用kafka緩衝一下的(同時爲接入各類流分析平臺作足了準備)。這些內容,咱們在jaeger安裝的部分順便說明。
若是你的項目使用了SpringBoot,是很是方便進行擴展的。如下內容不止一次提起,讀過的請忽略。
咱們接下來實現的功能是:任何加了
@OwlTrace
註解的方法,都將產生一條調用鏈信息。
首先,咱們要定義一個註解:
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OwlTrace {
}
複製代碼
而後,實現其處理類。代碼經過AOP
對Spring管理的Bean進行攔截,很是簡單的實現了Trace
信息的構造。代碼以下:
import io.opentracing.Span;
import io.opentracing.Tracer;
import io.opentracing.tag.Tags;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
@Slf4j
public class OwlTraceAutoConfiguration {
static final String TAG_COMPONENT = "java";
@Autowired
@Lazy
Tracer tracer;
@Bean
public TracingAspect pxTracingAspect() {
return new TracingAspect();
}
@Aspect
class TracingAspect {
@Around("@annotation(com.sayhiai.arch.trace.annotation.OwlTrace)")
public Object pxTraceProcess(ProceedingJoinPoint pjp) throws Throwable {
Span span = null;
if (tracer != null) {
final String cls = pjp.getTarget().getClass().getName();
final String mName = pjp.getSignature().getName();
span = tracer.buildSpan(cls + "." + mName)
.withTag(Tags.COMPONENT.getKey(), TAG_COMPONENT)
.withTag("class", cls)
.withTag("method", mName)
.startActive(false)
.span();
}
try {
return pjp.proceed();
} catch (Throwable t) {
Map<String, Object> exceptionLogs = new LinkedHashMap<>(2);
exceptionLogs.put("event", Tags.ERROR.getKey());
exceptionLogs.put("error.object", t);
span.log(exceptionLogs);
Tags.ERROR.set(span, true);
throw t;
} finally {
if (tracer != null && span != null) {
span.finish();
}
}
}
}
}
複製代碼
最後,根據Spring
的加載方式,將路徑添加到src/main/resources/META-INF/spring.factories
中:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
arch.trace.core.owl.OwlTraceAutoConfiguration,\
arch.trace.core.log.LoggingAutoConfiguration
複製代碼
將組件打成jar
包,一個spring boot starter
就實現了。
application.properties
確保開了AOP
# AOP
spring.aop.auto=true
spring.aop.proxy-target-class=true
opentracing.jaeger.log-spans=true
opentracing.jaeger.udp-sender.host=192.168.3.237
opentracing.jaeger.udp-sender.port=5775
複製代碼
接下來,咱們將逐步進入客戶端api的開發之中。