Spring Cloud 07 - 分布式链路追踪APM

分布式链路追踪APM

文章目录

一:Spring Cloud Sleuth

Spring Cloud Sleuth 主要功能就是在分布式系统中提供追踪解决方案【sleuth -> 侦探;警犬】

兼容支持了 zipkin,你只需要在pom文件中引入相应的依赖即可

1:为什么要引入sleuth

微服务架构是一个分布式架构,它按业务划分服务单元,一个分布式系统往往有很多个服务单元。

由于服务单元数量众多,业务的复杂性,如果出现了错误和异常,很难去定位。微服务架构中,必须实现分布式链路追踪。

举个例子,在微服务系统中,一个来自用户的请求,请求先达到前端A(如前端界面),然后通过远程调用,达到系统的中间件B、C(如负载均衡、网关等),最后达到后端服务D、E,后端经过一系列的业务逻辑计算最后将数据返回给用户。

对于这样一个请求,经历了这么多个服务,怎么样将它的请求过程的数据记录下来呢?这就需要用到服务链路追踪。

在Spring Cloud Sleuth中集成Zipkin非常的简单,只需要引入相应的依赖和做相关的配置即可

2:sleuth基本术语

Spring Cloud Sleuth采用的是Google的开源项目Dapper的专业术语。

  • Span:基本工作单元,发送一个远程调度任务就会产生一个Span

    • Span是一个64位ID唯一标识的,Span还有其他数据信息,比如摘要、时间戳事件、Span的ID、以及进度ID。
  • Trace:Trace是用另一个64位ID唯一标识的,一系列Span组成的一个树状结构。

    • 请求一个微服务系统的API接口,这个API接口,需要调用多个微服务,调用每个微服务都会产生一个新的Span
    • 所有由这个请求产生的Span组成了这个Trace。
  • Annotation:用来及时记录一个事件的,一些核心注解用来定义一个请求的开始和结束。这些注解包括以下:[客户端和服务端的收发]
    • cs - Client Sent -客户端发送一个请求,这个注解描述了这个Span的开始
    • sr - Server Received -服务端获得请求并准备开始处理它,如果将其sr减去cs时间戳便可得到网络传输的时间。
    • ss - Server Sent (服务端发送响应)--该注解表明请求处理的完成,如果ss的时间戳减去sr时间戳,就可以得到服务器请求的时间。
    • cr - Client Received (客户端接收响应)-此时Span的结束,如果cr的时间戳减去cs时间戳便可以得到整个请求所消耗的时间。

3:sleuth使用案例示例

本文案例一共四个工程采用多Module形式。

  • eureka-server工程,作为服务注册中心,eureka-server的创建过程这里不重复;
  • zipkin-server作为链路追踪服务中心,负责存储链路数据;
  • gateway-service作为服务网关工程,负责请求的转发, 同时它也作为链路追踪客户端,负责产生数据,并上传给zipkin-service;
  • user-service为一个应用服务,对外暴露API接口,同时它也作为链路追踪客户端,负责产生数据。
3.1:构建zipkin-server工程
  1. 新建一个Module工程,取名为zipkin-server,其pom文件继承了主Maven工程的pom文件;

  2. 依赖导入:

    1. 作为Eureka Client,引入Eureka的起步依赖spring-cloud-starter-eureka
    2. 引入zipkin-server依赖和zipkin-autoconfigure-ui依赖,这两个依赖提供了Zipkin的功能和Zipkin界面展示的功能。
xml 复制代码
<!-- zipkin注解  -->
<dependency>
    <groupId>io.zipkin.java</groupId>
    <artifactId>zipkin-server</artifactId>
</dependency>

<dependency>
    <groupId>io.zipkin.java</groupId>
    <artifactId>zipkin-autoconfigure-ui</artifactId>
</dependency>
  1. 在程序的启动类ZipkinServiceApplication加上@EnableZipkinServer开启ZipkinServer的功能
java 复制代码
@SpringBootApplication
@EnableEurekaClient
@EnableZipkinServer // 加入这个注解
public class ZipkinServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZipkinServerApplication.class, args);
    }
}
  1. 在配置文件application.yml文件,指定程序名为zipkin-server,端口为9411,服务注册地址为http://localhost:8761/eureka/。
yaml 复制代码
# 将自己加入到注册中心,这里指定注册中心的地址
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

