Spring Cloud Sleuth + Zipkin 历险记_cab5的博客-程序员宅基地

前言

经过近期的努力,已经为公司初步搭建起了一套基于 springcloud、docker、k8s、gitlab-ci 的微服务结构。虽然可以整条线跑起来了,但是还有很多支撑服务有待完善,今天主要讲一下我是如何通过 Spring Cloud Sleuth + Zipkin 来解决分布式链路跟踪问题的,以及过程中出现的问题。

Spring Cloud Sleuth 简介

Spring Cloud Sleuth 为服务之间调用提供链路追踪。通过Sleuth可以很清楚的了解到一个服务请求经过了哪些服务,每个服务处理花费了多长。从而让我们可以很方便的理清各微服务间的调用关系。

Zipkin 简介

Zipkin 是一个开放源代码分布式的跟踪系统,由 Twitter 公司开源,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现。

每个服务向 Zipkin 报告计时数据,例如用户每次请求服务的处理时间等,可方便的监测系统中存在的瓶颈。

Zipkin 会根据调用关系通过 Zipkin UI 生成依赖关系图。

Spring Cloud Sleuth 与 Zipkin 的关系

Spring Cloud Sleuth 配合 Zipkin,将信息发送到 Zipkin,利用 Zipkin 的存储来存储信息,同时可以利用 Zipkin UI 来展示数据。Spring Cloud Sleuth 与需要进行供链路追踪的系统进行绑定。Spring Cloud 提供了spring-cloud-sleuth-zipkin 来方便集成 zipkin (指的是Zipkin Client,而不是Zipkin服务器),该 jar 包可以通过 spring-cloud-starter-zipkin 依赖来引入。

开始搭建 Zipkin Server

在整个结构中,我理解有这么几种角色:

  • Zipkin Server
    数据收集、存储、查找和展现。
  • 作为 Zipkin Client 的「服务提供者」
    通过 Spring Cloud Sleuth 将请求发送到 Zipkin Server。
  • 「服务调用者」
    向「服务提供者」发起请求。

前提条件

这里首先要说一下,本文中涉及到的所有系统,依赖的软件版本如下:

  • spring-boot:2.1.3.RELEASE
  • spring-cloud:Greenwich.SR1

一、搭建 Zipkin Server

Spring Boot 2.0 之前 Zipkin Server 是需要我们自己创建一个对应的 project 的。而 Spring Boot 2.0 之后,Zipkin 已不再推荐自定义 Zipkin Server 了,官方推荐了以下2种方式来部署 Zipkin Server:

  1. 通过 Jar 包部署
    下载官方提供的可执行 Jar 包,例如:zipkin-server-2.12.9-exec.jar,然后在服务器上运行即可,例如:java -jar zipkin-server-2.12.9-exec.jar。
  2. 通过 image 部署
    通过官方提供的镜像,例如:openzipkin/zipkin,采用 docke r或者 k8s 进行部署。

在前言中已经提到,我的所有服务是通过 k8s 进行部署的,所以,Zipkin Server 也不例外。k8s 环境如何搭建不是本文的重点,所以这里就不进行介绍了,直接说一下 Zipkin Server 所涉及的2个 YAML 文件:

  • annoroad-zipkin-deployment.yaml
    Zipkin Server 将会作为 k8s 的 Deployment 资源投递到 k8s 环境中,代码如下:
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: annoroad-zipkin
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: annoroad-zipkin
  template:
    metadata:
      labels:
        app: annoroad-zipkin
    spec:
      terminationGracePeriodSeconds: 60
      containers:
      - name: annoroad-zipkin
        env:
        - name: DEPLOY_TAG
          value: tag5
        - name: MY_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: MY_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: MY_POD_NAMESPACE # 传入当前命名空间
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: MY_POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: SERVICE_NAME # 因为pod 通过域名互相访问,需要使用headless服务名称
          value: annoroad-zipkin-service
        - name: STORAGE_TYPE
          value: mysql
        - name: MYSQL_HOST
          value: rm-1azzwo8172t6v014mm.mysql.rds.aliyuncs.com
        - name: MYSQL_USER
          value: user
        - name: MYSQL_PASS
          value: 123321
        - name: MYSQL_DB
          value: annoroad_zipkin_database
        - name: COLLECTOR_PORT
          value: '10400'
        - name: QUERY_PORT
          value: '10401'
        image: registry-vpc.cn-beijing.aliyuncs.com/annoroad-cloud/annoroad-zipkin:latest
        imagePullPolicy: Always #每次都重新拉取镜像
        readinessProbe:
          httpGet:
            path: /health
            port: 10401
          initialDelaySeconds: 30
          timeoutSeconds: 10
        livenessProbe:
          httpGet:
            path: /health
            port: 10401
          initialDelaySeconds: 60
          timeoutSeconds: 10
        ports:
        - name: http
          containerPort: 10400

