不少狀況,trace
是分佈在不一樣的應用中的,最經常使用的遠程調用方式就是Http
。java
在這種狀況下,咱們一般經過增長額外的Http Header
傳遞Trace信息,而後將其組織起來。git
本部分經過構建一個目前最火的SpringBoot
服務端,而後經過OkHttp3
進行調用,來展現分佈式調用鏈的組織方式。github
更多連載關注小姐姐味道,本文相關代碼見:web
https://github.com/sayhiai/example-jaeger-opentracing-tutorial-003
複製代碼
須要的知識:spring
建立一個簡單的SpringBoot應用
使用OkHttp3發起一個Post請求
瞭解OpenTracing的inject和extract函數
複製代碼
這是兩個爲了跨進程追蹤而生的兩個函數,力求尋找一種通用的trace傳輸方式。這是兩個強大的函數,它進行了一系列抽象,使得OpenTracing協議不用和特定的實現進行耦合。bash
Carrier 攜帶trace信息的載體,下文中將自定義一個
inject 將額外的信息`注入`到相應的載體中
extract 將額外的信息從載體中`提取`出來
複製代碼
其實,這個載體大多數都是用一個Map(具體是text map)來實現;或者是其餘二進制方式實現。app
在本文中,咱們就是用了text map,載體的底層就是http頭信息(也能夠經過request params進行傳遞)。curl
首先,經過bom方式import進spring boot的相關配置。maven
spring-boot-dependencies 2.1.3.RELEASE
複製代碼
而後,引入其餘依賴分佈式
opentracing-util 0.32.0
jaeger-client 0.35.0
logback-classic 1.2.3
spring-boot-starter-web 2.1.3.RELEASE
okhttp 3.14.1
複製代碼
建立一個SpringBoot應用,端口指定爲8888,並初始化默認的Tracer
。
@SpringBootApplication
@EnableAutoConfiguration
@ComponentScan(basePackages = { "com.sayhiai.example.jaeger.totorial03.controller",
})
public class App extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
@Bean
public JaegerTracer getJaegerTracer() {
return JaegerTracerHelper.initTracer("LoveYou");
}
}
複製代碼
在controller目錄下建立一個簡單的服務/hello
,經過request body傳遞參數。
關鍵代碼以下:
@PostMapping("/hello")
@ResponseBody
public String hello(@RequestBody String name,HttpServletRequest request) {
Map<String, String> headers = new HashMap<>();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String header = headerNames.nextElement();
headers.put(header, request.getHeader(header));
}
System.out.println(headers);
Tracer.SpanBuilder builder = null;
SpanContext parentSpanContext = tracer.extract(Format.Builtin.HTTP_HEADERS, new TextMapAdapter(headers));
if (null == parentSpanContext) {
builder = tracer.buildSpan("hello");
} else {
builder = tracer.buildSpan("hello").asChildOf(parentSpanContext);
}
Span span = builder.start();
複製代碼
首先拿到頭信息,並進行extract
,若是獲得的SpanContext
不爲空,則表明當前的請求是另一個應用發起的。在這種狀況下,咱們把請求的來源,做爲當前請求的parent
。
使用Curl進行調用,確保服務能正常運行。
curl -XPOST http://localhost:8888/hello -H "Content-Type:text/plain;charset=utf-8" -d "小姐姐味道"
複製代碼
OkHttp3是一個很是輕量級的類庫,它的header信息能夠經過如下代碼設置。
Request.Builder builder;
builder.addHeader(key, value);
複製代碼
咱們在上面提到,將要建立一個自定義的Carrier
,這裏經過繼承TextMap
,來實現一個。
public class RequestBuilderCarrier implements io.opentracing.propagation.TextMap {
private final Request.Builder builder;
RequestBuilderCarrier(Request.Builder builder) {
this.builder = builder;
}
@Override
public Iterator<Map.Entry<String, String>> iterator() {
throw new UnsupportedOperationException("carrier is write-only");
}
@Override
public void put(String key, String value) {
builder.addHeader(key, value);
}
}
複製代碼
使用OkHttp3發起一個簡單的Post請求便可。
public static void main(String[] args) {
Tracer tracer = JaegerTracerHelper.initTracer("Main");
String url = "http://localhost:8888/hello";
OkHttpClient client = new OkHttpClient();
Request.Builder request = new Request.Builder()
.url(url)
.post(RequestBody.create(MediaType.parse("text/plain;charset=utf-8"), "小姐姐味道"));
Span span = tracer.buildSpan("okHttpMainCall").start();
Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_CLIENT);
Tags.HTTP_METHOD.set(span, "POST");
Tags.HTTP_URL.set(span, url);
tracer.activateSpan(span);
tracer.inject(span.context(), Format.Builtin.HTTP_HEADERS, new RequestBuilderCarrier(request));
client.newCall(request.build()).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println(response.body().string());
}
});
span.finish();
}
複製代碼
注意,在方法中間,咱們使用inject函數,將trace信息附着在RequestBuilderCarrier
上進行傳遞。
這兩個函數,使用的就是jaeger的實現。見:
io.jaegertracing.internal.propagation.TextMapCodec
複製代碼
運行Main方法,查看Jaeger的後臺,能夠看到,咱們的分佈式Trace已經生成了。
本文展現了建立分佈式調用鏈的通常方式。類比此法,能夠很容易的寫出基於HttpClient
組件的客戶端組件。
接下來,咱們將使用Spring的拿手鐗Aop,來封裝經過Feign接口調用的SpringCloud服務。你會發現,實現一個相似Sleuth的客戶端收集器,仍是蠻簡單的。