Spring Cloud之zipkin日志文件化

方案背景

Zipkin 作为一个链路跟踪工具,用来在微服务中记录每个链路调用的用时情况,链接调用情况包括:

(1)服务作为客户端从发起请求(cs)到收到响应(cr)的时间。

该情况一般在zuul网关或者存在一个服务调用另一个的时间存在。

(2)服务作为服务端从接收到请求(sr)到把将结果发送给客户端的时间(ss,即outputStream flush前的时间)。

一般来说,如果涉及一个服务调用另一个服务,则一次请求会产生至少两条json格式的日志。不涉及到调用其它服务的服务,则会产生一条日志。对于所产生的日志,通过异步http请求的方式发送到zipkin server中进行入库或存到zipkin server的缓存中。之后便可以通过zipkin server进行调用链的分析及跟踪。

基于以上的分析,如果基于默认的解决方案,调用量大的情况下,会导致带来以下问题:

(1)对于zipkin server来说,可能会由于频繁的接收数据而变慢或者直接瘫痪。

(2)对于zipkin client来说(微服务使用端),如果zipkin server响应不及时会导致异步的日志发送请求堆积。堆积会带来对内存的消耗,另外当堆积满了,会导致日志丢失。

(3)发送日志大小的限制。在默认的实现中(HttpZipkinSpanReporter),对于发送日志的数据量有限制大小(5 * 1024 * 1024)。如果日志量超过,则会丢失该日志。

(4)如果zipkin server挂了,会导致大量的数据丢失,还会使得zipkin client由于尝试连接zipkin server端而变得慢。

(5)如果对zipkin client产生的影响,就会影响到业务的正常访问。

基于上述分析,我们需要一种不会导致日志缺失并且对业务访问影响最小的方案。

实现分析

要达到不会导致日志缺失并且对业务访问影响最小,最简单的方式便是将zipkin的日志写到日志文件中。之后再通过分布式日志收集工具将日志收集起来。当需要看某个日志id的调用链路时,我们再通过日志收集工具把相关数据找出,然后用数据导入的方式将日志导致到zipkin servier中。最后,我们便可以利用zipkin server进行调用链路的分析了。

  • 如何实现zipkin日志文件化

通过分析zipkin的日志发送机制,发现其主要是通过实现ZipkinSpanReporter接口的HttpZipkinSpanReporter来发送。而HttpZipkinSpanReporter在自动化配置中为以下:

注意,ConditionalOnMissingBean,这是条件化配置,意思是说当没有存在指定类型的bean(ZipkinSpanReporter)才会实例化该bean。因此,我们可以通过实现ZipkinSpanReporter接口的方式,在实现中通过写日志的方式来将日志写到文件中。

  • 如何将zipkin日志收集起来

可以采用 logstash(日志收集)+ElasticSearch(数据存储)。当日志收集起来后,便可以通过elasticsearch 的HTTP RESTful API,根据日志id查询相关的数据。

  • 如何将zipkin日志导入到zipkin server中 

将日志中json格式的数据,通过HttpZipkinSpanReporter来向zipkin server进行数据导入。

方案实施

在接口实现中,将要记录的数据序列化成json格式,然后记录到日志文件中。

 源码:

package com.yao.springcloud.sleuth;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter;
import zipkin.Span;


public class TraceZipkinSpanReporter implements ZipkinSpanReporter {
    private Logger LOGGER = LoggerFactory.getLogger(TraceZipkinSpanReporter
            .class);


    @Override
    public void report(Span span) {
        if(span != null){
            LOGGER.info(span.toString());
        }

    }
}
  • 将日志数据导入zipkin server

spring-cloud-sleuth-zipkin提供了一个将日志导入zipkin server的方式,即HttpZipkinSpanReporter。我们只需要做以下事情:

(1)将zipkin 日志内容转换成HttpZipkinSpanReporter的发送对象。

(2)调用HttpZipkinSpanReporter进行发送。

 

源码:

package com.yao.sprintcloud.test;

import org.junit.Test;
import org.springframework.cloud.sleuth.metric.NoOpSpanMetricReporter;
import org.springframework.cloud.sleuth.zipkin.HttpZipkinSpanReporter;
import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter;
import zipkin.Codec;
import zipkin.Span;