# 声明自己的地址9411,因此zipkin-server的地址:http://localhost:9411
server:
  port: 9411

spring:
  application:
    name: zipkin-server
3.2:构建user-service模块
  1. 在主Maven工程下建一个Module工程,取名为user-service,作为应用服务,对外暴露API接口。

  2. pom文件继承了主Maven工程的pom文件,并引入:

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- zipkin starter -->
    <!-- 表明自己将会被zipkin追踪 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zipkin</artifactId>
        <version>RELEASE</version>
    </dependency>
</dependencies>
  1. 在配置文件applicatiom.yml

    • spring.sleuth.sampler.percentage = 1.0,在默认的情况下,该值为0.1
yaml 复制代码
# 声明eureka地址
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
# 声明自己的端口
server:
  port: 8762
# 声明应用名称
spring:
  application:
    name: user-service
  
  # 声明zipkin地址,就是zipkin-server的地址
  zipkin:
    base-url: http://localhost:9411
#    base-url: http://localhost:9411/ # 服务端地址
#    sender:
#      #type: web                      # 数据传输方式,web 表示以 HTTP 报文的形式向服务端发送数据
#      type: rabbit
#    rabbitmq:
#      queue: zipkin               # 队列名称
#  rabbitmq:
#    host: 192.168.10.101          # 服务器 IP
#    port: 5672                    # 服务器端口
#    username: guest               # 用户名
#    password: guest               # 密码
#    listener:
#      direct:
#        retry:
#          enabled: true           # 是否开启发布重试
#          max-attempts: 5         # 最大重试次数
#          initial-interval: 5000  # 重试间隔时间(单位毫秒)
#      simple:
#        retry:
#          enabled: true           # 是否开启消费者重试
#          max-attempts: 5         # 最大重试次数
#          initial-interval: 5000  # 重试间隔时间(单位毫秒)
  sleuth:
    sampler:
      probability: 1.0            # 收集数据百分比,默认 0.1(10%)
  1. 建一个/user/hi的API接口,对外提供服务,代码如下
java 复制代码
@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/hi")
    public String hi(){
        return "I'm forezp";
    }
}

启动类UserServiceApplication加上@EnableEurekaClient注解

3.3:构建gateway-service
  1. 新建一个名为gateway-service工程,这个工程作为服务网关

    • 将请求转发到user-service,作为Zipkin客户端,需要将链路数据上传给Zipkin Server,同时它也作为Eureka Client。
  2. 它在pom文件除了需要继承主Maven工程的 pom,还需引入的依赖如下:

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <!-- zuul -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zuul</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- zipkin starter -->
    <!-- 表明自己将会被zipkin追踪 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zipkin</artifactId>
        <version>RELEASE</version>
    </dependency>
</dependencies>
  1. 在application.yml文件,配置程序名为gateway-service,端口为5000:

yaml 复制代码
# 将自己注册到注册中心
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

# 声明自己使用的端口是5000
server:
  port: 5000

# 声明应用名称
spring:
  application:
    name: gateway-service
    
  # zipkin声明
  #  zipkin:
#    base-url: http://localhost:9411/ # 服务端地址
#    sender:
#      #type: web                      # 数据传输方式,web 表示以 HTTP 报文的形式向服务端发送数据
#      type: rabbit
#    rabbitmq:
#      queue: zipkin               # 队列名称
#  rabbitmq:
#    host: 192.168.10.101          # 服务器 IP
#    port: 5672                    # 服务器端口
#    username: guest               # 用户名
#    password: guest               # 密码
#    listener:
#      direct:
#        retry:
#          enabled: true           # 是否开启发布重试
#          max-attempts: 5         # 最大重试次数
#          initial-interval: 5000  # 重试间隔时间(单位毫秒)
#      simple:
#        retry:
#          enabled: true           # 是否开启消费者重试
#          max-attempts: 5         # 最大重试次数
#          initial-interval: 5000  # 重试间隔时间(单位毫秒)
  sleuth:
    sampler:
      probability: 1.0            # 收集数据百分比,默认 0.1(10%)   
      
  zipkin:
    base-url: http://localhost:9411 # 声明 zipkin server地址


