Zipkin的使用

我这边在初识Zipkin的时候使用了这样一个策略,在阿里云服务器上启动zipkin,然后在本地4个测试项目上配置Zipkin节点,进行一个服务间调用的监控。

1、首先在服务器上启动Zipkin

这个详见上一章节内容

2、建立测试项目

建立4个项目zipkin,zipkin2,zipkin3,zipkin4,下面以zipkin项目为例,brave作为各调用链路,只需要负责将指定格式的数据发送给zipkin即可,利用brave可快捷完成操作,这里利用idea创建springboot工程:

1)、添加pom依赖
<dependency>
        <groupId>io.zipkin.bravegroupId>
        <artifactId>brave-coreartifactId>
        <version>3.9.0version>
    dependency>
    
    <dependency>
        <groupId>io.zipkin.bravegroupId>
        <artifactId>brave-httpartifactId>
        <version>3.9.0version>
    dependency>
    <dependency>
        <groupId>io.zipkin.bravegroupId>
        <artifactId>brave-spancollector-httpartifactId>
        <version>3.9.0version>
    dependency>
    <dependency>
        <groupId>io.zipkin.bravegroupId>
        <artifactId>brave-web-servlet-filterartifactId>
        <version>3.9.0version>
    dependency>

    <dependency>
        <groupId>io.zipkin.bravegroupId>
        <artifactId>brave-okhttpartifactId>
        <version>3.9.0version>
    dependency>
2)、添加配置项

可以在配置文件application.properties中配置相关信息

com.zipkin.serviceName=service1
com.zipkin.url=http://106.14.150.41:9411
com.zipkin.connectTimeout=6000
com.zipkin.readTimeout=6000
com.zipkin.flushInterval=1
com.zipkin.compressionEnabled=true
server.port=8080

这里的http://106.14.150.41:9411你需要替换成你的Zipkin所在的ip。

3)、添加自定义配置项对应的java类
package com.example.zipkin;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * @description: 自定义配置项对应配置类
 * @author: chenyang
 * @create: 2018-05-23
 **/
@Data
@Configuration
@ConfigurationProperties(prefix = "com.zipkin")
public class ZipkinProperties {

    private String serviceName;

    private String url;

    private int connectTimeout;

    private int readTimeout;

    private int flushInterval;

    private boolean compressionEnabled;

}
4)、创建Zipkin的配置类
package com.example.zipkin4;

import com.github.kristofa.brave.Brave;
import com.github.kristofa.brave.EmptySpanCollectorMetricsHandler;
import com.github.kristofa.brave.Sampler;
import com.github.kristofa.brave.SpanCollector;
import com.github.kristofa.brave.http.DefaultSpanNameProvider;
import com.github.kristofa.brave.http.HttpSpanCollector;
import com.github.kristofa.brave.okhttp.BraveOkHttpRequestResponseInterceptor;
import com.github.kristofa.brave.servlet.BraveServletFilter;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @description: Zipkin配置类
 * @author: chenyang
 * @create: 2018-05-23
 **/
@Configuration
public class ZipkinConfig {

    @Autowired
    private ZipkinProperties properties;


    @Bean
    public SpanCollector spanCollector() {
        HttpSpanCollector.Config config = HttpSpanCollector.Config.builder().connectTimeout(properties.getConnectTimeout()).readTimeout(properties.getReadTimeout())
                .compressionEnabled(properties.isCompressionEnabled()).flushInterval(properties.getFlushInterval()).build();
        return HttpSpanCollector.create(properties.getUrl(), config, new EmptySpanCollectorMetricsHandler());
    }


    @Bean
    public Brave brave(SpanCollector spanCollector){
        Brave.Builder builder = new Brave.Builder(properties.getServiceName());  //指定state
        builder.spanCollector(spanCollector);
        builder.traceSampler(Sampler.ALWAYS_SAMPLE);
        Brave brave = builder.build();
        return brave;
    }

    @Bean
    public BraveServletFilter braveServletFilter(Brave brave){
        BraveServletFilter filter = new BraveServletFilter(brave.serverRequestInterceptor(),brave.serverResponseInterceptor(),new DefaultSpanNameProvider());
        return filter;
    }

