SpringCloud+Sleuth+Zipkin+Mysql链路追踪实现

前言

Spring Cloud Sleuth 在Spring Boot 1.x时代,是对Zipkin做一个完全整合,不仅实现了以 HTTP 的方式收集跟踪信息,还实现了通过消息中间件来对跟踪信息进行异步收集的封装。就连Zipkin服务端也做了一层封装。而到了 Spring Boot 2.0 之后 Zipkin 不再推荐我们再自定义 Server 端了,Sleuth专注于对Dapper 中的算法进行封装,spring-cloud-starter-zipkin 只是对Zipkin客户端的封装,对于Sleuth在工程中如何使用,Spring官网上是这样写的:

Only Sleuth (log correlation):如果只想使用Sleuth功能,而不想与Zipkin做集成的话,那么你只需要引入spring-cloud-starter-sleuth就可以了,普通开发人员可能没有机会用到这种方式。
Sleuth with Zipkin via HTTP :使用Sleuth并通过HTTP方式集成ZipkinServer,你只需要引入spring-cloud-starter-zipkin就可以了。starter中本身就已经依赖了sleuth和sleuth+zipkin。
在这里插入图片描述
Sleuth with Zipkin over RabbitMQ or Kafka :通过RabbitMQ或者Kafka这些消息中间件进行异步的消息处理,此时需要引入spring-cloud-starter-zipkin,还需要引入spring-rabbit(以Rabbit为例),且Zipkin的服务端也需要做rabbitMq的相应配置。
使用 RabbitMQ 异步发送 span 信息#
为什么选择 RabbitMQ 消息中间件发送 span 信息

1.sleuth 默认采用 http 通信方式,将数据传给 zipkin 作页面渲染,但是 http 本身存在握手链接等延时损耗,且传输过程中如果由于不可抗因素导致通信中断,那么此次通信的数据将会丢失。
2.而使用中间件的话,RabbitMQ 消息队列可以积压千万级别的消息,下次重连之后可以继续消费。且随着线程增多,并发量提升之后,RabbitMQ 异步发送数据明显更具有优势。
3.RabbitMQ 支持消息、队列持久化,可以通过消息状态落库、重回队列、镜像队列等技术手段保证其高可用。

  rabbitmq:
    host: 192.168.200.128
    port: 5672
    username: guest
    password: guest

本文本次仅实现**Sleuth with Zipkin via HTTP**方式。

1.eureka-server注册中心搭建

1.1pom.xml

<!--注册中心-->
       <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

1.2application.yml

server:
  port: 6868
spring:
  application:
    name: eureka   # 服务名称
eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false # 不要注册自己到eureka服务中
    fetch-registry: false # 是否从eureka中获取信息
    service-url: # 配置注册中心访问地址
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

1.3启动引导类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

2.zipkin-server服务端搭建

2.1pom.xml关键设置

    <dependencies>
        <!--eureka客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-netflix-eureka-client</artifactId>
            <version>2.1.1.RELEASE</version>
        </dependency>
        <!--包含spring-cloud-starter-sleuth和spring-cloud-sleuth-zipkin两个依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>
        <!-- zipkin 服务类-->
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-server</artifactId>
            <version>2.12.3</version>
            <!--排除日志,否则报错-->
            <exclusions>
                <exclusion>
                    <artifactId>log4j-slf4j-impl</artifactId>
                    <groupId>org.apache.logging.log4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- zipkin 界面-->
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-autoconfigure-ui</artifactId>
            <version>2.12.3</version>
        </dependency>
        <!-- zipkin 存储到数据库需要引入此类 -->
        <dependency>
            <groupId>io.zipkin.java</groupId>
            <artifactId>zipkin-autoconfigure-storage-mysql</artifactId>
            <version>2.12.3</version>
        </dependency>
        <!--保存到数据库需要如下依赖-->
	    <dependency>
	        <groupId>mysql</groupId>
	        <artifactId>mysql-connector-java</artifactId>
	    </dependency>
	    <dependency>
	        <groupId>org.springframework.boot</groupId>
	        <artifactId>spring-boot-starter-jdbc</artifactId>
	    </dependency>
	    <dependency>
	        <groupId>org.springframework.amqp</groupId>
	        <artifactId>spring-rabbit</artifactId>
	    </dependency>
    </dependencies>

2.2application.yml

server:
  port: 8082
management:
  metrics:
    web:
      server:
        auto-time-requests: false
spring:
  zipkin:
