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

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

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

更多连载关注小姐姐味道,本文相关代码见:

https://github.com/sayhiai/example-jaeger-opentracing-tutorial-003
复制代码

需要的知识:

创建一个简单的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已经生成了。

End

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

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

实现一个分布式调用(OkHttp+SpringBoot)的更多相关文章

  1. 在这个应用中,我使用了 MQ 来处理异步流程、Redis 缓存热点数据、MySQL 持久化数据,还有就是在系统中调用另外一个业务系统的接口,对我的应用来说这些都是属于 RPC 调用,而 MQ、MySQL 持久化的数据也会存在于一个分布式文件系统中,他们之间的调用也是需要用 RPC 来完成数据交互的。

    在这个应用中,我使用了 MQ 来处理异步流程.Redis 缓存热点数据.MySQL 持久化数据,还有就是在系统中调用另外一个业务系统的接口,对我的应用来说这些都是属于 RPC 调用,而 MQ.MySQ ...

  2. 分享在Linux下使用OSGi.NET插件框架快速实现一个分布式服务集群的方法

    在这篇文章我分享了如何使用分层与模块化的方法来设计一个分布式服务集群.这个分布式服务集群是基于DynamicProxy.WCF和OSGi.NET插件框架实现的.我将从设计思路.目标和实现三方面来描述. ...

  3. 基于Cat的分布式调用追踪

    Cat是美团点评出的一款APM工具,同类的产品也有不少,知名的开源产品如zipkin和pinpoint:国内收费的产品如oneapm.考虑到Cat在互联网公司的应用比较广,因此被纳入选型队列,我也有幸 ...

  4. Dubbo[一个分布式服务框架

    http://alibaba.github.io/dubbo-doc-static/User+Guide-zh.htm#UserGuide-zh-API%E9%85%8D%E7%BD%AE http: ...

  5. dubbo专题」dubbo其实很简单,就是一个远程服务调用的框架(1)

    一.dubbo是什么? 1)本质:一个Jar包,一个分布式框架,,一个远程服务调用的分布式框架. 既然是新手教学,肯定很多同学不明白什么是分布式和远程服务调用,为什么要分布式,为什么要远程调用.我简单 ...

  6. 使用docker-compose 一键部署你的分布式调用链跟踪框架skywalking

    一旦你的程序docker化之后,你会遇到各种问题,比如原来采用的本地记日志的方式就不再方便了,虽然你可以挂载到宿主机,但你使用 --scale 的话,会导致 记录日志异常,所以最好的方式还是要做日志中 ...

  7. okhttputils【 Android 一个改善的okHttp封装库】使用(一)

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 本文使用的OKHttp封装库是张鸿洋(鸿神)写的,因为在项目中一直使用这个库,所以对于一些常用的请求方式都验证过,所以特此整理下. ...

  8. okhttputils【 Android 一个改善的okHttp封装库】使用(三)

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 这一篇主要讲一下将OkHttpUtils运用到mvp模式中. 数据请求地址:http://www.wanandroid.com/to ...

  9. 分布式调用技术 RPC VS REST

    一 分布式调用大体上就分为两类,RPC式的,REST式的,两者的区别主要是就是: 1. RPC是面向动作的(方法调用) 2. REST是面向资源的(URL表示资源,HTTP动词表示动作) 从变现形式来 ...

  10. Spring Cloud 5分钟搭建教程(附上一个分布式日志系统项目作为参考) - 推荐

    http://blog.csdn.net/lc0817/article/details/53266212/ https://github.com/leoChaoGlut/log-sys 上面是我基于S ...

随机推荐

  1. dotnet的Lambda表达式 委托泛型(2) Action Func

    // 总结:// 泛型:把类,方法,属性,字段做到了通用化// 反射:操作dll文件的一个帮助类库// 特性:就是一个特殊的类 自定义标记属性特性 他就是AOP的另一种实现方式 验证属性// 委托:就 ...

  2. 托管服务简介IHostedService接口 继承 BackgroundSerice接口

    1. 场景:代码运行在后台,比如服务器启动的时候在后台预先加载数据到缓存,每天凌晨3 点把数据到处到数据库备份,每隔5秒在两张表之间同步一次数据 : 2. 托管服务实现IHoutedService接口 ...

  3. 在不需要WiFi密码的情况下进行断网攻击

    本教程只能用于学习研究之用 任何未经他人允许的攻击行为都是违法行为 参考教程 https://www.youtube.com/davidbombal 准备 * kali linux 系统 这里使用的是 ...

  4. day13-JavaDoc

    JavaDoc JavaDoc命令是用来生成自己API文档的 参数信息 @author 作者名 @version 版本号 @since 指明需要最早使用的jdk版本 @param 参数名 @retur ...

  5. Js数组&高阶函数

    数组也是一种复合数据类型,在数组可以存储多个不同类型的数据 数组中存储的是有序的数据,数组中的每个数据都有一个唯一的索引可以通过索引来操作获取数据 数组中存储的数据叫做元素 任何类型的值都可以成为数组 ...

  6. 干货收藏!Calico 路由反射模式权威指南

    1. 概述 作为 Kubernetes 最长使用的一种网络插件,Calico 具有很强的扩展性,较优的资源利用和较少的依赖,相较于 Flannel 插件采用 Overlay 的网络,Calico 可以 ...

  7. 【Kernel】基于 QEMU 的 Linux 内核编译和安装

    目录 安装虚拟机系统 共享目录 编译内核 卸载内核 参考资料 本文主要记录个人做存储系统研究时,在 QEMU 环境下编译和安装 Linux 内核的过程 安装虚拟机系统 之前在 利用 RocksDB + ...

  8. 修改文件的所有者失败(chown: changing ownership of `uploads': Operation not permitted)

    在项目开发的时候,经常需要将文件上传到指定的目录下. 例如这次用thinkphp5的时候,需要在public目录下建立uploads目录用于存放上传的资源. 首先在命令窗口下输入: 1 mkdir u ...

  9. 自学PHP笔记(一)PHP语法

    PHP基本语法 php使用一对特殊的标记包含php代码,与HTML代码混在一起.当服务器解析页面时,能够自动过滤出PHP脚本并进行解释,最后把生成的静态网页传递给客户端. 1.PHP标记 一般情况下, ...

  10. 电脑配置不够玩不了原神、剑三和魔兽世界?ToDesk云电脑来帮你!

    原神.剑网三.魔兽世界这种吃配置的游戏,对电脑硬件和软件的要求可都不低,所以当游戏玩家遇到配置一般的电脑,就只能望游戏而兴叹吗? 当然不用!云电脑成为你的游戏电脑平替之选. 用云电脑来玩游戏,不仅对你 ...