在前面的例子里,我们有两个微服务,分别是订单服务和物流服务,随着业务的增加,就会有越来越多的微服务存在,他们之间也会有更加复杂的调用关系。
这个调用关系,仅仅通过观察代码,会越来越难以识别,所以就需要通过 zipkin 服务链路追踪服务器 这个东西来用图片进行识别了。
Zipkin是一个分布式的服务跟踪系统,可以帮助我们收集分布式系统的性能数据,并跟踪请求在不同服务间的流转情况。 它通过在不同的服务间发送轻量级跟踪数据并存储它们,来帮助我们理解不同子系统间的互动状态和分析性能指标。
Zipkin的核心是将分布式交易的跟踪信息进行聚合、存储和展示,并提供了强大的工具来分析这些数据和定位系统问题。Zipkin主要包括四个组件:Collector、Storage、API和UI,下面来分别介绍。
Collector:用于聚合、处理和存储跟踪数据,并将这些数据发送到Storage进行存储。
Storage:将接收到的跟踪数据存储在后端存储(如:Mysql、Elasticsearch等)中,用于以后查询和分析。
API:用于存储、查询跟踪数据,并通过UI展示查询结果。
UI:展示通过API查询到的跟踪数据,以及相关的分析工具。
改造订单+物流服务
先添加依赖:
zipkin依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zipkin</artifactId> </dependency>
两个的配置文件都加上
spring: zipkin: base-url: http://localhost:9411
抽样策略
Zipkin支持通过配置抽样策略的方式来控制采集和存储跟踪数据的量,从而避免造成系统资源占用过高,降低系统的可用性。 Zipkin提供了三种基本的采样策略:
AlwaysSample:表示始终采样所有的请求数据,不论你的系统请求频率有多高,对性能会有很大的影响,不建议在生产环境中使用。
PercentageBasedSampler:表示基于一定比例进行采样。其中,比率由参数“rate”来指定。例如,设定为0.5,则意味着只有一半的请求会被保留下来,另一半请求则会被丢弃。这个策略适用于大多数生产环境中的情况。
ProbabilitySampler:表示以一个定义好的概率来决定是否采样一个请求。概率值由参数“samplingProbability”来确定,默认为0.001。
如果以上的采样策略不能够满足您的需求,您也可以自己实现一个采取策略,Zipkin提供了接口可以供您扩展,只需要实现Sampler接口即可。
在启动类里配置 Sampler 抽样策略: ALWAYS_SAMPLE 表示持续抽样
@Bean public Sampler defaultSampler() { return Sampler.ALWAYS_SAMPLE; }
两个服务的启动类都要加。
zipkin下载
你可以在Zipkin官方网站上下载最新版本的Zipkin Server jar包。下载地址为:https://search.maven.org/artifact/io.zipkin.java/zipkin-server 你也可以在Maven中央仓库中查找对应版本的依赖。具体依赖信息如下:
<dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin-server</artifactId> <version>2.23.2</version> </dependency>
可以修改上述配置中的版本号来获取不同版本的Zipkin Server jar包。
下载好了以后,用 java -jar命令运行,如果端口被占用,用下面的方法来解除。(以下方法仅适用于我遇到的情况)
查询9411端口使用情况
netstat -ano | findstr ":9411"
发现被19500占用
TCP 0.0.0.0:9411 0.0.0.0:0 LISTENING 19500 TCP [::]:9411 [::]:0 LISTENING 19500
查看19500是什么进程
tasklist | findstr "19500"
得
javaw.exe 19500 Console 1 585,120 K
最终发现是javaw,执行命令 taskkill /im javaw.exe /f 杀死进程即可,最好使用 taskkill /pid 进程号 /f 按进程号杀死进程。
重新启动jar,成功了。
验证
依次启动注册中心,和两个服务,其中oms有1个集群共两个端口。
访问:http://localhost:9411/zipkin/dependency/
访问一次:http://localhost:8084/logistic/create 这就触发一次feign调用。
再访问zipkin, 就看到了这个:
总结
Zipkin是一种分布式跟踪系统,它可以帮助开发人员识别分布式系统中的性能问题。以下是Zipkin的优缺点:
优点:
易于使用:Zipkin易于安装和配置,并提供简单直观的用户界面,使开发人员可以轻松使用和理解系统性能和瓶颈问题。
能够识别延迟:Zipkin能够帮助开发人员识别分布式系统中的延迟并分析其原因。开发人员可以使用这些信息来查找瓶颈和优化性能。
可扩展性:Zipkin使用代码库和模块结构,以适应大型分布式系统,因此随着系统的扩大,它可以轻松地扩展。
缺点:
对于小规模项目来说,Zipkin的配置可能会有些冗长复杂,需要一定的学习成本。
Zipkin需要将跟踪数据存储在专门的存储系统中,存储系统的维护和管理可能需要一定的成本和精力。
在高并发环境中,Zipkin的性能可能会受到影响,因为它需要捕获和处理大量的跟踪数据。
zipkin存储数据
注意,本教程并没有用数据库来给zipkin提供存储, Zipkin Server默认存储追踪数据至内存中,这种方式并不适合生产环境,一旦server关闭重启或者服务崩溃,就会导致历史数据消失。Zipkin支持修改存储策略使用其他存储组件,支持MySQL,Elasticsearch等。
1、数据库脚本
(将链路追踪数据存储到MySQL中,实现同步处理)
打开MySQL数据库,创建zipkin库,执行一下SQL脚本。
官网地址:zipkin/mysql.sql at master · openzipkin/zipkin · GitHub
CREATE TABLE IF NOT EXISTS zipkin_spans ( `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit', `trace_id` BIGINT NOT NULL, `id` BIGINT NOT NULL, `name` VARCHAR(255) NOT NULL, `remote_service_name` VARCHAR(255), `parent_id` BIGINT, `debug` BIT(1), `start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL', `duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query', PRIMARY KEY (`trace_id_high`, `trace_id`, `id`) ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci; ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds'; ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames'; ALTER TABLE zipkin_spans ADD INDEX(`remote_service_name`) COMMENT 'for getTraces and getRemoteServiceNames'; ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range'; CREATE TABLE IF NOT EXISTS zipkin_annotations ( `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit', `trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id', `span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id', `a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1', `a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB', `a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation', `a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp', `endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null', `endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address', `endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null', `endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null' ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci; ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate'; ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans'; ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds'; ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames'; ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces and autocomplete values'; ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces and autocomplete values'; ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job'; CREATE TABLE IF NOT EXISTS zipkin_dependencies ( `day` DATE NOT NULL, `parent` VARCHAR(255) NOT NULL, `child` VARCHAR(255) NOT NULL, `call_count` BIGINT, `error_count` BIGINT, PRIMARY KEY (`day`, `parent`, `child`) ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
把以上代码转存成sql文件
2、部署Zipkin服务端
添加启动参数,重新部署服务端
官网地址:zipkin/zipkin-server-shared.yml at master · openzipkin/zipkin · GitHub
java -jar zipkin-server-2.10.1-exec.jar --STORAGE_TYPE=mysql --MYSQL_HOST=localhost --MYSQL_TCP_PORT=3306 --MYSQL_USER=root --MYSQL_PASS=NULL --MYSQL_DB=zipkin
再调用服务,可以看到数据存到mysql了。