微服务之调用链(Feign+SpringCloud)
终于到了我们的重点,微服务了。
与使用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)的更多相关文章
- springcloud微服务实战:Eureka+Zuul+Feign/Ribbon+Hystrix Turbine+SpringConfig+sleuth+zipkin
相信现在已经有很多小伙伴已经或者准备使用springcloud微服务了,接下来为大家搭建一个微服务框架,后期可以自己进行扩展.会提供一个小案例: 服务提供者和服务消费者 ,消费者会调用提供者的服务,新 ...
- 【多线程】java多线程Completablefuture 详解【在spring cloud微服务之间调用,防止接口超时的应用】【未完成】
参考地址:https://www.jianshu.com/p/6f3ee90ab7d3 示例: public static void main(String[] args) throws Interr ...
- SpringCloud微服务基础 Eureka、Feign、Ribbon、Zuul、Hystrix、配置中心的基础使用
1.单点系统架构 传统项目架构 传统项目分为三层架构,将业务逻辑层.数据库访问层.控制层放入在一个项目中. 优点:适合于个人或者小团队开发,不适合大团队开发. 分布式项目架构 根据业务需求进行拆分成N ...
- SpringCloud微服务实战——搭建企业级开发框架(十一):集成OpenFeign用于微服务间调用
作为Spring Cloud的子项目之一,Spring Cloud OpenFeign以将OpenFeign集成到Spring Boot应用中的方式,为微服务架构下服务之间的调用提供了解决方案.首先, ...
- 微服务架构案例(05):SpringCloud 基础组件应用设计
本文源码:GitHub·点这里 || GitEE·点这里 更新进度(共6节): 01:项目技术选型简介,架构图解说明 02:业务架构设计,系统分层管理 03:数据库选型,业务数据设计规划 04:中间件 ...
- 【一起学源码-微服务】Eureka+Ribbon+Feign阶段性总结
前言 想说的话 这里已经梳理完Eureka.Ribbon.Feign三大组件的基本原理了,今天做一个总结,里面会有一个比较详细的调用关系流程图. 说明 原创不易,如若转载 请标明来源! 博客地址:一枝 ...
- 服务注册中心之ZooKeeper系列(二) 实现一个简单微服务之间调用的例子
上一篇文章简单介绍了ZooKeeper,讲了分布式中,每个微服务都会部署到多台服务器上,那服务之间的调用是怎么样的呢?如图: 1.集群A中的服务调用者如何发现集群B中的服务提供者呢? 2.集群A中的服 ...
- springcloud 实现微服务间调用
package com.idoipo.ibt.config; import org.apache.http.HttpException; import org.apache.http.HttpRequ ...
- 快速了解阿里微服务热门开源分布式事务框架——Seata
一.Seata 概述 Seata 是 Simple Extensible Autonomous Transaction Architecture 的简写,由 feascar 改名而来. Seata 是 ...
- 【微服务】之五:轻松搞定SpringCloud微服务-调用远程组件Feign
上一篇文章讲到了负载均衡在Spring Cloud体系中的体现,其实Spring Cloud是提供了多种客户端调用的组件,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使 ...
随机推荐
- iOS中异常处理机制使用小结
在iOS开发中经常会由于数组越界,添加数据为空,通信或者文件错误,内存溢出导致程序终端运行而引入异常处理机制.常用的处理方式是try catch机制.不过有几个专业术语需要解释,异常句柄.异常处理域断 ...
- Tarjan 算法
远古算法笔记. dfs 生成树 无向图 对于一张连通的无向图,我们可以从任意一点开始 dfs,得到原图的一棵生成树(以开始 dfs 的那个点为根). 这棵生成树上的边称作树边,不在生成树上的边称作非树 ...
- 盘点.NET支持的 处理器架构
在一个会议上,中国招投标协会的技术负责人居然当着很多领导的面说.NET不能在国产服务器上运行,可以说这个技术负责人非蠢即坏.国产服务器的处理器架构主要包括x86.ARM.LoongArch.risc- ...
- Nuxt.js 应用中的 app:resolve 事件钩子详解
title: Nuxt.js 应用中的 app:resolve 事件钩子详解 date: 2024/10/17 updated: 2024/10/17 author: cmdragon excerpt ...
- appium-解决uiautomatorviewer不能定位android7以上版本元素的方法
上图是android10模拟器,发现我们是无法连接模拟器识别的 解决方法: 在模拟器内打开指定页面然后截图,最后保存为uix文件,然后用adb拉到本地,一共四个命令,可以保存为.bat文件快捷执行(这 ...
- 布局(LinearLayout,RelativeLayout,FrameLayout,TableLayout,GridLayout,ConstraintLayout)
LinearLayout layout_gravity:组件在父容器里的对齐方式 gravity:组件包含的所有子元素的对齐方式 layout_weight:在原有基础上分配剩余空间,一般把layou ...
- .net 在线客服系统,到底能不能处理 50万 级消息量,系统架构实践
业余时间用 .net core 写了一个在线客服系统.我把这款业余时间写的小系统丢在网上,陆续有人找我要私有化版本,我都给了,毕竟软件业的初衷就是免费和分享. 后来我索性就发了一个100%私有化版直接 ...
- cnblogs的GitHub同步markdown文件的blog如何识别文章的唯一性(身份ID如何判定)
本篇blog是写在GitHub的对应的仓库中的. cnblogs会给终身用户提供一个把GitHub仓库中的markdown文件同步到cnblogs上的一个服务,本文就是使用这个服务同步到个人blog地 ...
- 关于C++当中的“模板函数”
本人C++草鸟,在工作当中遇到了这个问题,就简单做个记录.
- Linux /dev/loop0文件详解
loop设备介绍 在类 UNIX 系统里,loop 设备是一种伪设备(pseudo-device),或者也可以说是仿真设备.它能使我们像块设备一样访问一个文件. 在使用之前,一个 loop 设备必须要 ...