public class ZipkinImprotTest {
    @Test
    public void testPostDataToZipkinServer(){
        String url = "http://localhost:8087";
        //可以同时发送多条数据,以下是两条数据
        String data = "{\"traceId\":\"b27d50e739a9241b\",\"id\":\"2641fb830471fe4e\",\"name\":\"http:/saybhello\",\"parentId\":\"b5d9d2f28ca9a6ee\",\"timestamp\":1546652853045000,\"duration\":19000,\"annotations\":[{\"timestamp\":1546652853045000,\"value\":\"cs\",\"endpoint\":{\"serviceName\":\"servicea\",\"ipv4\":\"192.168.1.105\",\"port\":8084}},{\"timestamp\":1546652853064000,\"value\":\"cr\",\"endpoint\":{\"serviceName\":\"servicea\",\"ipv4\":\"192.168.1.105\",\"port\":8084}}],\"binaryAnnotations\":[{\"key\":\"http.host\",\"value\":\"192.168.1.105\",\"endpoint\":{\"serviceName\":\"servicea\",\"ipv4\":\"192.168.1.105\",\"port\":8084}},{\"key\":\"http.method\",\"value\":\"GET\",\"endpoint\":{\"serviceName\":\"servicea\",\"ipv4\":\"192.168.1.105\",\"port\":8084}},{\"key\":\"http.path\",\"value\":\"/saybhello\",\"endpoint\":{\"serviceName\":\"servicea\",\"ipv4\":\"192.168.1.105\",\"port\":8084}},{\"key\":\"http.url\",\"value\":\"http://192.168.1.105:8085/saybhello?name=ServiceA\",\"endpoint\":{\"serviceName\":\"servicea\",\"ipv4\":\"192.168.1.105\",\"port\":8084}},{\"key\":\"spring.instance_id\",\"value\":\"192.168.1.105:serviceA:8084\",\"endpoint\":{\"serviceName\":\"servicea\",\"ipv4\":\"192.168.1.105\",\"port\":8084}}]}\n" +
                "{\"traceId\":\"b27d50e739a9241b\",\"id\":\"b5d9d2f28ca9a6ee\",\"name\":\"http:/sayhello\",\"parentId\":\"b27d50e739a9241b\",\"annotations\":[{\"timestamp\":1546652852989000,\"value\":\"sr\",\"endpoint\":{\"serviceName\":\"servicea\",\"ipv4\":\"192.168.1.105\",\"port\":8084}},{\"timestamp\":1546652854469000,\"value\":\"ss\",\"endpoint\":{\"serviceName\":\"servicea\",\"ipv4\":\"192.168.1.105\",\"port\":8084}}],\"binaryAnnotations\":[{\"key\":\"lc\",\"value\":\"hystrix\",\"endpoint\":{\"serviceName\":\"servicea\",\"ipv4\":\"192.168.1.105\",\"port\":8084}},{\"key\":\"mvc.controller.class\",\"value\":\"ServiceAController\",\"endpoint\":{\"serviceName\":\"servicea\",\"ipv4\":\"192.168.1.105\",\"port\":8084}},{\"key\":\"mvc.controller.method\",\"value\":\"sayhello\",\"endpoint\":{\"serviceName\":\"servicea\",\"ipv4\":\"192.168.1.105\",\"port\":8084}},{\"key\":\"spring.instance_id\",\"value\":\"192.168.1.105:serviceA:8084\",\"endpoint\":{\"serviceName\":\"servicea\",\"ipv4\":\"192.168.1.105\",\"port\":8084}},{\"key\":\"thread\",\"value\":\"hystrix-serviceb-2\",\"endpoint\":{\"serviceName\":\"servicea\",\"ipv4\":\"192.168.1.105\",\"port\":8084}}]}";

        Span span =  Codec.JSON.readSpan(data.getBytes());
        postDataToZipkin(url, span);
    }

    private void postDataToZipkin(String url, Span data){
        ZipkinSpanReporter reporter = new HttpZipkinSpanReporter(url, 1,
                false, new NoOpSpanMetricReporter());
        reporter.report(data);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

源码地址:https://github.com/micat707/myprojects/tree/master/spring-cloud-solutions

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值