1. 程式人生 > >使用Zipkin 和 Brave 實現http(springmvc)服務呼叫跟蹤(二)

使用Zipkin 和 Brave 實現http(springmvc)服務呼叫跟蹤(二)

上次講了Brave為Spring提供的Servlet攔截器(ServletHandlerInterceptor)及Rest(BraveClientHttpRequestInterceptor)模板的攔截器,向zipkin報告監控資料,其中,BraveClientHttpRequestInterceptor負責cs與cr的處理,ServletHandlerInterceptor負責sr與ss的處理,並沒有記錄到請求引數的監控及使用的是預設的span名稱,那如何才能記錄請求引數及修改span名稱。

需要實現自定義攔截器,我選擇在服務端做處理記錄請求引數的過程,先看看Brave提供服務端攔截器ServletHandlerInterceptor的原始碼實現,發現使用HttpServerRequestAdapter介面卡,

再跟蹤HttpServerRequestAdapter原始碼,在其中實現ServerRequestAdapter介面,包括跟蹤資料的獲取,span名稱的獲取及鍵值資料的新增

那我們除了實現自定義攔截器,還需要實現自己的介面卡,介面卡實現介面ServerRequestAdapter,在獲取鍵值資料的方法中實現新增請求引數到keyvalue,在span名稱獲取方法返回自己span名稱,即完成了請求引數的新增及span名稱的自定義

以下實現在上篇中提供的git的工程中實現,可直接下載,git地址:https://github.com/blacklau/brave-webmvc-example

1、攔截器的實現

package brave.interceptor;

import static com.github.kristofa.brave.internal.Util.checkNotNull;

import java.net.URI;
import java.net.URISyntaxException;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.github.kristofa.brave.Brave;
import com.github.kristofa.brave.ServerRequestInterceptor;
import com.github.kristofa.brave.ServerResponseInterceptor;
import com.github.kristofa.brave.ServerSpan;
import com.github.kristofa.brave.ServerSpanThreadBinder;
import com.github.kristofa.brave.http.DefaultSpanNameProvider;
import com.github.kristofa.brave.http.HttpResponse;
import com.github.kristofa.brave.http.HttpServerRequest;
import com.github.kristofa.brave.http.HttpServerRequestAdapter;
import com.github.kristofa.brave.http.HttpServerResponseAdapter;
import com.github.kristofa.brave.http.SpanNameProvider;
import com.github.kristofa.brave.spring.ServletHandlerInterceptor;

import brave.adapter.CustomServerRequestAdapter;

@Configuration
public class CustomServletHandlerInterceptor  extends HandlerInterceptorAdapter {

    static final String HTTP_SERVER_SPAN_ATTRIBUTE = ServletHandlerInterceptor.class.getName() + ".customserver-span";

    /** Creates a tracing interceptor with custom */
    public static CustomServletHandlerInterceptor create(Brave brave) {
        return new Builder(brave).build();
    }

    public static Builder builder(Brave brave) {
        return new Builder(brave);
    }

    public static final class Builder {
        final Brave brave;
        SpanNameProvider spanNameProvider = new DefaultSpanNameProvider();

        Builder(Brave brave) { // intentionally hidden
            this.brave = checkNotNull(brave, "brave");
        }

        public Builder spanNameProvider(SpanNameProvider spanNameProvider) {
            this.spanNameProvider = checkNotNull(spanNameProvider, "spanNameProvider");
            return this;
        }

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

    private final ServerRequestInterceptor requestInterceptor;
    private final ServerResponseInterceptor responseInterceptor;
    private final ServerSpanThreadBinder serverThreadBinder;
    private final SpanNameProvider spanNameProvider;

    @Autowired // internal
    CustomServletHandlerInterceptor(SpanNameProvider spanNameProvider, Brave brave) {
        this(builder(brave).spanNameProvider(spanNameProvider));
    }

    CustomServletHandlerInterceptor(Builder b) { // intentionally hidden
        this.requestInterceptor = b.brave.serverRequestInterceptor();
        this.responseInterceptor = b.brave.serverResponseInterceptor();
        this.serverThreadBinder = b.brave.serverSpanThreadBinder();
        this.spanNameProvider = b.spanNameProvider;
    }

