Skip to content

Latest commit

 

History

History
249 lines (223 loc) · 14.1 KB

APM-ZipKin.md

File metadata and controls

249 lines (223 loc) · 14.1 KB

APM框架系列:


ZipKin架构

相关概念:

  • 架构组件:
  • 项目组成:
    • openzipkin/zipkin:ZipKin Server组件,包含Collector、Storage、API、UI;
    • openzipkin/brave:Instrumentation探针项目,客户端引用;
    • openzipkin/zipkin-reporter-java:Reporter指Instrumentation向Collector发送数据的方案,例如okhttp3Kafka。首先Collector支持不同数据接收方式,Instrumentation发送方案也不一样;另外同一种Collector数据接收方案,也可以使用不同的客户端框架来完成,这些工作由reporter项目负责;
  • 链路跟踪:
    • TraceId:标记一次全链路调用,全局唯一;
    • SpanId:标记一次RPC调用;
    • Annotations:ZipKin跟踪RPC性能的方法,记录4个时间点来反映RPC调用性能细节:Client Start、Server Start、Server Finish、Client Finish,参考功界面功能 -> 链路跟踪详情
    • Propagation:在RPC调用过程中Inject封装、Extract解封跟踪数据,例如b3-propagation

部署ZipKin Server

使用当前ZipKin Server最新版本2.19.2,使用my-demo作为演示项目。

ZipKin默认按Docker容器部署方式发布,使用Docker或Java快速部署都非常方便。

快速部署(内存存储、HTTP Collector):

curl -sSL https://zipkin.io/quickstart.sh | bash -s
java -jar zipkin.jar

存储到MySQL(只用到3个表):

  1. MySQL创建zipkin数据库,执行schema DDL建表;
  2. 启动ZipKin:
    STORAGE_TYPE=mysql MYSQL_HOST=localhost MYSQL_TCP_PORT=3306 MYSQL_DB=zipkin MYSQL_USER=root MYSQL_PASS=dev java -jar zipkin.jar

访问http://localhost:9411查看UI。


客户端应用使用

  • PinPoint:采用javaagent方式,对应用完全无侵入,项目无需添加任何额外依赖项,无需修改代码,这点做得最好;
  • SkyWalking:采用javaagent方式,普通功能对应用无侵入,以下两项功能需要应用稍作修改:
    • 中添加自定义Tag项:应用代码在SkyWalking Span中添加自定义Tag,可以按需输出方法参数值等关键信息,辅助应用排错和性能分析,PinPointZipKin都不支持(可自行实现);
    • 日志中输出全局跟踪ID,需要添加SkyWalking依赖项;
  • ZipKin:没有采用javaagent方式,应用强依赖ZipKin,必须打包到应用中与应用一起运行,每个项目需要添加依赖项、配置,不同探针有不同的bean配置需求;

my-demo中运行ZipKin演示:

  1. 使用zipkin参数编译打包(或者手工打包,使用maven profile dev,zipkin):
    sh $PROJECT_HOME/package.sh -zipkin
  2. 按下面脚本顺序启动服务和应用:
    java -jar item-service\target\item-service-0.0.1-SNAPSHOT.jar
    java -jar stock-service\target\stock-service-0.0.1-SNAPSHOT.jar
    java -jar user-service\target\user-service-0.0.1-SNAPSHOT.jar
    java -jar order-service\target\order-service-0.0.1-SNAPSHOT.jar
    java -jar shop-web\target\shop-web-0.0.1-SNAPSHOT.jar
  3. 访问http://localhost:8090/shop执行一些操作,即可在PinPoint界面查看结果;

对不同框架Instrumentation使用的拦截方案不同,具体使用方法参考各Instrumentation实现,下面是my-demo中用到的几种。

SpringBoot Web项目

最简单方案是使用spring-cloud-sleuth(项目不需要是SpringCloud服务,普通SpringBoot Web项目添加spring-cloud-sleuth依赖即可),my-demo中的shop-app配置示例如下:

  1. pom.xml添加依赖项:
    <dependencies>
        <dependency>
    	      <groupId>org.springframework.cloud</groupId>
    		  <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-sleuth</artifactId>
                <version>2.2.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
  2. application.yml添加配置:
    spring:
       zipkin: # spring-cloud-sleuth配置
          base-url: http://192.168.31.108:9411 # ZipKin Server地址
          sleuth:
             sampler:
                percentage: 1.0 # 采样比例,1.0表示100%采样

底层使用Servlet的Filter实现HTTP拦截,ZipKin Instrumentation的设置工作都由spring-cloud-sleuth完成,项目中无需额外处理。

Dubbo项目

使用Dubbo的Filter机制实现拦截,Alibaba Dubbo使用dubbo-rpc instrumentation,Apache Dubbo使用dubbo instrumentationmy-demo基于Alibaba Dubbo 2.6.7dubbo-spring-boot-starter使用dubbo-rpc,设置方法如下:

  1. pom.xml添加依赖项:
    <dependencies>
         <dependency>
             <groupId>io.zipkin.brave</groupId>
             <artifactId>brave-instrumentation-dubbo-rpc</artifactId>
         </dependency>
         <dependency>
             <groupId>io.zipkin.reporter2</groupId>
             <artifactId>zipkin-sender-okhttp3</artifactId>
         </dependency>
    </dependencies>
    <dependencyManagement>
     	<dependency>
     		<groupId>io.zipkin.brave</groupId>
     		<artifactId>brave-bom</artifactId>
     		<version>5.9.1</version>
     		<type>pom</type>
     		<scope>import</scope>
     	</dependency>
    </dependencyManagement>
  2. 实现ZipkinPropertiesZipkinConfiguration
     @ConfigurationProperties(prefix="zipkin")
     public class ZipkinProperties {
     	@Value("${dubbo.application.name}")
     	private String serviceName;
     	private String server;
     	private int connectTimeout;
     	private int readTimeout;
     	//getters and setters
     }
     
     @Configuration
     @EnableConfigurationProperties({ZipkinProperties.class})
     public class ZipkinConfiguration {
     	@Autowired
     	ZipkinProperties properties;
     	@Bean
     	public Tracing tracing() { //名称必须为tracing
     		Sender sender = OkHttpSender.create(properties.getServer());
     		AsyncReporter<Span> reporter = AsyncReporter.builder(sender)
     			.closeTimeout(properties.getConnectTimeout(), TimeUnit.MILLISECONDS)
     			.messageTimeout(properties.getReadTimeout(), TimeUnit.MILLISECONDS)
     			.build();
     		Tracing tracing = Tracing.newBuilder()
     			.localServiceName(properties.getServiceName())
     			.propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "brave-trace"))
     			.sampler(Sampler.ALWAYS_SAMPLE)
     			.spanReporter(reporter)
     			.build();
     		return tracing;
     	}
     }
  3. application.yml添加配置:
    dubbo:
       # 省略了其它dubbo配置
       provider:
          filter: tracing # 对服务提供者启用ZipKin监控
       consumer:
          filter: tracing # 对服务消费者启用ZipKin监控
    zipkin:
       server: http://192.168.31.108:9411/api/v2/spans
       connectTimeout: 2000
       readTimeout: 2000

实现原理:

  1. dubbo-rpc/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter内容如下:
    tracing=brave.dubbo.rpc.TracingFilter
    
    providerconsumer上指定的filter: tracing为这个SPI扩展的名称,Dubbo根据名称加载brave.dubbo.rpc.TracingFilter扩展。
  2. TracingFilter的注解说明该filter依赖一个名为tracingbrave.Tracing对象,即ZipkinConfiguration提供的:
    @Activate(group = {Constants.PROVIDER, Constants.CONSUMER}, value = "tracing")
    Dubbo ExtensionLoader根据setters(setTracing(Tracing tracing)setRpcTracing(RpcTracing rpcTracing))为filter提供依赖注入。

dubbo-rpc 5.9.1版本在处理一个新的调用链开始、结束方面有些问题。my-demo中的shop-web,之前并非是web项目,而是普通Java项目直接命令行运行,使用了SpringBoot,执行完runFullTestCase后,记录下来的调用链是错乱的,改为web项目,前面使用了spring-cloud-sleuth之后就正常了。
因为runFullTestCase中触发了多个Dubbo服务调用,在第一次调用时整个应用还没有开启跟踪,没有trace上下文,应当开启一个新的跟踪,该方法调用结束时,结束这个跟踪,随后遇到其它Dubbo服务调用同样处理,这样记录下来的调用链就是合理的、结构正确的,这方面处理不当则会造成调用链错乱。

MySQL JDBC

通过JDBC的interceptor机制实现,根据my-demo使用的mysql-connector-java版本选择brave-instrumentation-mysql8

  1. pom.xml添加依赖项:
     <dependency>
         <groupId>io.zipkin.brave</groupId>
         <artifactId>brave-instrumentation-mysql8</artifactId>
     </dependency>
  2. JDBC url添加参数,指定interceptor:
    ?queryInterceptors=brave.mysql8.TracingQueryInterceptor&exceptionInterceptors=brave.mysql8.TracingExceptionInterceptor&zipkinServiceName=db-order
应用日志中输出全局TraceId
  1. pom.xml添加依赖项:
    <dependency>
        <groupId>io.zipkin.brave</groupId>
        <artifactId>brave-context-slf4j</artifactId>
    </dependency>
  2. brave.Tracing配置slf4j的MDCScopeDecorator
     Tracing tracing = Tracing.newBuilder()
     	.localServiceName(properties.getServiceName())
     	.propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "brave-trace"))
     	.sampler(Sampler.ALWAYS_SAMPLE)
     	.spanReporter(reporter)
     	//配置slf4j的MDCScopeDecorator
     	.currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder().addScopeDecorator(MDCScopeDecorator.create()).build())
     	.build();
  3. logback.xml通过%X{traceId}%X{spanId}输出链路跟踪信息:


界面功能

ZipKin的界面功能最简单,只有依赖图、链路跟踪查询。

依赖图:

链路跟踪 - 列表:

链路跟踪 - 详情(Dubbo服务调用,右边Annotations上的4个点依次代表Client Start、Server Start、Server Finish、Client Finish,可以清晰的看出服务端执行时间、客户端调用时间、请求和响应的网络传输时间):

链路跟踪 - 详情(Web请求,Tags记录了部分Web请求信息):

链路跟踪 - 详情(JDBC操作,记录了SQL语句):