[jaeger] 三、实现一个分布式调用(OkHttp+SpringBoot)

栏目: Java · 发布时间: 5年前

内容简介:很多情况,在这种情况下,我们通常通过增加额外的本部分通过构建一个目前最火的

很多情况, trace 是分布在不同的应用中的,最常用的远程调用方式就是 Http

在这种情况下,我们通常通过增加额外的 Http Header 传递Trace信息,然后将其组织起来。

本部分通过构建一个目前最火的 SpringBoot 服务端,然后通过 OkHttp3 进行调用,来展示分布式调用链的组织方式。

需要的知识:

  • 创建一个简单的SpringBoot应用
  • 使用OkHttp3发起一个Post请求
  • 了解OpenTracing的inject和extract函数

inject & extract函数

这是两个为了跨进程追踪而生的两个函数,力求寻找一种通用的trace传输方式。这是两个强大的函数,它进行了一系列抽象,使得OpenTracing协议不用和特定的实现进行耦合。

  • Carrier 携带trace信息的载体,下文中将自定义一个
  • inject 将额外的信息 注入 到相应的载体中
  • extract 将额外的信息从载体中 提取 出来

其实,这个载体大多数都是用一个Map(具体是text map)来实现;或者是其他二进制方式实现。

在本文中,我们就是用了text map,载体的底层就是http头信息(也可以通过request params进行传递)。

创建一个Server端

maven依赖

首先,通过bom方式import进spring boot的相关配置。

  • 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应用

创建一个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客户端调用

创建载体

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已经生成了。

[jaeger] 三、实现一个分布式调用(OkHttp+SpringBoot)

本文展示了创建分布式调用链的一般方式。类比此法,可以很容易的写出基于 HttpClient 组件的客户端组件。

接下来,我们将使用Spring的拿手锏Aop,来封装通过Feign接口调用的SpringCloud服务。你会发现,实现一个类似Sleuth的客户端收集器,还是蛮简单的。

[jaeger] 三、实现一个分布式调用(OkHttp+SpringBoot)


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

电脑报(上下册)

电脑报(上下册)

电脑报社 / 西南师范大学出版社 / 2006-12-01 / 45.00元

全套上、下两册,浓缩2006年电脑报精华文章。附录包含70余篇简明IT应用指南,覆盖软件、硬盘、数码、网络四大领域。配赠权威实用的2006-2007中国计算机年鉴DVD光盘,近1.4GB海量信息与资源超值奉献。8大正版超值软件,涵盖系统维护、系统安全、办公应用、多媒体娱乐等四大领域。微软、腾讯、友立等知名厂商,新年献礼。提供2006-2007全系列硬件、数码产品资讯,兼具知识性与资料性。官方网站全......一起来看看 《电脑报(上下册)》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具