`

zipkin链路监控跟踪

阅读更多
zipkin链路跟踪:
多服务之间调用,服务与服务之间监控,原理是在发送和接收请求时由brave异步传输(HTTP, Kafka和Scribe)给zipkin传输记录,zipkin把记录整理存储起来(可以内存,DB,es等)。
同时zipkin有自己的ui可以展示给我们使用,调查日志。
另外zipkin也可以使用brave-mysql类库跟踪业务DB,增加MySQLStatementInterceptorManagementBean方法实例 ,原理是在jdbc时进行拦截把记录放到zipkin。

OkHttpClient调用发起http请求。在每次发起请求时则需要通过brave记录Span信息,并异步传递给zipkin
作为被调用方(服务端)也同样需要完成以上操作。

zipkin链路跟踪mysql:
maven引用brave-mysql类库,增加MySQLStatementInterceptorManagementBean实例 ,原理是在jdbc时进行拦截把记录放到zipkin。

cs:Client Send,客户端发起请求;
sr:Server Receive,服务器接受请求,开始处理;
ss:Server Send,服务器完成处理,给客户端应答;
cr:Client Receive,客户端接受应答从服务器;



zipkin参考:
zipkin入门和brave源码:
https://blog.csdn.net/john1337/article/details/71127056/
https://blog.csdn.net/apei830/article/details/78722180
brave-mysql:
https://www.cnblogs.com/java-zhao/p/5858138.html
https://www.h3399.cn/201805/576621.html

google dapper







package com.pingan.haofang.qypms.common.configuration.zipkin;

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.mysql.MySQLStatementInterceptorManagementBean;
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;

/**
 * @Author: huaxiuming
 * @Description: [此处填写类的描述信息]
 * @Date: Created in 15:22 2019-3-4
 * @Modified By:
 */
@Configuration
public class SqlSpanConfig {

    @Autowired
    private ZipkinProperties properties;

//    @Bean
//    HttpResponseInjectingTraceFilter responseInjectingTraceFilter(Brave brave) {
//        return new HttpResponseInjectingTraceFilter(brave.localTracer());
//    }

    @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
    public MySQLStatementInterceptorManagementBean mySQLStatementInterceptorManagementBean(Brave brave) {
        return new MySQLStatementInterceptorManagementBean(brave.clientTracer());
    }

}


package com.pingan.haofang.qypms.common.configuration.zipkin;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.web.filter.GenericFilterBean;

import com.alibaba.fastjson.JSONObject;


/**
* @Author: huaxiuming
* @Description: [此处填写类的描述信息]
* @Date: Created in 15:21 2019-3-4
* @Modified By:
*/
public class HttpResponseInjectingTraceFilter extends GenericFilterBean {

    private static final Logger logger = LoggerFactory
            .getLogger(HttpResponseInjectingTraceFilter.class);

    private final Tracer tracer;

    public HttpResponseInjectingTraceFilter(Tracer tracer) {
        this.tracer = tracer;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain filterChain) throws IOException, ServletException {
        Span currentSpan = this.tracer.getCurrentSpan();
        HttpServletRequest httpRequest = (HttpServletRequest) request;

        // 添加头部信息
        Map<String, String> headerMap = new HashMap<String, String>();
        Enumeration<String> enume = httpRequest.getHeaderNames();
        while (enume.hasMoreElements()) {
            String key = enume.nextElement();
            String value = httpRequest.getHeader(key);
            headerMap.put(key, value);
        }
        if (headerMap.size() > 0) {
            currentSpan.tag("http.head", JSONObject.toJSONString(headerMap));
        }


        //方法名
        String method = httpRequest.getMethod();
        currentSpan.tag("request.method", method);

        RequestWrapper wrapperRequest = new RequestWrapper(httpRequest);// 转换成代理类
        ResponseWrapper wrapperResponse = new ResponseWrapper(
                (HttpServletResponse) response);// 转换成代理类
        // 拦截返回
        filterChain.doFilter(wrapperRequest, wrapperResponse);

        // 添加参数信息
        String params = this.getRequestParameter(wrapperRequest);
        currentSpan.tag("http.params", params);

        // 添加返回值信息
        byte[] content = wrapperResponse.getContent();// 获取返回值
        // 判断是否有值
        if (content.length > 0) {
            try {
                String result = new String(content, "UTF-8");
                currentSpan.tag("http.result", result);
            } catch (Exception e) {
                logger.error("添加返回值信息异常", e);
            }
        }

        // 把返回值输出到客户端
        ServletOutputStream out = null;
        try {
            out = response.getOutputStream();
        } catch (Exception e) {
        } finally {
            try {
                if (out != null) {
                    out.write(content);
                    out.flush();
                    out.close();
                }
            } catch (IOException e) {
            }
        }
    }

    /**
     * 方法功能说明: 获取请求参数包括form表单和json参数
     */
    public String getRequestParameter(RequestWrapper wrapperRequest) {
        if (null == wrapperRequest) {
            return null;
        }
        String params = null;
        String method = wrapperRequest.getMethod();
        if (StringUtils.isNotBlank(method) && "GET".equals(method.toUpperCase())) {
            // 获取请求体中的字符串(get)
            params = wrapperRequest.getQueryString();
            try {
                if (StringUtils.isNotBlank(params)) {
                    params = URLDecoder.decode(params, "UTF-8");
                }
            } catch (UnsupportedEncodingException e) {
                // Logger.error("获取到的请求参数解码错误 : {}", e.getMessage());
            }
            return params;
        } else {
            return wrapperRequest.getBodyString(wrapperRequest);
        }
    }

}

package com.pingan.haofang.qypms.common.configuration.zipkin;

import org.springframework.cloud.sleuth.Tracer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author: huaxiuming
 * @Description: [此处填写类的描述信息]
 * @Date: Created in 15:22 2019-3-4
 * @Modified By:
 */
@Configuration
public class HttpSpanConfig {

    @Bean
    HttpResponseInjectingTraceFilter responseInjectingTraceFilter(Tracer tracer) {
        return new HttpResponseInjectingTraceFilter(tracer);
    }

}



package com.pingan.haofang.qypms.common.configuration.zipkin;

import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.SpanTextMap;
import org.springframework.cloud.sleuth.instrument.web.ZipkinHttpSpanInjector;


/**
 * @Author: huaxiuming
 * @Description: [此处填写类的描述信息]
 * @Date: Created in 15:20 2019-3-4
 * @Modified By:
 */
public class CustomHttpServletResponseSpanInjector extends ZipkinHttpSpanInjector {

    @Override
    public void inject(Span span, SpanTextMap carrier) {
        super.inject(span, carrier);
        carrier.put(Span.TRACE_ID_NAME, span.traceIdString());
        carrier.put(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
    }

}


package com.pingan.haofang.qypms.common.configuration.zipkin;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Enumeration;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.lang3.StringUtils;
import org.springframework.util.StreamUtils;

/**
 * @Author: huaxiuming
 * @Description: [请求输出代理类]
 * @Date: Created in 15:23 2019-3-4
 * @Modified By:
 */
public class RequestWrapper extends HttpServletRequestWrapper {

    private final byte[] body;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        String sessionStream = getBodyString(request);
        body = sessionStream.getBytes(Charset.forName("UTF-8"));
    }

    /**
     * 获取请求Body
     *
     * @param request
     * @return
     */
    public String getBodyString(final ServletRequest request) {
        String contentType = request.getContentType();
        String bodyString = "";

        if (StringUtils.isNotBlank(contentType) &&
                (contentType.contains("multipart/form-data") ||
                        contentType.contains("x-www-form-urlencoded"))) {

            Enumeration<String> pars = request.getParameterNames();

            while (pars.hasMoreElements()) {

                String n = pars.nextElement();

                bodyString += n + "=" + request.getParameter(n) + "&";

            }

            bodyString = bodyString.endsWith("&") ? bodyString.substring(0, bodyString.length() - 1) : bodyString;

            return bodyString;

        }

        try {
            byte[] byteArray = StreamUtils.copyToByteArray(request.getInputStream());
            bodyString = new String(byteArray, "UTF-8");
        } catch (IOException e) {
        }

        return bodyString;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        final ByteArrayInputStream bais = new ByteArrayInputStream(body);

        return new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return bais.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }

}

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics