终于到了我们的重点,微服务了。

与使用OkHttp3来实现的客户端类似,Feign接口本来也就是一个Http调用,依然可以使用Http头传值的方式,将 Trace 往下传。

本文更多的是关于 SpringCloud 的一些知识,你需要了解一些基本的 Spring 相关的知识。

安装Consul

SpringCloud的注册中心,我们选用Consul。

consul也是用golang开发的。从consul官网下载二进制包以后,解压。

./consul agent   -bind 127.0.0.1 -data-dir . -node my-register-center -bootstrap-expect 1 -ui -dev
复制代码

使用以上脚本快速启动,即可使用。

访问 http://localhost:8500/ui/ 可以看到Consul的web页面。

构建微服务服务端和客户端

maven依赖

以bom方式引入springboot和springcloud的组件。

spring-boot-dependencies 2.1.3.RELEASE
spring-cloud-dependencies Greenwich.SR1
复制代码

都是热乎乎的新鲜版本。

接下来下,引入其他必须的包

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客户端

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;
}
复制代码

集成jaeger

目前,已经有相关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的整个调用过程都被记录下来了。

原理

Feign的调用

Feign通过Header传递参数

首先看下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是一致的。

使用委托包装Client

通过实现 feign.Client 接口,结合委托,可以重新封装 execute 方法,然后将信息 inject进Feign的scope中。

使用Aop自动拦截Feign调用

@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功能了。

Web端的发送和接收

了解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());
复制代码

就这样,整个链条就穿插起来啦。

微服务之调用链(Feign+SpringCloud)的更多相关文章

  1. springcloud微服务实战:Eureka+Zuul+Feign/Ribbon+Hystrix Turbine+SpringConfig+sleuth+zipkin

    相信现在已经有很多小伙伴已经或者准备使用springcloud微服务了,接下来为大家搭建一个微服务框架,后期可以自己进行扩展.会提供一个小案例: 服务提供者和服务消费者 ,消费者会调用提供者的服务,新 ...

  2. 【多线程】java多线程Completablefuture 详解【在spring cloud微服务之间调用,防止接口超时的应用】【未完成】

    参考地址:https://www.jianshu.com/p/6f3ee90ab7d3 示例: public static void main(String[] args) throws Interr ...

  3. SpringCloud微服务基础 Eureka、Feign、Ribbon、Zuul、Hystrix、配置中心的基础使用

    1.单点系统架构 传统项目架构 传统项目分为三层架构,将业务逻辑层.数据库访问层.控制层放入在一个项目中. 优点:适合于个人或者小团队开发,不适合大团队开发. 分布式项目架构 根据业务需求进行拆分成N ...

  4. SpringCloud微服务实战——搭建企业级开发框架(十一):集成OpenFeign用于微服务间调用

    作为Spring Cloud的子项目之一,Spring Cloud OpenFeign以将OpenFeign集成到Spring Boot应用中的方式,为微服务架构下服务之间的调用提供了解决方案.首先, ...

  5. 微服务架构案例(05):SpringCloud 基础组件应用设计

    本文源码:GitHub·点这里 || GitEE·点这里 更新进度(共6节): 01:项目技术选型简介,架构图解说明 02:业务架构设计,系统分层管理 03:数据库选型,业务数据设计规划 04:中间件 ...

  6. 【一起学源码-微服务】Eureka+Ribbon+Feign阶段性总结

    前言 想说的话 这里已经梳理完Eureka.Ribbon.Feign三大组件的基本原理了,今天做一个总结,里面会有一个比较详细的调用关系流程图. 说明 原创不易,如若转载 请标明来源! 博客地址:一枝 ...

  7. 服务注册中心之ZooKeeper系列(二) 实现一个简单微服务之间调用的例子

    上一篇文章简单介绍了ZooKeeper,讲了分布式中,每个微服务都会部署到多台服务器上,那服务之间的调用是怎么样的呢?如图: 1.集群A中的服务调用者如何发现集群B中的服务提供者呢? 2.集群A中的服 ...

  8. springcloud 实现微服务间调用

    package com.idoipo.ibt.config; import org.apache.http.HttpException; import org.apache.http.HttpRequ ...

  9. 快速了解阿里微服务热门开源分布式事务框架——Seata

    一.Seata 概述 Seata 是 Simple Extensible Autonomous Transaction Architecture 的简写,由 feascar 改名而来. Seata 是 ...

  10. 【微服务】之五:轻松搞定SpringCloud微服务-调用远程组件Feign

    上一篇文章讲到了负载均衡在Spring Cloud体系中的体现,其实Spring Cloud是提供了多种客户端调用的组件,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使 ...

随机推荐

  1. python处理nii文件

    第一步安装nibabel,可以使用命令:pip install nibabel 之后: from nibabel.viewers import OrthoSlicer3D as osdimport n ...

  2. Pytorch 实现 GAN 网络

    Pytorch 实现 GAN 网络 原理 GAN的基本原理其实非常简单,假设我们有两个网络,G(Generator)和D(Discriminator).它们的功能分别是: G 是一个生成网络,它接收一 ...

  3. 墨天轮专访TDengine陶建辉:坚持做难而正确的事,三次创业成就不悔人生

    导读: 时序数据库(Time Series Database)在最近几年被越来越多的用户接受并使用,并有广泛的应用场景.云原生时序数据库 TDengine 一直稳居墨天轮时序数据库榜首,其近期的海外发 ...

  4. 基于腾讯云短信接口和nodejs服务器实现手机号验证码

    知识储备:js基础.nodejs基础.ajax基础: 1. 手机验证码原理 表单提交,把手机号码传送到后端:后端拿到手机号码后根据相关算法随机形成一个验证码,并将其保存在数据库:用户拿到验证码后将验证 ...

  5. Kubernetes 备份容灾服务产品体验教程

    作者:尹珉,KubeSphere 社区用户委员会杭州站站长 前言 Kubernetes 集群天生自带自愈功能,但是往往有些意外情况使自愈功能不起作用,比如:公司同事把某个 namespace 删除.存 ...

  6. LiveData

    ViewModel 添加依赖 implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' activity_main.xml < ...

  7. windows宝塔面板请使用正确的入口登录面板问题解决记录

    问题起因:浏览器升级面板的时候,浏览器卡死,导致登录不进,cmd里面修复或者更新依然显示请使用正确的入口登录面板. 解决方法: 先停止宝塔面板,然后将下面这个目录删掉 C:\Program Files ...

  8. Jenkins Job触发其他远程Job

    https://blog.csdn.net/diaojian66/article/details/117334537 如果不想遇到连接远程Jenkins主机失败后的反复尝试,去掉认证会是一个不错的选择 ...

  9. c++设计模式:设计原则

    c++设计八大原则(降低改变带来的代码修改) 一.依赖倒置原则(DIP) 1.高层模块(稳定)不应该依赖于低层模块(变化),二者应该依赖于抽象(更稳定) <高层模块 包括 低层模块所依赖的抽象, ...

  10. Exadata系列之配置利器OECA

    Oracle Exadata Configuration Assistant (OECA) 是一款简便的配置工具,用于快速生成Exadata系统的推荐配置方案.它根据用户的需求和负载特点,自动推荐合适 ...