这里提到的 registry-vpc.cn-beijing.aliyuncs.com/annoroad-cloud/annoroad-zipkin:latest 镜像,其实就是 openzipkin/zipkin 镜像,为了公司内部统一镜像名称我将 openzipkin/zipkin 打成 tag(registry-vpc.cn-beijing.aliyuncs.com/annoroad-cloud/annoroad-zipkin ),过程如下:

  • 拉取 openzipkin/zipkin 镜像
[[email protected] /]# docker pull openzipkin/zipkin
  • 打tag
[[email protected] /]# docker tag openzipkin/zipkin registry.cn-beijing.aliyuncs.com/annoroad-cloud/annoroad-zipkin
  • 推送到私有镜像仓库
[[email protected] /]# docker push registry.cn-beijing.aliyuncs.com/annoroad-cloud/annoroad-zipkin

本文中 zipkin server 通过设置环境变量 STORAGE_TYPE:mysql 来指定存储类型(mysql),默认为缓存存储(STORAGE_TYPE:mem)。zipkin server 还可通过环境变量来获取 mysql 的相关信息,例如:MYSQL_HOST、MYSQL_USER、MYSQL_PASS 等等,所以我们可以根据真实环境来设置这些环境变量。我们还可以通过变量 STORAGE_TYPE 切换不同的存储策略。

这里还有注意两个PORT:

  • COLLECTOR_PORT
    The port to listen for thrift RPC scribe requests. Defaults to 9410
  • QUERY_PORT
    Listen port for the http api and web ui; Defaults to 9411。后边提到的 base_url 使用的是该端口。
  1. annoroad-zipkin-service.yaml
    为 Zipkin Server 创建一个 k8s 对应的 Service 资源,使 Zipkin Server 在 k8s 环境中有一个固定的对外的入口,例如:登录 Zipkin UI 查询等等,代码如下:
apiVersion: v1
kind: Service
metadata:
  name: annoroad-zipkin-service
  labels:
    app: annoroad-zipkin-service
spec:
  ports:
  - name: 4collector
    port: 10400
    protocol: TCP
    targetPort: 10400
  - name: 4query
    port: 10401
    protocol: TCP
    targetPort: 10401
  selector:
    app: annoroad-zipkin
  type: LoadBalancer

YAML 文件已经准备就绪,下面就可以部署 Zipkin Server 了,执行以下命令:

[[email protected] /]# kubectl apply -f annoroad-zipkin-deployment.yaml -n test --record
[[email protected] /]# kubectl create -f annoroad-zipkin-service.yaml -n test

Zipkin Server 已经部署了,我们如何能看到UI界面呢?首选要获取 Zipkin UI 访问入口,如下:

[[email protected] /]# kubectl get svc -n test
NAME                        CLUSTER-IP      EXTERNAL-IP     PORT(S)                        AGE
annoroad-zipkin-service     252.16.10.32    23.6.80.234   10400:31351/TCP,10401:30040/TCP   1d

上边的 23.6.80.234 是一个公网地址,有了这个地址我们就可以访问 Zipkin UI 了。打开浏览器,在地址栏输入 http://23.6.80.234:10401,将会出现如下界面:
在这里插入图片描述
至此,恭喜你,Zipkin Server 已部署完成了!!!!!!!

这里还有一点需要说明的是,上图中的服务名 annoroad-alpha 对应的是 annoroad-alpha 项目中 application.xml 文件中的 spring.application.name,如下图:
在这里插入图片描述
如果未指定该值,则该 project(annoroad-alpha) 在 zipkin 中的服务名为 default。

二、配置 「服务提供者」

这里我以 annoroad-alpha 项目为例,步骤如下:

  1. 在 pom.xml 文件中增加依赖包,如下:
    </dependencies>
        <!-- zipkin 分布式请求跟踪监控 begin -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>
        <!-- zipkin 分布式请求跟踪监控 end -->
    </dependencies>
  1. 在application.yml文件中增加 Sleuth + ZipKin 相关配置,如下:
