我这边在初识Zipkin的时候使用了这样一个策略,在阿里云服务器上启动zipkin,然后在本地4个测试项目上配置Zipkin节点,进行一个服务间调用的监控。
这个详见上一章节内容
建立4个项目zipkin
,zipkin2
,zipkin3
,zipkin4
,下面以zipkin项目为例,brave作为各调用链路,只需要负责将指定格式的数据发送给zipkin即可,利用brave可快捷完成操作,这里利用idea创建springboot
工程:
<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>
可以在配置文件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。
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;
}
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;
}
}
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
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
界面查看Zipkin
:http://你的部署的IP:9411/zipkin