终于到了咱们的重点,微服务了。java
与使用OkHttp3来实现的客户端相似,Feign接口原本也就是一个Http调用,依然可使用Http头传值的方式,将Trace
往下传。node
本文更多的是关于SpringCloud
的一些知识,你须要了解一些基本的Spring
相关的知识。git
更多系列,请关注公众号小姐姐味道,本文相关代码的github地址,见:github
https://github.com/sayhiai/example-jaeger-opentracing-tutorial-004
复制代码
SpringCloud的注册中心,咱们选用Consul。golang
consul也是用golang开发的。从consul官网下载二进制包之后,解压。web
./consul agent -bind 127.0.0.1 -data-dir . -node my-register-center -bootstrap-expect 1 -ui -dev
复制代码
使用以上脚本快速启动,便可使用。spring
访问 http://localhost:8500/ui/ 能够看到Consul的web页面。sql
以bom方式引入springboot和springcloud的组件。apache
spring-boot-dependencies 2.1.3.RELEASE
spring-cloud-dependencies Greenwich.SR1
复制代码
都是热乎乎的新鲜版本。bootstrap
接下来下,引入其余必须的包
opentracing-util 0.32.0
jaeger-client 0.35.0
logback-classic 1.2.3
opentracing-spring-jaeger-cloud-starter 2.0.0
spring-boot-starter-web
spring-boot-starter-aop
spring-boot-starter-actuator
spring-cloud-starter-consul-discovery
spring-cloud-starter-openfeign
复制代码
服务端App的端口是8888
@SpringBootApplication
@EnableAutoConfiguration
@EnableDiscoveryClient
@ComponentScan(basePackages = {
"com.sayhiai.example.jaeger.totorial04.controller",
})
public class App extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
复制代码
在application.yml中,配置Consul做为配置中心。
cloud:
consul:
host: 127.0.0.1
port: 8500
discovery:
register: true
tags: version=1.0,author=xjjdog
healthCheckPath: /actuator/health
healthCheckInterval: 5s
复制代码
建立Rest服务/hello
@PostMapping("/hello")
@ResponseBody
public String hello(@RequestBody String name) {
return "hello " + name;
}
复制代码
Feign客户端的App端口是9999
,一样是一个SpringCloud服务。
建立FeignClient
@FeignClient("love-you-application")
public interface LoveYouClient {
@PostMapping("/hello")
@ResponseBody
public String hello(@RequestBody String name);
}
复制代码
建立调用入口/test
@GetMapping("/test")
@ResponseBody
public String hello() {
String rs = loveYouClient.hello("小姐姐味道");
return rs;
}
复制代码
目前,已经有相关SpringCloud的轮子了,咱们就不重复制造了。
首先,咱们看一下使用方法,而后,说明一下背后的原理。了解原理以后,你将很容易的给本身开发的中间件加入Trace功能。
轮子在这里,引入相应maven包便可使用:
https://github.com/opentracing-contrib/java-spring-jaeger
复制代码
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-jaeger-cloud-starter</artifactId>
</dependency>
复制代码
加入配置生效
在application.yml
中,加入如下配置,就能够获得调用链功能了。
配置指明了trace的存放地址,并将本地log打开。
opentracing.jaeger.http-sender.url: http://10.30.94.8:14268/api/traces
opentracing.jaeger.log-spans: true
复制代码
访问 localhost:9999/test,会获得如下调用链。
首先看下Feign的Request构造函数。
public static Request create( String method, String url, Map<String, Collection<String>> headers, byte[] body, Charset charset) {
return new Request(method, url, headers, body, charset);
}
复制代码
如代码,彻底能够经过在headers参数中追加咱们须要的信息进行传递。
接着源代码往下找: Client**->** LoadBalancerFeignClient execute()-> executeWithLoadBalancer()-> IClient**->**
再往下,IClient实现有 OkHttpLoadBalancingClient RibbonLoadBalancingHttpClient(基于apache的包) 等,它们均可以很容易的设置其Header
最终,咱们的请求仍是由这些底层的库函数发起,默认的是HttpURLConnection。
读过Feign和Ribbon源码的人都知道,这部分代码不是通常的乱,但好在上层的Feign是一致的。
经过实现feign.Client
接口,结合委托,能够从新封装execute
方法,而后将信息inject
进Feign的scope中。
@Aspect
class TracingAspect {
@Around("execution (* feign.Client.*(..)) && !within(is(FinalType))")
public Object feignClientWasCalled(final ProceedingJoinPoint pjp) throws Throwable {
Object bean = pjp.getTarget();
if (!(bean instanceof TracingClient)) {
Object[] args = pjp.getArgs();
return new TracingClientBuilder((Client) bean, tracer)
.withFeignSpanDecorators(spanDecorators)
.build()
.execute((Request) args[0], (Request.Options) args[1]);
}
return pjp.proceed();
}
}
复制代码
利用spring boot starter技术,咱们不须要任何其余改动,就能够拥有trace功能了。
了解spring的人都知道,最适合作http头信息添加和提取的地方,就是拦截器和过滤器。
发送
对于普通的http请求客户端来讲,是经过添加一个 ClientHttpRequestInterceptor
拦截器来实现的。过程再也不表诉,依然是使用inject等函数进行头信息设置。
接收
而对于接收,则使用的是Filter进行实现的。经过实现一个普通的servlet filter。能够经过extract
函数将trace信息提取出来,而后将context做为Request的attribute进行传递。
相关代码片断以下。
if (servletRequest.getAttribute(SERVER_SPAN_CONTEXT) != null) {
chain.doFilter(servletRequest, servletResponse);
} else {
SpanContext extractedContext = tracer.extract(Format.Builtin.HTTP_HEADERS,
new HttpServletRequestExtractAdapter(httpRequest));
final Span span = tracer.buildSpan(httpRequest.getMethod())
.asChildOf(extractedContext)
.withTag(Tags.SPAN_KIND.getKey(), Tags.SPAN_KIND_SERVER)
.start();
httpRequest.setAttribute(SERVER_SPAN_CONTEXT, span.context());
复制代码
就这样,整个链条就穿插起来啦。
相似pinpoint和SkyWalking的所实现的同样。opentracing-contrib还基于bytebuddy
写了一套比较全的agent套件。
https://github.com/opentracing-contrib/java-specialagent
复制代码
已经有如下客户端被支持。
OkHttp3
JDBC API (java.sql)
Concurrent API (java.util.concurrent)
Java Web Servlet API (javax.servlet)
Mongo Driver
Apache Camel
AWS SDK
Cassandra Driver
JMS API (javax.jms v1 & v2)
Elasticsearch6 Client
RxJava 2
Kafka Client
AsyncHttpClient
RabbitMQ Client
RabbitMQ Spring
Thrift
GRPC
Jedis Client
Apache HttpClient
Lettuce Client
Spring Web
Spring Web MVC
Spring WebFlux
Redisson
Grizzly HTTP Server
Grizzly AsyncHttpClient
Reactor
Hazelcast
复制代码
可是,bytebuddy
的性能是较差的,你可能须要参考这些代码,使用asm
一类的api进行构建。
关于更详细的javaagent技术,能够参考小姐姐味道的另一篇文章: 冷门instrument包,功能d炸天
咱们后面的几篇文章,从单机到多机,从多线程到分布式,说明了OpenTracing的api使用方法。能够看到它的抽象仍是比较全面的,可以适应大部分场景。
随着OpenTracing规范愈来愈完善,它的使用也愈来愈流行。在设计本身的软件时,能够提早考虑预留接口(一般给予重要功能拦截器的工呢功能),以便在扩展时,可以方面的将本身加入到调用链链路上来。
剩下的,就是工做量了。