    @Bean
    public OkHttpClient okHttpClient(Brave brave){
        OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(new BraveOkHttpRequestResponseInterceptor(brave.clientRequestInterceptor(), brave.clientResponseInterceptor(), new DefaultSpanNameProvider()))
                .build();
        return client;
    }
}
bean介绍(可以先跳过)
  • SpanCollector
 @Bean
    public SpanCollector spanCollector() {
        HttpSpanCollector.Config config = HttpSpanCollector.Config.builder().connectTimeout(properties.getConnectTimeout()).readTimeout(properties.getReadTimeout())
                .compressionEnabled(properties.isCompressionEnabled()).flushInterval(properties.getFlushInterval()).build();
        return HttpSpanCollector.create(properties.getUrl(), config, new EmptySpanCollectorMetricsHandler());
    }

从名称上看HttpSpanCollector是基于http的span收集器,因此超时配置是必须的,默认给出的超时时间较长,flushInterval表示span的传递
间隔,实际为定时任务执行的间隔时间.在HttpSpanCollector中覆写了父类方法sendSpans

@Override
  protected void sendSpans(byte[] json) throws IOException {
    // intentionally not closing the connection, so as to use keep-alives
    HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
    connection.setConnectTimeout(config.connectTimeout());
    connection.setReadTimeout(config.readTimeout());
    connection.setRequestMethod("POST");
    connection.addRequestProperty("Content-Type", "application/json");
    if (config.compressionEnabled()) {
      connection.addRequestProperty("Content-Encoding", "gzip");
      ByteArrayOutputStream gzipped = new ByteArrayOutputStream();
      try (GZIPOutputStream compressor = new GZIPOutputStream(gzipped)) {
        compressor.write(json);
      }
      json = gzipped.toByteArray();
    }
    connection.setDoOutput(true);
    connection.setFixedLengthStreamingMode(json.length);
    connection.getOutputStream().write(json);

    try (InputStream in = connection.getInputStream()) {
      while (in.read() != -1) ; // skip
    } catch (IOException e) {
      try (InputStream err = connection.getErrorStream()) {
        if (err != null) { // possible, if the connection was dropped
          while (err.read() != -1) ; // skip
        }
      }
      throw e;
    }
  }
}

可以看出最终span信息是通过HttpURLConnection实现的,同样道理就可以推理brave对brave-spring-resttemplate-interceptors模块的实现,
只是换了一种http封装。

  • Brave
@Bean
    public Brave brave(SpanCollector spanCollector){
        Brave.Builder builder = new Brave.Builder(properties.getServiceName());  //指定state
        builder.spanCollector(spanCollector);
        builder.traceSampler(Sampler.ALWAYS_SAMPLE);
        Brave brave = builder.build();
        return brave;
    }

Brave类包装了各种工具类

public Brave build() {
    return new Brave(this);
}

创建一个Brave

private Brave(Builder builder) {
        serverTracer = ServerTracer.builder()
                .randomGenerator(builder.random)
                .spanCollector(builder.spanCollector)
                .state(builder.state)
                .traceSampler(builder.sampler).build();

        clientTracer = ClientTracer.builder()
                .randomGenerator(builder.random)
                .spanCollector(builder.spanCollector)
                .state(builder.state)
                .traceSampler(builder.sampler).build();

        localTracer = LocalTracer.builder()
                .randomGenerator(builder.random)
                .spanCollector(builder.spanCollector)
                .spanAndEndpoint(SpanAndEndpoint.LocalSpanAndEndpoint.create(builder.state))
                .traceSampler(builder.sampler).build();

        serverRequestInterceptor = new ServerRequestInterceptor(serverTracer);
        serverResponseInterceptor = new ServerResponseInterceptor(serverTracer);
        clientRequestInterceptor = new ClientRequestInterceptor(clientTracer);
        clientResponseInterceptor = new ClientResponseInterceptor(clientTracer);
        serverSpanAnnotationSubmitter = AnnotationSubmitter.create(SpanAndEndpoint.ServerSpanAndEndpoint.create(builder.state));
        serverSpanThreadBinder = new ServerSpanThreadBinder(builder.state);
        clientSpanThreadBinder = new ClientSpanThreadBinder(builder.state);
    }