#    关闭本工程的推送到zipkin服务的功能
    enabled: false
    discovery-client-enabled: false
	storage:
	  type: mysql
  rabbitmq:
    host: 192.168.200.128
    port: 5672
    username: guest
    password: guest
    
  # 配置mysql
  datasource:
    schema: classpath:/mysql.sql
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/atest?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: 123456
    # Switch this on to create the schema on startup:
#    initialize: true
#    continueOnError: true

  sleuth:
    enabled: false
#    sampler:
#      probability: 1.0


##数据库脚本创建地址,当有多个是可使用[x]表示集合第几个元素
#spring.datasource.schema[0]=classpath:/mysql.sql

# 关闭自动配置启用所有请求得检测
# 加入下面的配置可以防止报错:java.lang.IllegalArgumentException: Prometheus requires that all meters with the same name have the same set of tag keys.
management:
  metrics:
    web:
      server:
        # 避免访问 zipkin 页面报 java.lang.IllegalArgumentException
        auto-time-requests: false

2.3启动引导类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import zipkin2.server.internal.EnableZipkinServer;

@SpringBootApplication
@EnableEurekaClient
@EnableZipkinServer
public class QddFreightZipkinApplication {

    public static void main(String[] args) {
        SpringApplication.run(QddFreightZipkinApplication.class, args);
    }
}

3.建表sql

