10分钟快速开始SkyWalking结合Springboot项目

10分钟快速开始SkyWalking结合Springboot项目

实习期间,公司让我去学习一下链路追踪如何集成到Springboot项目中。

为此有两个方案:

1.opentelementry+jaeger+prometheus

opentelementry 收集器收集线上的metrics和traces,然后发送给jaeger和prometheus去处理。jaeger确实好很多,在一个controller中用多线程调用另一个controller依然能显示一颗完整的调用过程。但是可视化做的不行,需要依赖于grafana。这就导致需要开很多服务,因此不太容易搭建。

2.SkyWalking+elasticsearch

skywalking集成了收集和分析的功能,所以只需要elasticsearch作为存储就行,简单,可视化好,适用于个人和中小公司使用。

依赖配置

xml 复制代码
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.2.0.RELEASE</version>
        </dependency>
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.2.0.RELEASE</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency> <!-- 引入log4j2依赖 -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
            <version>2.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-log4j-2.x</artifactId>
            <version>9.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-trace</artifactId>
            <version>9.1.0</version>
        </dependency>

这边需要排除掉springboot自带的日志框架,很重要

Dockerfile文件编写

yaml 复制代码
version: '3.3'
services:
  elasticsearch:
    image: elasticsearch:7.17.6
    container_name: elasticsearch
    restart: always
    ports:
      - "9201:9200"
    environment:
      - "TAKE_FILE_OWNERSHIP=true" #volumes 挂载权限 如果不想要挂载es文件改配置可以删除
      - "discovery.type=single-node" #单机模式启动
      - "TZ=Asia/Shanghai" # 设置时区
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m" # 设置jvm内存大小
    volumes:
      - ./elasticsearch/logs:/usr/share/elasticsearch/logs
      - ./elasticsearch/data:/usr/share/elasticsearch/data
      #- ./elasticsearch/conf/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
    ulimits:
      memlock:
        soft: -1
        hard: -1
  skywalking-oap-server:
    image: apache/skywalking-oap-server:8.9.1
    container_name: skywalking-oap-server
    depends_on:
      - elasticsearch
    links:
      - elasticsearch
    restart: always
    ports:
      - "11800:11800"
      - "12800:12800"
    environment:
      SW_STORAGE: elasticsearch  # 指定ES版本
      SW_STORAGE_ES_CLUSTER_NODES: elasticsearch:9200
      TZ: Asia/Shanghai
      #volumes:
      #- ./oap/conf/alarm-settings.yml:/skywalking/config/alarm-settings.yml
  skywalking-ui:
    image: apache/skywalking-ui:8.9.1
    container_name: skywalking-ui
    depends_on:
      - skywalking-oap-server
    links:
      - skywalking-oap-server
    restart: always
    ports:
      - "9090:8080"
    environment:
      SW_OAP_ADDRESS: http://skywalking-oap-server:12800
      TZ: Asia/Shanghai

dockerfile如何运行,自行查询即可

启动完成之后,打开http://127.0.0.1:9090,会出现Skywalking的UI界面。

配置日志文件

在/src/main/resources下创建log4j2.xml文件

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>

<Configuration status="INFO">

    <Appenders>

        <!-- 控制台输出 -->

        <Console name="Console" target="SYSTEM_OUT">

            <PatternLayout pattern="%d [%traceId] %-5p %c{1}:%L - %m%n"/>

        </Console>

        <!-- skywalking grpc 日志收集 8.4.0版本开始支持 -->

        <GRPCLogClientAppender name="grpc-log">

            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>

        </GRPCLogClientAppender>

    </Appenders>

    <Loggers>

        <Root level="INFO">

            <AppenderRef ref="Console"/>

            <AppenderRef ref="grpc-log"/>

        </Root>

    </Loggers>

</Configuration>

接下来,只需要写一个简单的测试项目,我这边主要用了我老的influxdb项目。可以参考一下,就不能cv大法了。

controller文件

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import top.warmheart.dao.DeviceDao;
import top.warmheart.pojo.Device;
import top.warmheart.service.impl.DeviceServiceImpl;
import top.warmheart.util.BaseResponse;
import java.time.Instant;
import java.time.LocalDateTime;

/**
 * @author 滚~韬
 * @date 2024/1/29 13:00
 */
