微服务调用链追踪中心搭建 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
hansonwang99
V2EX    程序员

微服务调用链追踪中心搭建

  •  2
     
  •   hansonwang99
    hansonwang99 2018-04-24 08:55:52 +08:00 6618 次点击
    这是一个创建于 2731 天前的主题,其中的信息可能已经有所发展或是发生改变。

    ## 概述

    一个完整的微服务系统包含多个微服务单元,各个微服务子系统存在互相调用的情况,形成一个 调用链。一个客户端请求从发出到被响应 经历了哪些组件哪些微服务请求总时长每个组件所花时长 等信息我们有必要了解和收集,以帮助我们定位性能瓶颈、进行性能调优,因此监控整个微服务架构的调用链十分有必要,本文将阐述如何使用 Zipkin 搭建微服务调用链追踪中心。


    Zipkin 初摸

    正如 Ziplin 官网 所描述,Zipkin 是一款分布式的追踪系统,其可以帮助我们收集微服务架构中用于解决延时问题的时序数据,更直白地讲就是可以帮我们追踪调用的轨迹。

    Zipkin 的设计架构如下图所示:

    Zipkin 设计架构

    要理解这张图,需要了解一下 Zipkin 的几个核心概念:

    • Reporter

    在某个应用中安插的用于发送数据给 Zipkin 的组件称为 Report,目的就是用于追踪数据收集

    • Span

    微服务中调用一个组件时,从发出请求开始到被响应的过程会持续一段时间,将这段跨度称为 Span

    • Trace

    从 Client 发出请求到完成请求处理,中间会经历一个调用链,将这一个整个过程称为一个追踪( Trace )。一个 Trace 可能包含多个 Span,反之每个 Span 都有一个上级的 Trace。

    • Transport

    一种数据传输的方式,比如最简单的 HTTP 方式,当然在高并发时可以换成 Kafka 等消息队列


    看了一下基本概念后,再结合上面的架构图,可以试着理解一下,只有装配有 Report 组件的 Client 才能通过 Transport 来向 Zipkin 发送追踪数据。追踪数据由 Collector 收集器进行手机然后持久化到 Storage 之中。最后需要数据的一方,可以通过 UI 界面调用 API 接口,从而最终取到 Storage 中的数据。可见整体流程不复杂。

    Zipkin 官网给出了各种常见语言支持的 OpenZipkin libraries:

    OpenZipkin libraries

    本文接下来将 构造微服务追踪的实验场景 并使用 Brave 来辅助完成微服务调用链追踪中心搭建!


    部署 Zipkin 服务

    利用 Docker 来部署 Zipkin 服务再简单不过了:

    docker run -d -p 9411:9411 \ --name zipkin \ docker.io/openzipkin/zipkin 

    完成之后浏览器打开:localhost:9411可以看到 Zipkin 的可视化界面:

    Zipkin 可视化界面


    模拟微服务调用链

    我们来构造一个如下图所示的调用链:

    微服务调用链

    图中包含 一个客户端 + 三个微服务

    • Client:使用 /servicea 接口消费 ServiceA 提供的服务

    • ServiceA:使用 /serviceb 接口消费 ServiceB 提供的服务,端口 8881

    • ServiceB:使用 /servicec 接口消费 ServiceC 提供的服务,端口 8882

    • ServiceC:提供终极服务,端口 8883

    为了模拟明显的延时效果,准备在每个接口的响应中用代码加入 3s 的延时。

    简单起见,我们用 SpringBt 来实现三个微服务。

    ServiceA 的控制器代码如下:

    @RestController public class ServiceAContorller { @Autowired private RestTemplate restTemplate; @GetMapping("/servicea ”) public String servicea() { try { Thread.sleep( 3000 ); } catch (InterruptedException e) { e.printStackTrace(); } return restTemplate.getForObject("http://localhost:8882/serviceb", String.class); } } 

    ServiceB 的代码如下:

    @RestController public class ServiceBContorller { @Autowired private RestTemplate restTemplate; @GetMapping("/serviceb ”) public String serviceb() { try { Thread.sleep( 3000 ); } catch (InterruptedException e) { e.printStackTrace(); } return restTemplate.getForObject("http://localhost:8883/servicec", String.class); } } 

    ServiceC 的代码如下:

    @RestController public class ServiceCContorller { @Autowired private RestTemplate restTemplate; @GetMapping("/servicec ”) public String servicec() { try { Thread.sleep( 3000 ); } catch (InterruptedException e) { e.printStackTrace(); } return "Now, we reach the terminal call: servicec !”; } } 

    我们将三个微服务都启动起来,然后浏览器中输入localhost:8881/servicea来发出请求,过了 9s 之后,将取到 ServiceC 中提供的微服务接口所返回的内容,如下图所示:

    微服务链式调用结果

    很明显,调用链可以正常 work 了!

    那么接下来我们就要引入 Zipkin 来追踪这个调用链的信息!

    编写与 Zipkin 通信的工具组件

    从 Zipkin 官网我们可以知道,借助 OpenZipkin 库 Brave,我们可以开发一个封装 Brave 的公共组件,让其能十分方便地嵌入到 ServiceA,ServiceB,ServiceC 服务之中,完成与 Zipkin 的通信。

    为此我们需要建立一个新的基于 Maven 的 Java 项目:ZipkinTool

    • pom.xml 中加入如下依赖:
    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.hansonwang99</groupId> <artifactId>ZipkinTool</artifactId> <version>1.0-SNAPSHOT</version> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>6</source> <target>6</target> </configuration> </plugin> </plugins> </build> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot</artifactId> <version>2.0.1.RELEASE</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.7.RELEASE</version> <scope>provided</scope> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-spring-web-servlet-interceptor</artifactId> <version>4.0.6</version> </dependency> <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-spring-resttemplate-interceptors</artifactId> <version>4.0.6</version> </dependency> <dependency> <groupId>io.zipkin.reporter</groupId> <artifactId>zipkin-sender-okhttp3</artifactId> <version>0.6.12</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>RELEASE</version> <scope>compile</scope> </dependency> </dependencies> </project> 
    • 编写 ZipkinProperties 类

    其包含 endpoint 和 service 两个属性,我们最后是需要将该两个参数提供给 ServiceA、ServiceB、ServiceC 微服务作为其 application.properties 中的 Zipkin 配置

    @Data @Component @ConfigurationProperties("zipkin") public class ZipkinProperties { private String endpoint; rivate String service; } 

    用了 lombok 之后,这个类异常简单!

    [注意:关于 lombok 的用法,可以看这里]

    • 编写 ZipkinConfiguration 类

    这个类很重要,在里面我们将 Brave 的 BraveClientHttpRequestInterceptor 拦截器注册到 RestTemplate 的拦截器调用链中来收集请求数据到 Zipkin 中;同时还将 Brave 的 ServletHandlerInterceptor 拦截器注册到调用链中来收集响应数据到 Zipkin 中

    上代码吧:

    @Configuration @Import({RestTemplate.class, BraveClientHttpRequestInterceptor.class, ServletHandlerInterceptor.class}) public class ZipkinConfiguration extends WebMvcConfigurerAdapter { @Autowired private ZipkinProperties zipkinProperties; @Autowired private RestTemplate restTemplate; @Autowired private BraveClientHttpRequestInterceptor clientInterceptor; @Autowired private ServletHandlerInterceptor serverInterceptor; @Bean public Sender sender() { return OkHttpSender.create( zipkinProperties.getEndpoint() ); } @Bean public Reporter<Span> reporter() { return AsyncReporter.builder(sender()).build(); } @Bean public Brave brave() { return new Brave.Builder(zipkinProperties.getService()).reporter(reporter()).build(); } @Bean public SpanNameProvider spanNameProvider() { return new SpanNameProvider() { @Override public String spanName(HttpRequest httpRequest) { return String.format( "%s %s", httpRequest.getHttpMethod(), httpRequest.getUri().getPath() ); } }; } @PostConstruct public void init() { List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors(); interceptors.add(clientInterceptor); restTemplate.setInterceptors(interceptors); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(serverInterceptor); } } 

    ZipkinTool 完成以后,我们需要在 ServiceA、ServiceB、ServiceC 三个 SpringBt 项目的 application.properties 中加入 Zipkin 的配置:

    以 ServiceA 为例:

    server.port=8881 zipkin.endpoint=http://你 Zipkin 服务所在机器的 IP:9411/api/v1/spans zipkin.service=servicea 

    我们最后依次启动 ServiceA、ServiceB、和 ServiceC 三个微服务,并开始实验来收集链路追踪数据 !


    ## 实际实验

    1. 依赖分析

    浏览器打开 Zipkin 的 UI 界面,可以查看 依赖分析

    点击依赖分析

    图中十分清晰地展示了 ServiceA、ServiceB 和 ServiceC 三个服务之间的调用关系! 注意,该图可缩放,并且每一个元素均可以点击,例如点击 ServiceB 这个微服务,可以看到其调用链的上下游!

    点击 ServiceB 微服务


    2. 查找调用链

    接下来我们看一下调用链相关,点击 服务名,可以看到 Zipkin 监控到个所有服务:

    查找调用链

    同时可以查看 Span,如以 ServiceA 为例,其所有 REST 接口都再下拉列表中:

    查看 Span

    以 ServiceA 为例,点击 Find Traces,可以看到其所有追踪信息:

    Find Traces

    点击某个具体 Trace,还能看到详细的每个 Span 的信息,如下图中,可以看到 A B C 调用过程中每个 REST 接口的详细时间戳:

    某一个具体 Trace

    点击某一个 REST 接口进去还能看到更详细的信息,如查看 /servicec 这个 REST 接口,可以看到从发送请求到收到响应信息的所有详细步骤:

    某一个具体 Span 详细信息

    参考文献


    23 条回复    2018-04-25 10:29:21 +08:00
    onepunch
        1
    onepunch  
       2018-04-24 09:39:52 +08:00
    40 分钟已经过去了,战略性 mark
    byrain
        2
    byrain  
       2018-04-24 10:05:17 +08:00
    嗯。这东西挺好的,我们这边做了 http grpc 以及 mysql 调用的追踪。
    lawmil
        3
    lawmil  
       2018-04-24 11:09:44 +08:00
    战略 mark 一下
    prolic
        4
    prolic  
       2018-04-24 11:22:06 +08:00
    mark
    bolide2005
        5
    bolide2005  
       2018-04-24 11:28:40 +08:00
    建议再看看 OpenTracing,可以了解一些 Zipkin 更底层的设计思路
    Mitt
        6
    Mitt  
       2018-04-24 11:30:09 +08:00
    mark
    salmon5
        7
    salmon5  
       2018-04-24 11:41:37 +08:00
    @hansonwang99
    请教下,如果作为一个 CTO,这个工作交给谁合适呢?
    架构,后端,前端,UI,运维,测试,PM ?
    hansonwang99
        8
    hansonwang99  
    OP
       2018-04-24 11:44:52 +08:00 via iPhone
    @salmon5 可以咨询一下 CEO
    freehere
        9
    freehere  
       2018-04-24 11:49:21 +08:00
    oneapm 是否直接能替代搭建这个系统?
    projectzoo
        10
    projectzoo  
       2018-04-24 12:37:23 +08:00
    看到“百度一下”在收藏夹第一位。
    关了。
    xuanyuanaosheng
        11
    xuanyuanaosheng  
       2018-04-24 12:45:46 +08:00 via Android
    mark,有同类产品比较么?其他产品有么?
    tanszhe
        12
    tanszhe  
       2018-04-24 13:16:17 +08:00   1
    一个日志分析就能有这个功能,还可以更细化 也不需要在应用中安插什么 Reporter,只要日志格式统一就好了。
    实现这个主要就是给每个请求产生一个唯一 id
    flight2006
        13
    flight2006  
       2018-04-24 13:33:57 +08:00 via Android
    mark,公司有用过,但是没细细研究过
    valkyrja
        14
    valkyrja  
       2018-04-24 14:16:40 +08:00 via iPhone   1
    @projectzoo 一股恶臭的优越感扑面而来
    RorschachZZZ
        15
    RorschachZZZ  
       2018-04-24 14:43:59 +08:00
    hpu423
        16
    hpu423  
       2018-04-24 14:50:05 +08:00
    以前公司搭建过,后来没推广成功
    oswuhan
        17
    oswuhan  
       2018-04-24 16:38:38 +08:00
    看开头不明觉厉,看完醍醐灌顶
    YanY
        18
    YanY  
       2018-04-24 18:29:25 +08:00
    @xuanyuanaosheng CAT 也是服务链调用日志
    yuhuan6666
        19
    yuhuan66666  
       2018-04-24 18:37:24 +08:00 via Android
    spring cloud 里的数据链路中心就是封装了这个
    war1644
        20
    war1644  
       2018-04-24 20:33:46 +08:00
    战略 mark 下,还在梳理公司 docker 微服务。调用链这块早晚得用上
    PureWhite
        21
    PureWhite  
       2018-04-25 02:16:33 +08:00
    其实就是分布式追踪
    xuanyuanaosheng
        22
    xuanyuanaosheng  
       2018-04-25 10:27:28 +08:00
    @YanY 好的 谢谢
    gmywq0392
        23
    gmywq0392  
       2018-04-25 10:29:21 +08:00
    现在主流的 APM 也有这种 trace 的功能了。比如 Elastic APM, Pinpoint, OneAPM 这些入侵性更少,而且从监控的角度来说体验更好呀。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     885 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 21:18 PVG 05:18 LAX 14:18 JFK 17:18
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86