    /**
     * @deprecated please use {@link #create(Brave)} or {@link #builder(Brave)}
     */
    @Deprecated
    public CustomServletHandlerInterceptor(ServerRequestInterceptor requestInterceptor, ServerResponseInterceptor responseInterceptor, SpanNameProvider spanNameProvider, final ServerSpanThreadBinder serverThreadBinder) {
        this.requestInterceptor = requestInterceptor;
        this.spanNameProvider = spanNameProvider;
        this.responseInterceptor = responseInterceptor;
        this.serverThreadBinder = serverThreadBinder;
    }

    @Override
    public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
        requestInterceptor.handle(new CustomServerRequestAdapter(request, spanNameProvider));

        return true;
    }

    @Override
    public void afterConcurrentHandlingStarted(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
        request.setAttribute(HTTP_SERVER_SPAN_ATTRIBUTE, serverThreadBinder.getCurrentServerSpan());
        serverThreadBinder.setCurrentSpan(null);
    }

    @Override
    public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) {

        final ServerSpan span = (ServerSpan) request.getAttribute(HTTP_SERVER_SPAN_ATTRIBUTE);

        if (span != null) {
            serverThreadBinder.setCurrentSpan(span);
        }

       responseInterceptor.handle(new HttpServerResponseAdapter(new HttpResponse() {
           @Override
           public int getHttpStatusCode() {
               return response.getStatus();
           }
       }));
    }
}

2、介面卡的實現
package brave.adapter;

import static com.github.kristofa.brave.IdConversion.convertToLong;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import com.github.kristofa.brave.KeyValueAnnotation;
import com.github.kristofa.brave.ServerRequestAdapter;
import com.github.kristofa.brave.SpanId;
import com.github.kristofa.brave.TraceData;
import com.github.kristofa.brave.http.BraveHttpHeaders;
import com.github.kristofa.brave.http.SpanNameProvider;

import zipkin.TraceKeys;

public class CustomServerRequestAdapter  implements ServerRequestAdapter {
    private final HttpServletRequest request;

    public CustomServerRequestAdapter(HttpServletRequest request, SpanNameProvider spanNameProvider) {
        this.request = request;
    }

    @Override
    public TraceData getTraceData() {
        String sampled = request.getHeader(BraveHttpHeaders.Sampled.getName());
        String parentSpanId = request.getHeader(BraveHttpHeaders.ParentSpanId.getName());
        String traceId = request.getHeader(BraveHttpHeaders.TraceId.getName());
        String spanId = request.getHeader(BraveHttpHeaders.SpanId.getName());

        // Official sampled value is 1, though some old instrumentation send true
        Boolean parsedSampled = sampled != null
            ? sampled.equals("1") || sampled.equalsIgnoreCase("true")
            : null;

        if (traceId != null && spanId != null) {
            return TraceData.create(getSpanId(traceId, spanId, parentSpanId, parsedSampled));
        } else if (parsedSampled == null) {
            return TraceData.EMPTY;
        } else if (parsedSampled.booleanValue()) {
            // Invalid: The caller requests the trace to be sampled, but didn't pass IDs
            return TraceData.EMPTY;
        } else {
            return TraceData.NOT_SAMPLED;
        }
    }

    @Override
    public String getSpanName() {
        return "custom spanName";
    }

    @Override
    public Collection<KeyValueAnnotation> requestAnnotations() {
        List<KeyValueAnnotation> kvs = new ArrayList<KeyValueAnnotation>();
        
    	Map<String, String[]> params = this.request.getParameterMap();
    	for(String key:params.keySet()){
    		KeyValueAnnotation kv = KeyValueAnnotation.create(key, params.get(key)[0]);
    		kvs.add(kv);
    	}
    	
        KeyValueAnnotation uriAnnotation = KeyValueAnnotation.create(
                TraceKeys.HTTP_URL, request.getRequestURI().toString());
        kvs.add(uriAnnotation);
        return kvs;
    }

    static SpanId getSpanId(String traceId, String spanId, String parentSpanId, Boolean sampled) {
        return SpanId.builder()
            .traceIdHigh(traceId.length() == 32 ? convertToLong(traceId, 0) : 0)
            .traceId(convertToLong(traceId))
            .spanId(convertToLong(spanId))
            .sampled(sampled)
            .parentId(parentSpanId == null ? null : convertToLong(parentSpanId)).build();
   }
}

瀏覽器輸入測試: http://localhost:80810brave-webmvc-example/a?service=account.user.login

        開啟zipkin:  http://localhost:9411/, 檢視跟蹤結果