@RestController
@RequestMapping("/influx")
public class InfluxDBController {
    @Autowired
    private DeviceServiceImpl deviceServiceImpl;
    @Autowired
    private DeviceDao dao;
    @GetMapping("/queryByTime")
    public BaseResponse Query(LocalDateTime start,LocalDateTime end){
        return dao.QueryByTime(start,end);
    }
    @GetMapping("/queryById")
    public BaseResponse Query(String Id){
        return dao.QueryById(Id);
    }
    @PostMapping("/DeleteByTime")
    public BaseResponse Delete(LocalDateTime start,LocalDateTime end){
        return dao.DeleteByTime(start,end);
    }
    @PostMapping("/insertByBlocking")
    public BaseResponse InsertByBlocking(Device device){
        device.setTime(Instant.now());
        return deviceServiceImpl.InsertDataByBlocking(device);
    }
    @PostMapping("/insert")
    public BaseResponse Insert(Device device){
        device.setTime(Instant.now());
        return deviceServiceImpl.InsertData(device);
    }
}

Service层

java 复制代码
import com.influxdb.annotations.Measurement;
import com.influxdb.client.domain.InfluxQLQuery;
import com.influxdb.client.domain.WritePrecision;
import com.influxdb.exceptions.InfluxException;
import com.influxdb.query.InfluxQLQueryResult;
import com.influxdb.spring.influx.InfluxDB2Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.warmheart.core.Op;
import top.warmheart.core.Query;
import top.warmheart.enums.ErrorCode;
import top.warmheart.model.DeleteModel;
import top.warmheart.model.QueryModel;
import top.warmheart.pojo.Device;
import top.warmheart.service.DeviceService;
import top.warmheart.util.BaseResponse;
import top.warmheart.util.InfluxdbUtils;

import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.List;

import static top.warmheart.decorator.InfluxApiDecorator.*;


/**
 * @author 滚~韬
 * @date 2024/1/26 13:20
 */

@Service
public class DeviceServiceImpl implements DeviceService {

    @Autowired
    private InfluxDB2Properties influxDB2Properties;

    protected static Logger log = LoggerFactory.getLogger(DeviceServiceImpl.class);

    public BaseResponse QueryData(Class<?> clazz, QueryModel queryModel) {
        Measurement annotation = clazz.getAnnotation(Measurement.class);
        if (annotation != null) {
            queryModel
                    .setMeasurement(annotation.name())
                    .setWhere(Op.where(queryModel));
        }
        String build = Query.build(queryModel);
        return QueryData(build);
    }

    public BaseResponse QueryData(String sql) {
        log.info("查询语句:" + sql);
        InfluxQLQueryResult result = getInfluxQLQueryApi().query(new InfluxQLQuery(sql, influxDB2Properties.getBucket()));
        return QueryData(result);
    }

    public BaseResponse QueryData(InfluxQLQueryResult result) {
        if (result == null) {
            return new BaseResponse(200, null, "获取成功,无数据");
        }
        List<Device> pojo = InfluxdbUtils.toPOJO(result, Device.class);
        log.info("查询数据数量为:" + pojo.size() + "--------------------------");
        return new BaseResponse(200, pojo, "获取成功");
    }

    public BaseResponse InsertData(Object o) {
        try {
            getWriteApi().writeMeasurement(WritePrecision.NS, o);
        } catch (Exception e) {
            return new BaseResponse(ErrorCode.SYSTEM_ERROR, "插入数据过程中异常");
        }
        return new BaseResponse(200, o, "插入成功");
    }

    public BaseResponse InsertDataByBlocking(Object o) {
        try {
            getWriteApiBlocking().writeMeasurement(WritePrecision.NS, o);
        } catch (Exception e) {
            return new BaseResponse(ErrorCode.SYSTEM_ERROR, "插入数据过程中异常");
        }
        return new BaseResponse(200, o, "插入成功");
    }

    /**
     * 批量写有问题
     *
     * @param devices
     * @return
     */
    @Deprecated
    public BaseResponse InsertData(List<Device> devices) {
        try {
            getWriteApi().writeMeasurements(WritePrecision.NS, devices);
        } catch (Exception e) {
            return new BaseResponse(ErrorCode.SYSTEM_ERROR, "插入数据过程中异常");
        }
        return new BaseResponse(200, devices, "插入成功");
    }