手动建数据库表,sql如下:

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,
  `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'
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
 
ALTER TABLE zipkin_spans ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `id`) COMMENT 'ignore insert on duplicate';
ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`, `id`) COMMENT 'for joining with zipkin_annotations';
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(`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';
ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces';
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
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
 
ALTER TABLE zipkin_dependencies ADD UNIQUE KEY(`day`, `parent`, `child`);

运行Zipkin服务端,并访问http:..laocalhost:8082j就可以看到zipkin的界面。如下:
在这里插入图片描述

3.service-provider

3.1pom.xml

添加zipkin依赖

 		<!--eureka客户端-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
      <!-- 服务追踪 -->
       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-starter-zipkin</artifactId>
       </dependency>

3.2application.yml

spring:
  zipkin:
    # 不使用rabbitmq需要配置Zipkin服务器地址
    base-url: http://localhost:8082
  sleuth:
    sampler:
      # 配置采样比例,生产环境应调小,否则影响系统执行效率,或者改为rabbitMq方式
      probability: 1.0

但在测试的过程中我们会发现,有时候,程序刚刚启动后,刷新几次,并不能看到任何数据,原因就是我们的spring-cloud-sleuth收集信息是有一定的比率的,默认的采样率是0.1,该值的大小与系统响应速度成反比。建议生产环境下不要设置过大。

可能是版本更新问题,一些文章中设置的方式是spring.sleuth.sampler.percentage=1,但我查看了源码后发现,现在已经改为probability了。如果按照原来的写应该会报黄。是否生效,我没再进行确认。但如果已经没有这个字段属性了,应该不会生效。

3.3启动引导类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
//import org.springframework.context.annotation.Bean;
import zipkin2.server.internal.EnableZipkinServer;
//import zipkin2.storage.mysql.v1.MySQLStorage;

//import javax.sql.DataSource;

@SpringBootApplication
@EnableEurekaClient
@EnableZipkinServer
public class QddFreightZipkinApplication {

    public static void main(String[] args) {
        SpringApplication.run(QddFreightZipkinApplication.class, args);
    }
//    不使用该bean同样可以完成数据库写操作
//    @Bean
//    public MySQLStorage mySQLStorage(DataSource datasource) {
//        return MySQLStorage.newBuilder().datasource(datasource).executor(Runnable::run).build();
//    }
}

有些文章上说需要在引导类中注册MySQLStorage的Bean,但是本人测试过按照我文章中所写的方法,并不需要该Bean。而且,大部分文章里给的Bean的注册方式都是MySQLStorage.builder()....;。但我用的版本中方法为MySQLStorage.newBuilder()....。因为没有用到这个Bean,所以我不知道是否正确,但至少不报错。
注意DataSource的包名是javax.sql.DataSource。

4.发起请求

因为是测试,所以没有搭建服务consumer,只是使用postman做了请求测试。
如下请求,响应成功。
在这里插入图片描述
查看zipkin界面上显示的追踪到的数据:
在这里插入图片描述
可以点击查看详细的请求数据。
在这里插入图片描述
数据库中相应的数据也已经写入成功。
在这里插入图片描述

总结:

缺陷1:zipkin客户端向zipkin-server程序发送数据使用的是http的方式通信,每次发送的时候涉及到连接和发送过程。如果设置采样率过高,会拉低系统性能,所以不建议生产环境设置过高采样率。
缺陷2:当我们的zipkin-server程序关闭或者重启过程中,因为客户端收集信息的发送采用http的方式会被丢失。

改进方案:

1、通信采用socket或者其他效率更高的通信方式。
2、客户端数据的发送尽量减少业务线程的时间消耗,采用异步等方式发送收集信息。
3、客户端与zipkin-server之间增加缓存类的中间件,例如redis、MQ等,在zipkin-server程序挂掉或重启过程中,客户端依旧可以正常的发送自己收集的信息。

采用以上三种方式会提高系统的执行和响应效率和可靠性。而且spring-cloud也已经为我们提供了采用redis或Es等内存数据存储的方式,以及利用消息中间件进行数据分阶段存储的实现方式。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: SpringCloud和Hadoop可以配合实现分布式网盘系统,SpringCloud可以提供支撑服务,比如服务注册,服务发现,负载均衡,Hadoop则可以构建分布式文件系统,用于存储和管理文件,这样就可以实现一个分布式网盘系统。 ### 回答2: 要实现分布式网盘,可以使用Spring Cloud和Hadoop等技术来进行开发和搭建。 首先,使用Spring Cloud可以实现分布式的服务治理和微服务架构。可以使用Eureka作为服务注册与发现的中心,各个微服务将自身注册到Eureka服务器中,并通过Eureka来发现其他微服务的地址。通过使用Ribbon或Feign等负载均衡组件,可以实现对微服务的负载均衡和调用。此外,还可以使用Spring Cloud Config来统一管理配置文件,从而方便对系统进行配置和管理。 其次,使用Hadoop分布式文件系统HDFS来存储和管理网盘中的文件。HDFS的分布式特性可以将文件划分为多个块,并将这些块存储在不同的节点上,从而实现高可靠性和高性能的分布式文件存储。可以将HDFS作为网盘的底层文件系统,通过在Spring Cloud中调用HDFS提供的API,可以实现文件的上传、下载、删除和修改等功能。 在搭建分布式网盘时,可以使用Spring Cloud实现用户管理和权限控制等功能。可以通过使用Spring Security来实现用户认证和授权,保证只有合法用户可以访问网盘。可以在用户登录后,根据其身份和权限,通过HDFS提供的API来实现对文件的访问控制,例如只有拥有读权限的用户才能下载文件,只有拥有写权限的用户才能上传文件等。 此外,可以使用Spring Cloud的其他组件来实现更多的功能,例如使用Spring Cloud Stream来实现文件传输,使用Spring Cloud Task来实现定时清理和备份任务,使用Spring Cloud Sleuth实现分布式链路追踪等。 总结来说,通过结合Spring Cloud和Hadoop等技术,可以实现分布式网盘系统,从而实现文件的分布式存储、用户管理和权限控制等功能。 ### 回答3: 要实现分布式网盘,可以结合使用Spring Cloud和Hadoop来完成。 首先,我们可以使用Spring Cloud来构建网盘的核心服务,包括用户管理、文件上传下载、文件管理等功能。通过Spring Cloud的微服务架构,可以将这些功能拆分为不同的服务,每个服务可以独立部署,并通过Spring Cloud的注册中心进行服务发现和管理。 接下来,我们可以使用Hadoop来存储和管理分布式网盘的文件。Hadoop提供了分布式文件系统HDFS,可以将大文件切分为小文件,然后分散存储在不同的HDFS节点上,实现文件的分布式存储和冗余备份。同时,Hadoop还提供了分布式计算框架MapReduce,可以用来处理大规模数据集。 在整合Spring Cloud和Hadoop时,可以通过Spring Cloud的服务调用功能来实现文件的上传和下载。当用户上传文件时,可以将文件切分为小文件后,并使用Hadoop的分布式文件系统HDFS进行存储。同时,可以使用Spring Cloud的负载均衡功能,将文件上传请求转发到不同的HDFS节点,实现负载均衡和高可用。 在文件的下载过程中,可以通过Spring Cloud将下载请求转发到对应的HDFS节点上,从而实现文件的并发下载。此外,还可以使用Hadoop的MapReduce进行文件的搜索和统计等操作,提供更多的网盘功能。 总之,结合使用Spring Cloud和Hadoop,可以实现一个分布式网盘系统,具备高可用、分布式存储和计算等优势。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值