# 设置网关映射:以/user-api/**开头的Uri请求,转发到服务名为user-service的服务
zuul:
  routes:
    api-a: 
      path: /user-api/**
      serviceId: user-service

在程序的启动类GatewayServiceApplication

  • 加上@EnableEurekaClient注解开启Eureka Client
  • 加上@EnableZuulProxy注解,开启Zuul代理功能。代码如下:
java 复制代码
@SpringBootApplication
@EnableZuulProxy  // 开启zuul代理功能
@EnableEurekaClient
public class GatewayServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayServiceApplication.class, args);
    }
}

4:在链路数据中添加自定义数据

现在需要实现这样一个功能:需要在链路数据中加上操作人。

这需要在gateway-service上实现。建一个ZuulFilter过滤器,它的类型为"post",order为900,开启拦截。

在拦截逻辑方法里,通过Tracer的addTag方法加上自定义的数据,比如本案例中加入了链路的操作人。

另外也可以在这个过滤器中获取当前链路的traceId信息,traceId作为链路数据的唯一标识,可以存储在log日志中,方便后续查找。

java 复制代码
@Component
public class LoggerFilter extends ZuulFilter {

    @Autowired
    Tracer tracer;
    
    @Override
    public String filterType() {
        return FilterConstants.POST_TYPE;
    }

    @Override
    public int filterOrder() {
        return 900;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    // 在链路中加入操作人是forezp
    @Override
    public Object run() {
        // tracer.addTag
        tracer.addTag("operator","cuihaida");
        System.out.print(tracer.getCurrentSpan().traceIdString());
        return null;
    }
}

5:rabbitMQ组件通讯

在上述的案例中,最终gateway-service收集的数据,是通过Http上传给zipkin-server的

在Spring Cloud Sleuth中支持消息组件来通讯的,下面使用RabbitMQ来通讯。

5.1:改造zipkin-server
  1. 在pom文件将zipkin-server的依赖去掉

  2. 加上spring-cloud-sleuth-zipkin-stream和spring-cloud-starter-stream-rabbit,代码如下:

xml 复制代码
<dependency>
    <!-- zipkin流式管理 -->
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
</dependency>
<dependency>
    <!-- rabbitMQ流式 -->
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
  1. 在application.yml配置上RabbitMQ的配置:
yaml 复制代码
# 声明消息中间件的信息,host & port & username & password
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
  1. 在程序的启动类ZipkinServerApplication上@EnableZipkinStreamServer注解,开启ZipkinStreamServer。代码如下:
java 复制代码
@SpringBootApplication
@EnableEurekaClient
@EnableZipkinStreamServer // 引入stream注解
public class ZipkinServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZipkinServerApplication.class, args);
    }
}
5.2:改造Zipkin Client

包括gateway-service、user-service

  1. 在pom文件中将spring-cloud-starter-zipkin去掉
  2. 新增spring-cloud-sleuth-zipkin-stream和spring-cloud-starter-stream-rabbit
xml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
  1. 同时在applicayion.yml文件加上RabbitMQ的配置,同zipkin-server工程。
yaml 复制代码
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
  1. 就将链路的上传数据从Http改了为用消息代组件RabbitMQ。

6:链路数据存储

6.1:存储在Mysql数据库

Zipkin支持Mysql、Elasticsearch、Cassandra存储

  1. 首先,在zipkin-server工程加上Mysql的连接依赖mysql-connector-java,JDBC的起步依赖spring-boot-starter-jdbc
xml 复制代码
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
  1. 在配置文件application.yml加上数据源的配置,包括数据库的Url、用户名、密码、连接驱动,另外需要配置zipkin.storage.type为mysql
yaml 复制代码
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/zipkin?useUnicode=true&characterEncoding=utf8&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

# 设置存储方式是mysql
zipkin:
  storage:
    type: mysql
  1. 另外需要在Mysql数据库中初始化数据库脚本
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,
  error_count BIGINT
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;

ALTER TABLE zipkin_dependencies ADD UNIQUE KEY(day, parent, child);复制代码
6.2:存储在ES (了解)
  1. 首先在pom文件,加上zipkin的依赖和zipkin-autoconfigure-storage-elasticsearch-http的依赖,代码如下:
xml 复制代码
<dependency>
    <groupId>io.zipkin.java</groupId>
    <artifactId>zipkin</artifactId>
    <version>1.28.0</version>
</dependency>
<dependency>
    <groupId>io.zipkin.java</groupId>
    <artifactId>zipkin-autoconfigure-storage-elasticsearch-http</artifactId>
    <version>1.28.0</version>
</dependency>
  1. 在application.yml文件加上Zipkin的配置,配置了zipkin的存储类型为elasticsearch,使用的StorageComponent为elasticsearch。

  2. 然后需要配置elasticsearch,包括hosts,可以配置多个,用","隔开;index为zipkin等,具体配置如下:

yaml 复制代码
zipkin:
  storage:
    type: elasticsearch
    StorageComponent: elasticsearch
    elasticsearch:
      cluster: elasticsearch # es集群名称
      max-requests: 30 # 最大请求并发数
      index: zipkin # 索引名称
      index-shards: 3 # 分片数
      index-replicas: 1 # 副本数
      hosts: localhost:9200 # es地址

二:Skywalking

1:SkyWalking初探

1.1:APM系统

APM(Application Performance Management) 即应用性能管理系统,是对企业系统即时监控以实现对应用程序性能管理和故障管理的系统化解决方案

应用性能管理,主要是针对企业的关键业务应用进行检测,优化,提高企业应用的可靠性和质量,保证用户得到良好的服务,降低总拥有成本。

APM系统可以帮助理解系统的行为,用于分析性能问题的工具,以便于发生故障的时候,可以快速地定位和解决问题。

APM比较关注的是三个方面:日志(log) + 指标(metrice) + 链路追踪(traces)

主流的APM落地实现落地方案系统:日志 -> ELK Stack, 指标 -> Prometheus,链路追踪 -> SkyWalking

遵循OpenTracing标准

1.2:SkyWalking概述
1.2.1:基本概念

官网:https://skywalking.apache.org

SkyWalking是一个开源的可观测平台,用于从服务和云原生基础设施收集,分析,聚合和可视化数据

SkyWalking提供了一种简便的方式来清晰的观测分布式系统,甚至横跨多个云平台

SkyWalking是一个现代化的应用程序性能监控(APM)系统,尤其专为云原生,基于容器的分布式系统设计。

SkyWalking的核心功能

  • 服务,服务实例,端点(URL)指标分析
  • 根本原因分析,在运行时上分析由进程内代理和ebpf分析器支持的代码
  • 业务拓扑图分析
  • 服务实例和端点(URL)的依赖关系分析
  • 服务和端点检测速度分析,性能优化
  • ...

SkyWalking的特点

  • java生态,功能丰富,社区活跃,迭代迅速
  • 链路追踪,拓扑分析能力强,采用先进的流式拓扑分析
  • 插件丰富,探针无侵入
  • 微内核 + 插件架构,存储,集群管理
  • ...

关键概念

1.2.2:快速入门

1:下载对应的服务端和Agent端【上报服务数据给服务端,进行链路追踪】



然后进入bin目录,选择启动脚本运行:

任务服务检查是否启动成功,看<skyWalking Home>/logs下面的日志文件就可以了


webapp的端口是多少呢?


1.2.3:架构设计

上一个版本架构图

现在版本架构图

skyWalking四大组件

Agent = 探针 => 收集对应服务的数据然后上传到SkyWalking上

设计目标

  • 可观测性 -> Agent
  • 拓扑结构 -> 链路调用关系
  • 轻量级 -> 作为监控系统,不能喧宾夺主,一定要轻量级
  • 可拔插 -> 可以选择是否使用,而不是集中式服务一定要使用,可插拔和轻量级相辅相成
  • 可移植 -> 多平台可用
1.3:JavaAgent监控Spring Boot
1.3.1:agent下载解压
1.3.2:服务接入agent

有三种基本方式配置agent -> 配置文件 || 启动脚本 || IDE开发工具

sh 复制代码
#! /bin/bash

# ============== 下面是启动脚本的方式的基本的实例 ============
export SW_AGENT_NAME=skywalking-demo # 探针的名称,一般指定为监控应用的名称
export SW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 # Collector地址,指向的是服务器中OAP服务
export SW_AGENT_SPAN_LIMIT=1000 # 配置链路的最大Span数量,默认是300个
export JAVA_AGENT=-javaagent:<探针Jar的位置> 
java $JAVA_AGENT -jar skywalking-demo-1.0.0-SNAPSHOT.jar #启动程序
sh 复制代码
# ============= 下面是通过idea启动配置中添加如下的jvm参数 ==================
-javaagent:<探针Jar的位置>
-DSW_AGENT_NAME=skywalking-demo # 探针的名称,一般指定为监控应用的名称
-DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 # Collector地址,指向的是服务器中OAP服务


2:链路追踪实战

2.1:持久化存储

skywalking默认使用的是H2数据库,H2是一个内存型数据库,如果skywalking重启了,数据就没有了【因为没有持久化到磁盘中】

因此需要对skywalking进行持久化存储的配置:一般采用的是es & mysql

因为链路追踪的日志等信息回比较多,所以一般存储在es中,下面将演示使用es进行持久化存储

2.1.1:es的安装和启动

前置工作 -> 解压es,移动目录&重命名,新建一个es用户和组【因为es不支持root启动】,赋予新用户es文件夹的权限

sh 复制代码
tar -zxf elasticsearch-7.17.0-linux-x86.64.tar.gz # 解压

mv elasticsearch-7.17.0-linux-x86.64 /usr/local/elasticsearch-7.17.0 # 移动到/usr/local下并重命名

groupadd elasticsearch # 创建一个组叫做elasticsearch
useradd elasticsearch -g elasticsearch # 创建一个用户名:elasticsearch,所属组 elasticsearch

su elasticsearch # 切换到这个新建的用户下
chown -R elasticsearch:elasticsearch elasticsearch-7.17.0/ # 权限赋予

配置es的配置文件:在config/jvm.options

sh 复制代码
vim config/jvm.options

启动并查看启动日志

sh 复制代码
bin/elasticsearch -d # -d是指定后台启动
tail -f log/elasticsearch.log # 查看日志 -f 追加型

测试下是否启动成功 -> curl http://localhost:9200 # you know, for search

自此,es配置启动完成

2.1.2:进行skywalking的配置

修改配置文件

sh 复制代码
exit # 退出elasticsearch这个用户,回到root
cd skywalking-apm-9.3/
sh 复制代码
vim config/application.yml

只需要改下面几个部分

重启oap服务


2.1.3:使用mysql说明【补充】
  1. skywalking中配置信息修改:
  • 修改storage.selector为mysql
  • 在下面指定url, username, password
  1. 默认是不支持mysql的,需要将mysql的驱动包拷贝到oap-lib目录下才可以
2.2:微服务链路追踪
2.2.1:dubbo示例

provider


主启动类支持dubbo并且实现对应的接口方法

consumer



2.2.2:spring cloud示例

gateway -> order(推送消息给finance) -> user(获取支付用户和收款用户) & product(查找商品)

java 复制代码
// order核心代码
@Slf4j
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    private final UserFeignService userFeignService;
    private final ProductFeignService productFeignService;
    private final RocketMQTemplate rocketMQTemplate;
    
    // 构造器注入
    public OrderServiceImpl(UserFeignService userFeignService, ProductFeignService productFeignService, RocketMQTemplate rocketMQTemplate) {
        this.userFeignService = userFeignService;
        this.productFeignService = productFeignService;
        this.rocketMQTemplate = rocketMQTemplate;
    }
    
    @Override
    public OrderDTO detailById(Long id) {
        Order order = super.getById(id);
        if (order != null) {
            OrderDTO dto = new OrderDTO();
            BeanUtils.copyProperties(order, dto);
            // 查找支付用户
            dto.setPalyerUser(userFeignService.findById(order.getPayerUserId()));
            // 查找收款用户
            dto.setReceiverUser(userFeignService.findById(order.getReceiveUserId()));
            // 查找商品
            dto.setProductId(productFeignService.findById(order.getProductId()));
            // 发送MQ消息给finance
            // ORDER -> key
            // DETAILS -> tag
            SendResult result = rocketMQTemplate.syncSend("ORDER:DETAILS", dto);
            log.info("发送状态:{}", result.getSendStatus());
            return dto;
        }
        return null;
    }
}

接受MQ消息

product核心逻辑

所有的服务都使用这个




2.3:日志采集
2.3.1:引入依赖
xml 复制代码
<!-- skyWalking-log -->
<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-logback-1.x</artifactId>
    <version>8.14.0</version>
</dependency>
2.3.2:LogBack配置
xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 根节点configuration -->
<!-- scan属性 -> 是否开启热加载配置文件,此属性设置成为true之后,配置文件如果发生改变,将会被重新加载,默认就是true -->
<!-- scanPeriod属性 -> 配置热加载的间隔时间,默认是1分钟,如果要关闭热加载,设置为5s检查一次 -->
<!-- debug属性 -> 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false -->
<configuration scan="true" scanPeriod="5 seconds">
    <!-- appender负责写日志的组件,它有两个必要属性name和class。name指定appender名称,class指定appender的全限定名。 -->
    <!-- 日志输出到控制台 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!-- 日志输出格式 -->
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <!-- 异步输出 -->
    <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
        <!-- 丢弃阈值 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 队列大小 -->
        <queueSize>1024</queueSize>
        <!-- 是否启用队列满丢弃日志记录功能 -->
        <neverBlock>true</neverBlock>
        <!-- 引用 -->
        <appender-ref ref="STDOUT"/>
    </appender>
    <!-- grpc日志输出,为skywalking控制台输出做准备 -->
    <appender name="grpc-log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
                <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
            </layout>
        </encoder>
    </appender>
    <!-- 根节点, 默认的日志级别是DEBUG,这里指定成为info -->
    <root level="INFO">
        <appender-ref ref="ASYNC"/>
        <appender-ref ref="grpc-log"/>
    </root>
</configuration>

请求调用之后,就可以在控制台看到对应的TID信息,说明链路追踪日志打印完成

2.4:告警通知
2.4.1:告警规则和webhook

skywalking有如下告警规则:

  • metrice-name:指标的名称,也是OAL脚本中的指标名称,可以配置告警的指标有:服务,实例,端点,服务关系,实例关系,端点关系
  • op:操作符,例如 >, <等等
  • threshold:阈值
  • period:告警规则多久被检查一次,是一个时间窗口
  • count:在一个时间窗口中,满足op超过阈值的次数达到count值,就会触发告警
  • slience-period:在时间N中触发告警后,在N + slience-period这段时间不告警
  • message:告警时通知的消息


触发了告警之后通知到哪里呢? -----> 将会通知到webhooks的配置中

2.4.2:演示一下

写一个读取通知的小demo

将这个服务推送到服务器并启动

webhook添加通知路径

重启skywalking

服务触发告警之后将会看到(不能立刻看到,2分钟后大概)

2.4.3:对接钉钉示例

进入skywalking官网,选择对应的版本

文档选择

钉钉中创建钉钉机器人


也可以通过群聊添加


修改配置文件


生成钉钉机器人的URL地址


改好之后,重启OAP

2.5:细粒度追踪

之前的链路追踪展示的效果粒度较粗,因此引入skyWalking工具包,细粒度化

2.5.1:引入依赖
xml 复制代码
<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-trace</artifactId>
    <version>8.14.0</version>
</dependency>
2.5.2:获取traceId和注解
java 复制代码
@RequestMapping("/trace")
public void traceId() {
    // 可以向上下文对象中绑定key/value数据
    TraceContext.putCorrelation("name", "xiaoliu");
    // 获取traceId
    log.info("get trace id: ()", TraceContext.traceId());
}

@Trace标签和@Tag标签

有了这两个标签,到时候看UI界面就会有对应的信息

相关推荐
退役小学生呀19 分钟前
十九、云原生分布式存储 CubeFS
分布式·docker·云原生·容器·kubernetes·k8s
smileNicky1 小时前
Kafka 为什么具有高吞吐量的特性?
分布式·kafka
小白不想白a7 小时前
【Hadoop】HDFS 分布式存储系统
hadoop·分布式·hdfs
随心............8 小时前
Spark面试题
大数据·分布式·spark
MrSYJ10 小时前
AuthenticationEntryPoint认证入口
java·spring cloud·架构
Hello.Reader10 小时前
用一根“数据中枢神经”串起业务从事件流到 Apache Kafka
分布式·kafka·apache
中工钱袋11 小时前
java后端如何实现下载功能
spring·maven
似水流年流不尽思念12 小时前
Spring Bean有哪些生命周期回调方法?有哪几种实现方式?
后端·spring·面试
找不到、了14 小时前
常用的分布式ID设计方案
java·分布式
尚学教辅学习资料14 小时前
SSM从入门到实战:3.2 SpringMVC请求处理与控制器
spring·ssm·springmvc