    public BaseResponse DeleteData(DeleteModel deleteModel) {
        try {
            OffsetDateTime startOff = OffsetDateTime.of(deleteModel.getStart(), ZoneOffset.UTC);
            OffsetDateTime endOff = OffsetDateTime.of(deleteModel.getEnd(), ZoneOffset.UTC);
            getDeleteApi().delete(startOff, endOff, "", influxDB2Properties.getBucket(), influxDB2Properties.getOrg());
        } catch (InfluxException ie) {
            log.warn("InfluxException: " + ie);
            return new BaseResponse(ErrorCode.SYSTEM_ERROR, "删除错误");
        }
        return new BaseResponse(200, null, "删除成功");
    }


}

dao层

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import top.warmheart.enums.ErrorCode;
import top.warmheart.model.DeleteModel;
import top.warmheart.model.QueryModel;
import top.warmheart.pojo.Device;
import top.warmheart.service.DeviceService;
import top.warmheart.util.BaseResponse;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.TreeMap;


/**
 * @Author:滚韬
 * @Date:2024/1/30 14:28
 */
@Component
public class DeviceDao {
    @Autowired
    private DeviceService deviceService;
    /**
     * 根据给定时间范围查询数据
     *
     * @param start 开始时间
     * @param end   结束时间,可选参数,如果不传,则默认为当前时间
     * @return 查询结果的BaseResponse对象
     */
    public BaseResponse QueryByTime(LocalDateTime start,LocalDateTime end){
        QueryModel queryModel = new QueryModel();
        if (start!=null){
            queryModel.setStart(start);
            if(end!=null){
                queryModel.setEnd(end);
            }else{
                queryModel.setEnd(LocalDateTime.now());
            }
        }else {
            return new BaseResponse(ErrorCode.SYSTEM_ERROR,"开始日期不能为空(检查是否格式正确)");
        }
        return deviceService.QueryData(Device.class, queryModel);
    }
    public BaseResponse QueryById(String Id){
        Map<String, Object> map = new TreeMap<>();
        map.put("device_no", Id);
        QueryModel queryModel = new QueryModel();
        queryModel.setMap(map);
        return deviceService.QueryData(Device.class, queryModel);
    }
    public BaseResponse DeleteByTime(LocalDateTime start,LocalDateTime end){
        DeleteModel deleteModel = new DeleteModel();
        if (start!=null){
            deleteModel.setStart(start);
            if(end!=null){
                deleteModel.setEnd(end);
            }else{
                deleteModel.setEnd(LocalDateTime.now());
            }
        }else {
            return new BaseResponse(ErrorCode.SYSTEM_ERROR,"开始日期不能为空(检查是否格式正确)");
        }
        return deviceService.DeleteData(deleteModel);
    }
}

启动

在虚拟机参数里加上这段,注意skywalking-agent.jar要去官网下载,jar包外面的文件也不能丢失,否则会报错

-javaagent:C:/skywalking/skywalking-agent/skywalking-agent.jar

-Dskywalking.agent.service_name=你自己的服务名字

-Dskywalking.collector.backend_service=127.0.0.1:11800

效果

请求查询接口,记得日志要打在service层里面

dashboard介绍

CPM/PPM:服务负荷

slow Services: 慢服务

Un-Health Services (Apdex): Apdex性能指标(1是满分)

Slow Endpoints: 慢请求点

Global Response Latency:百分比响应延时,不同百分比的延时时间,单位ms

相关推荐
devlei19 分钟前
从源码泄露看AI Agent未来:深度对比Claude Code原生实现与OpenClaw开源方案
android·前端·后端
pshdhx_albert1 小时前
AI agent实现打字机效果
java·http·ai编程
沉鱼.442 小时前
第十二届题目
java·前端·算法
努力的小郑2 小时前
Canal 不难,难的是用好:从接入到治理
后端·mysql·性能优化
赫瑞2 小时前
数据结构中的排列组合 —— Java实现
java·开发语言·数据结构
Victor3563 小时前
MongoDB(87)如何使用GridFS?
后端
Victor3563 小时前
MongoDB(88)如何进行数据迁移?
后端
小红的布丁3 小时前
单线程 Redis 的高性能之道
redis·后端
GetcharZp3 小时前
Go 语言只能写后端?这款 2D 游戏引擎刷新你的认知!
后端
周末也要写八哥3 小时前
多进程和多线程的特点和区别
java·开发语言·jvm