微服务调用链路治理–Zipkin

Zipkin 介绍

zipkin官方网站,点此进入

Zipkin是一个分布式链路调用监控系统,聚合各业务调用延迟数据,以达到链路调用监控跟踪

架构图如下

在这里插入图片描述

总体划分,zipkin 可以分为 client 和 server 两部分。

client 就是我们需要管理的各个服务,在各种调用行为的过程当中收集数据发送给server。

server 用于收集各个 client 上报的数据,存储,分析,以及展示等等。

Zipkin Server主要包括四个模块:

  1. Collector 收集client上报的数据
  2. Storage 存储接受或收集过来的数据,当前支持Memory,MySQL,Cassandra,ElasticSearch等,默认存储在内存中。
  3. API 负责查询和处理 Storage 中存储的数据
  4. Web 负责数据的展示

Zipkin Client 不同的语言和框架做了不一样的实现,但是基本原理是一致的。即在服务调用的过程当中拦截请求,添加用于链路追溯的一些信息或者收集信息上报server。

例如spring工程,在restTemplate发送请求的过程当中加入filter。又或者EGGJS是使用中间件进行请求的拦截。

服务调用的过程

服务A
服务B
服务C
  1. 服务调用开始会产生traceId,用于标识同一条链路
  2. 服务的调用 例如http协议当中的request 和 response,都认为是一个span ,会产生一个spanId. 如果单纯的被调用,作为reponse的一方也会产生spanId。
  3. 服务A调用B时,会在调用请求头中加入 traceId spanId parentSpanId.用于告知被调用方这个调用链路的traceId,以及调用方或者回复方的spanId是多少。同时每个span都会被上报到zipkin的server中,这样就可以通过这些信息呈现出调用链路。
  4. 通过上传各个span的延时和时间戳,也可以得到每次调用的时长。

Zipkin 服务端

zipkin-server 是用 java 实现的,可以执行 jar 包启动。或者直接跑 docker

docker run --name zipkin -d -p 9411:9411 openzipkin/zipkin

这是使用 zipkin-server 最简单的方式。

另外 server 可以使用额外的数据存储组件对数据进行持久化存储。以及接入 消息中间件用于缓解server端的数据接收压力。这些后续如果要上线使用再集成。

SpringBoot 应用集成 Zipkin

目前我们的java应用基本是使用springcloud的架构,spring cloud针对Zipkin的集成非常成熟。

加入相关的依赖

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>	

应用的配置

spring.application.name=spring-client-1  //应用名称
spring.zipkin.base-url=http://127.0.0.1:9411  //服务端地址
spring.sleuth.sampler.percentage=1.0 //采样比例
spring.sleuth.sampler.rate=100 //单位时间上传速率限制

应用日志配置

<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{X-B3-TraceId:-}-%X{X-B3-SpanId:-} %level ${LOG_SYSTEM} [%logger{100}_%M] - %msg%n</pattern>

EggJS 集成 Zipkin

有人针对 Eggjs 做了插件,引用插件即可。

添加依赖

 npm i egg-zipkin --save

启用插件

  zipkin: {
    enable: true,
    package: 'egg-zipkin',
  }

应用配置

  config.zipkin = {
    serviceName: 'base-service',
    httpsOn: false,
    targetServer: '127.0.0.1:9411',
    targetApi: '/api/v2/spans',
    jsonEncoder: 'v2',
    consoleRecorder: false
  }; 

Zipkin 的基本使用

按照上述的应用配置,我跑了三个应用。分别是

  1. PRODUCEWEB (JAVA)
  2. EUREKA-SERVICE-PRODUCE (JAVA)
  3. BASE-SERVICE (NODE)

请求一个API,调用过程如下

PRODUCEWEB
EUREKA-SERVICE-PRODUCE
BASE-SERVICE

执行该接口。

zipkin 上的呈现

在这里插入图片描述

zipkin还会呈现服务的依赖情况

在这里插入图片描述

当流程存在异常的时候,例如我在最后的服务写个BUG

服务日志里出现异常

2020-11-12 18:59:26.282 159c66b078aa8955-8849ccfd7a5f30e7 ERROR [com.starnetuc.zhixin.common.exception.BusinessErrorDecoder_decode] 

记得我们上面的日志配置%X{X-B3-TraceId:-}-%X{X-B3-SpanId:-}, 159c66b078aa8955这个是traceId。可以查询这次调用过程。