封装了*Tracer,*Interceptor,*Binder等。其中 serverTracer当服务作为服务端时处理span信息,clientTracer当服务作为客户端时处理span信息

  • Filter

BraveServletFilter是http模块提供的拦截器功能,传递serverRequestInterceptor,serverResponseInterceptor,spanNameProvider等参数
其中spanNameProvider表示如何处理span的名称,默认使用method名称,spring boot中申明的filter bean 默认拦截所有请求

详情见:https://blog.csdn.net/liaokailin/article/details/52077620


5)、创建Controller对外提供服务
package com.example.zipkin;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @description: ${description}
 * @author: chenyang
 * @create: 2018-05-23
 **/
@RestController
public class HomeController {

    @Autowired
    private OkHttpClient client;

    private Random random = new Random();

    @RequestMapping("/start")
    public String start() throws InterruptedException, IOException {
        int sleep= random.nextInt(100);
        TimeUnit.MILLISECONDS.sleep(sleep);
        Request request = new Request.Builder().url("http://localhost:8081/service2").get().build();
        Response response = client.newCall(request).execute();
        return "[service1]:" + response.body().toString();
    }
}

HomeController中利用OkHttpClient调用发起http请求。在每次发起请求时则需要通过brave记录Span信息,并异步传递给zipkin
到这里,service1就写好了。同样的道理,其他的工程就修改配置文件(调整com.zipkin.serviceName,以及server.port)以及controller对应的方法构造若干服务。
下面是其他zipkin区别于第一个项目的地方:
zipkin2

  • HomeController
@RequestMapping("/service2")
    public String foo() throws InterruptedException, IOException {
        int sleep= random.nextInt(100);
        TimeUnit.MILLISECONDS.sleep(sleep);
        Request request = new Request.Builder().url("http://localhost:8082/service3").get().build();  //service3
        Response response = client.newCall(request).execute();
        String result = response.body().string();
        request = new Request.Builder().url("http://localhost:8083/service4").get().build();  //service4
        response = client.newCall(request).execute();
        result += response.body().string();
        return "[service2]:" + result;
    }
  • application.properties
com.zipkin.serviceName=service2
com.zipkin.url=http://106.14.150.41:9411
com.zipkin.connectTimeout=6000
com.zipkin.readTimeout=6000
com.zipkin.flushInterval=1
com.zipkin.compressionEnabled=true
server.port=8081

zipkin3

  • HomeController
@RequestMapping("/serivce3")
    public String bar() throws InterruptedException, IOException {  //service3 method
        int sleep= random.nextInt(100);
        TimeUnit.MILLISECONDS.sleep(sleep);
        return " [service3 sleep: " + sleep+" ms]";
    }
  • application.properties
com.zipkin.serviceName=service3
com.zipkin.url=http://106.14.150.41:9411
com.zipkin.connectTimeout=6000
com.zipkin.readTimeout=6000
com.zipkin.flushInterval=1
com.zipkin.compressionEnabled=true
server.port=8082

zipkin4

  • HomeController
@RequestMapping("/service4")
    public String tar() throws InterruptedException, IOException { //service4 method
        int sleep= random.nextInt(1000);
        TimeUnit.MILLISECONDS.sleep(sleep);
        return " [service4 sleep :" + sleep+" ms]";
    }
  • application.properties
com.zipkin.serviceName=service4
com.zipkin.url=http://106.14.150.41:9411
com.zipkin.connectTimeout=6000
com.zipkin.readTimeout=6000
com.zipkin.flushInterval=1
com.zipkin.compressionEnabled=true
server.port=8083

最后,分别启动zipkin,zipkin2,zipkin3,zipkin4,在游览器调用http://localhost:8080/start
登录web ui界面查看Zipkinhttp://你的部署的IP:9411/zipkin

Zipkin的使用_第1张图片

Zipkin的使用_第2张图片

你可能感兴趣的:(运维)