spring:
  # 调用链 Sleuth + ZipKin
  sleuth:
    sampler:
      # 采用比例,默认 0.1 全部采样 1.0
      probability: 1.0
  zipkin:
    # 指定了 Zipkin 服务器的地址
    base-url: http://annoroad-zipkin-service:10401/

这里的 annoroad-zipkin-service 对应了 k8s 环境中 name 为 annoroad-zipkin-service 的Service,也就是 annoroad-zipkin-service.yaml 定义的 Service。

annoroad-alpha 是如何将请求信息推送给 Zipkin Server 的呢?Zipkin 提供了很多种方式来实现。

本文采用的 http 的方式,也就 annoroad-alpha 通过请求 base-url 将请求 annoroad-alpha 的信息推送给 Zipkin Server。

这里再着重说一下初次配置 base-url 的几点疑惑:

  1. 10401 为 zipkin 的 QUERY_PORT,而不是 COLLECTER_PORT
  2. 对于 base-url,首先会将 base-url(http://annoroad-zipkin-service:10401/) 作为一个注册中心里的一个应用名称从注册中心查找相关服务,如果不存在,也不会报错,base-url 会被当做普通的 ip 地址,或者域名来处理,所以,后台打印如下信息不必理会!!!!!!
2019-09-27 14:27:37.863[INFO ]com.netflix.loadbalancer.BaseLoadBalancer:  197-Client: localhost instantiated a LoadBalancer: DynamicServerListLoadBalancer:{
    NFLoadBalancer:name=localhost,current list of Servers=[],Load balancer stats=Zone stats: {
    },Server stats: []}ServerList:null
2019-09-27 14:27:37.871[INFO ]com.netflix.loadbalancer.DynamicServerListLoadBalancer:  222-Using serverListUpdater PollingServerListUpdater
2019-09-27 14:27:37.874[INFO ]com.netflix.loadbalancer.DynamicServerListLoadBalancer:  150-DynamicServerListLoadBalancer for client localhost initialized: DynamicServerListLoadBalancer:{
    NFLoadBalancer:name=localhost,current list of Servers=[],Load balancer stats=Zone stats: {
    },Server stats: []}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@5161aaf1

三、「服务调用者」

这里说的「服务调用者」就是 Postman,通过 Postman 请求 annoroad-alpha 服务,annoroad-alpha 再通过请求 base-url 将请求信息推送给 Zipkin Server,最终效果如下:
在这里插入图片描述

四、Zipkin UI

这里拿一个实际的调用场景来讲一下 Zipkin UI,调用关系如下:
在这里插入图片描述
如上图,整个调用链是 postman -> annoroad-openapi -> annoroad-alpha -> annoroad-beta,这里 的 annoroad-alpha、annoroad-beta 增加了对 spring-cloud-starter-zipkin 的依赖,也就是说对着两个服务进行了调用链路跟踪。

打开 Zipkin UI,我们可以看到如下界面:
在这里插入图片描述
这里红框中的部分称为“Trace”,TA 是一系列“Span”组成的一个树状结构。我们点开 TA,将会看到如下界面:
在这里插入图片描述
这里 annoroad-alpha 为根 Span,此 Span 中 span id 和 trace id 值相同,两个子 Span 都是 annoroad-beta,一个是请求 /annoroad-beta/hello,另外一个是请求 /annoroad-beta/bizexp。Span 是基本的工作单元,TA 包括一个64位的唯一ID,一个64位 trace 码,描述信息,时间戳事件,key-value ,如下图:
在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yangchao1125/article/details/101544162

智能推荐

Java报错解决:org.apache.http.ConnectionClosedException: Premature end of chunk coded message body: closi_zhangpeterx的博客-程序员宅基地

再跑爬虫程序的时候突然遇到了如下报错:org.apache.http.ConnectionClosedException: Premature end of chunk coded message body: closing chunk expected... at org.apache.http.impl.io.ChunkedInputStream.getChunkSize(C...

华为笔试题_您的华为帐号 185******48 绑定的安全手机号已成功更改为 199******71。 若想_bug07250432的博客-程序员宅基地

华为1.static有什么用途?(请至少说明两种)    1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。    2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。    3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块

深入理解linux下write()和read()函数_使用write程序后里面的buf数据变了_Tony_Xian的博客-程序员宅基地

 1、write()函数定义:ssize_t write (int fd, const void * buf, size_t count); 函数说明:write()会把参数buf所指的内存写入count个字节到参数放到所指的文件内。返回值:如果顺利write()会返回实际写入的字节数。当有错误发生时则返回-1,错误代码存入errno中。 附加说明:(1)write()...

【Android开发经验】Json数据格式介绍、使用Android自带类库完成Json生成与解析_赵凯强的博客-程序员宅基地

转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992    因为在之前的项目中,xml和json数据格式都有使用过,所以对json格式的简单、高效的特点印象深刻。如果使用json语言进行开发,强烈推荐使用json数据格式!    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析

Android基础入门教程——1.5.1 Git使用教程之本地仓库的基本操作_coder-pig的博客-程序员宅基地

Android基础入门教程——1.5.1 Git使用教程之本地仓库的基本操作标签: Android基础入门教程Git是什么? 一个分布式版本控制系统,和SVN类似,但远比SVN强大的一个版本控制系统 ①Git可以方便的在本地进行版本管理,如同你本地有一个版本管理服务器一样 我们可以选择在合适的时间将本地版本推送到统一的版本管理服务器 ②Git每次会提取整个代码仓库的完整镜像,相

android webview设置自适应任意大小的pc网页_风口猪炒股指标的博客-程序员宅基地

WebSettings webSettings = view.getSettings(); webSettings.setJavaScriptEnabled(true); //view.clearCache(true); //view.clearHistory(); // User settings webSettings.setJavaScriptEn

随便推点

【TensorFlow】Keras机器学习基础知识-使用TF.Hub进行文本分类_CA727的博客-程序员宅基地

此笔记本(notebook)使用评论文本将影评分为积极(positive)或消极(nagetive)两类。这是一个二元(binary)或者二分类问题,一种重要且应用广泛的机器学习问题。本教程演示了使用 Tensorflow Hub 和 Keras 进行迁移学习的基本应用。我们将使用来源于网络电影数据库(Internet Movie Database)的 IMDB 数据集(IMDB datase...

JAVA web log view 实现实时日志查询_javaweb实时显示日志_瓦哥架构实战的博客-程序员宅基地

项目介绍ws: tail -f 查看服务器日志功能使用方式spring boot 项目直接 java -jar LogView.jar 启动即可,没用数据库!右侧为服务器当前的路径的文件,文件夹可以点击!想看哪个日志,结合上面的路径放入待查看Input查看。 项目分为"宿主机服务器"和"远程服务器" 两种查看方式,只支持linux tail -f *.log 查看日志使用http://127.0.0.1:7003/logView/...

java.lang.TypeNotPresentException: Type org.apache.maven.plugin.surefire.SurefirePlugin not present_benpaozaicaoyuan的博客-程序员宅基地

错误信息如下[WARNING] Error injecting: org.apache.maven.plugin.surefire.SurefirePluginjava.lang.TypeNotPresentException: Type org.apache.maven.plugin.surefire.SurefirePlugin not present    at org.eclip...

Linux应用层查看系统时间的方法_linux查看系统时间_Jack-Cui的博客-程序员宅基地

系统时间,硬件时间,UTC时间,本地时间,查看、修改系统时间和硬件时间的方法,计算系统时间的应用程序。

爬虫遇到乱码怎么办? 解决乱码问题_爬虫爬出来的是乱码_DeltaTime的博客-程序员宅基地

情景在爬取非英文网站的页面时, 如果发现获取后的网页字符串无法正常显示, 出现乱码. 通常都是因为解码所使用的编码不是网页原来所使用的编码. 需要查询网页本身的编码.查询网页编码方法一:1. 打开浏览器开发者工具, 切换到console工具.2. 在控制台输入 document.chardet 即可显示出网页的编码格式.方法二:使用python获取网页编码格式.import r...

使用Zookeeper删除Kafka中的topic_vigel1990的博客-程序员宅基地

有关命令:  进入zookeeperclient[[email protected] mq]# cd zookeeper/bin[[email protected] bin]# lsREADME.txt zkCleanup.sh zkCli.cmd zkCli.sh zkEnv.cmd zkEnv.sh zkServer.cmd zkServer.sh[[email protected]...

推荐文章

热门文章

相关标签