当然系统提供了服务名称,时间等方式进行筛选

在这里插入图片描述

注意,系统对于HTTP请求是否异常的判断是通过HTTP状态码,所以服务接口出现意料之外的异常,状态码置为对应的状态码。例如 500, 403等等

后面附上调用的请求头和发送给zipkin的报文内容

服务A调用服务B的报文

GET /production/field/provinceandversion?pid=2 HTTP/1.1
X-B3-SpanId: ffa5382f8d436a5b
X-B3-ParentSpanId: adb5f4e54f3182c1
X-B3-Sampled: 1
X-B3-TraceId: adb5f4e54f3182c1
Accept: */*
User-Agent: Java/1.8.0_181
Host: 192.168.75.89:9097
Connection: keep-alive


服务A上报的报文

[{
	"traceId": "adb5f4e54f3182c1",
	"parentId": "adb5f4e54f3182c1",
	"id": "ffa5382f8d436a5b",
	"kind": "CLIENT",
	"name": "get",
	"timestamp": 1605182686556492,
	"duration": 134208,
	"localEndpoint": {
		"serviceName": "produceweb",
		"ipv4": "172.18.0.1"
	},
	"tags": {
		"http.method": "GET",
		"http.path": "/production/field/provinceandversion"
	}
}, {
	"traceId": "adb5f4e54f3182c1",
	"id": "adb5f4e54f3182c1",
	"kind": "SERVER",
	"name": "get /self/production/field/provinceandversion",
	"timestamp": 1605182686550122,
	"duration": 145217,
	"localEndpoint": {
		"serviceName": "produceweb",
		"ipv4": "172.18.0.1"
	},
	"remoteEndpoint": {
		"ipv4": "127.0.0.1",
		"port": 54718
	},
	"tags": {
		"http.method": "GET",
		"http.path": "/self/production/field/provinceandversion",
		"mvc.controller.class": "ProductionFieldController",
		"mvc.controller.method": "listProvinceAndHwversion"
	}
}]



服务B调用服务C的报文

GET /api/materials/finished/model/get?id=2 HTTP/1.1
X-B3-SpanId: 6bc0d5f3db423892
X-B3-ParentSpanId: ffa5382f8d436a5b
X-B3-Sampled: 1
X-B3-TraceId: adb5f4e54f3182c1
Accept: */*
User-Agent: Java/1.8.0_181
Host: 127.0.0.1:7001
Connection: keep-alive


服务B上报的报文

[{
	"traceId": "adb5f4e54f3182c1",
	"parentId": "ffa5382f8d436a5b",
	"id": "6bc0d5f3db423892",
	"kind": "CLIENT",
	"name": "get",
	"timestamp": 1605182686565280,
	"duration": 115108,
	"localEndpoint": {
		"serviceName": "eureka-service-produce",
		"ipv4": "172.18.0.1"
	},
	"tags": {
		"http.method": "GET",
		"http.path": "/api/materials/finished/model/get"
	}
}, {
	"traceId": "adb5f4e54f3182c1",
	"parentId": "adb5f4e54f3182c1",
	"id": "ffa5382f8d436a5b",
	"kind": "SERVER",
	"name": "get /production/field/provinceandversion",
	"timestamp": 1605182686561079,
	"duration": 127734,
	"localEndpoint": {
		"serviceName": "eureka-service-produce",
		"ipv4": "172.18.0.1"
	},
	"remoteEndpoint": {
		"ipv4": "192.168.75.89",
		"port": 49184
	},
	"tags": {
		"http.method": "GET",
		"http.path": "/production/field/provinceandversion",
		"mvc.controller.class": "ProductionFieldController",
		"mvc.controller.method": "listProvinceAndHwversion"
	},
	"shared": true
}]


服务C上报的报文

[{
	"traceId": "adb5f4e54f3182c1",
	"parentId": "ffa5382f8d436a5b",
	"id": "6bc0d5f3db423892",
	"name": "get",
	"kind": "SERVER",
	"timestamp": 1605182686638000,
	"duration": 40717,
	"localEndpoint": {
		"serviceName": "base-service",
		"ipv4": "192.168.75.89"
	},
	"tags": {
		"http.url": "127.0.0.1:7001/api/materials/finished/model/get?id=2",
		"http.status_code": "200"
	},
	"shared": true
}]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值