跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

SkyWalking 接入 Spring Cloud Alibaba 微服务:从链路追踪到告警

记录Spring Cloud Alibaba微服务集成SkyWalking的全过程:从Docker Compose启动Nacos和SkyWalking,到构建order和inventory两个服务的调用链,然后通过Java Agent接入追踪,展示Topology和Trace详情。进一步说明了手动埋点、告警配置、日志TraceId注入和性能剖析的实用做法,最后给出了生产环境存储选型、采样率控制和安全隔离等实践建议。

KernelLab发布于 2026/6/17更新于 2026/8/1816 浏览
SkyWalking 接入 Spring Cloud Alibaba 微服务:从链路追踪到告警

一个请求要穿过七八个服务,早就不是新鲜事了。但问题一来——到底慢在哪个环节,是数据库堵了还是下游超时——光靠猜可不行。全链路追踪就是给你一双眼睛,直接看清整个调用链。

SkyWalking 在开源 APM 里算得上老牌选手了,支持分布式追踪、拓扑分析、性能指标聚合,而且与 Spring Cloud Alibaba 生态配合起来很顺手。这里就记一下我实际搭建的过程,从拉服务、接 Agent,到手动埋点和告警,把最实用的部分串一遍。

先把骨架跑起来

用 Docker Compose 一把梭启动 SkyWalking 和 Nacos,省去单独部署的麻烦。创建一个 docker-compose.yml:

version: '3.8'
services:
  nacos:
    image: nacos/nacos-server:v2.2.3
    container_name: nacos-standalone
    environment:
      - MODE=standalone
    ports:
      - "8848:8848"
      - "9848:9848"
    restart: always
  oap:
    image: apache/skywalking-oap-server:9.7.0
    container_name: skywalking-oap
    ports:
      - "11800:11800"
      - "12800:12800"
    environment:
      - SW_STORAGE=h2
    depends_on:
      - nacos
    restart: always
  ui:
    image: apache/skywalking-ui:9.7.0
    container_name: skywalking-ui
    ports:
      - 
    
       
    
       
     
"8080:8080"
environment:
-
SW_OAP_ADDRESS=oap:12800
depends_on:
-
oap
restart:
always

执行 docker-compose up -d,等个两分钟左右,Nacos 在 http://localhost:8848/nacos,SkyWalking UI 在 http://localhost:8080,就能打开了。

搭一个最简单的调用链

模拟两个服务:order-service 下单,inventory-service 扣库存。通过 Feign 调用,服务注册到 Nacos。

父工程统一管理依赖

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>skywalking-sca-demo</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>
    <modules>
        <module>order-service</module>
        <module>inventory-service</module>
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.18</version>
        <relativePath/>
    </parent>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-cloud.version>2021.0.8</spring-cloud.version>
        <spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

inventory-service:扣库存

添加 Nacos 发现依赖和 Web 起步,写一个 /deduct 接口,模拟 200ms 处理延迟:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>skywalking-sca-demo</artifactId>
        <version>1.0.0</version>
    </parent>
    <artifactId>inventory-service</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>
</project>

application.yml:

server:
  port: 8082
spring:
  application:
    name: inventory-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
management:
  endpoints:
    web:
      exposure:
        include: '*'

主类和 Controller:

package com.example.inventory;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class InventoryServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(InventoryServiceApplication.class, args);
    }
}
package com.example.inventory.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/inventory")
public class InventoryController {
    private static final Logger log = LoggerFactory.getLogger(InventoryController.class);

    @PostMapping("/deduct")
    public String deduct(@RequestParam String productId, @RequestParam Integer count) {
        log.info("收到扣减库存请求:productId={}, count={}", productId, count);
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        log.info("库存扣减成功");
        return "success";
    }
}

order-service:下单并调用库存

同样注册到 Nacos,通过 Feign 调 inventory-service。

pom.xml 增加 OpenFeign:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>skywalking-sca-demo</artifactId>
        <version>1.0.0</version>
    </parent>
    <artifactId>order-service</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>
</project>

application.yml:

server:
  port: 8081
spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    feign:
      client:
        config:
          default:
            connectTimeout: 5000
            readTimeout: 5000
management:
  endpoints:
    web:
      exposure:
        include: '*'

定义 Feign 接口:

package com.example.order.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(value = "inventory-service")
public interface InventoryFeignClient {
    @PostMapping("/inventory/deduct")
    String deduct(@RequestParam("productId") String productId, @RequestParam("count") Integer count);
}

Controller 里调用它:

package com.example.order.controller;

import com.example.order.feign.InventoryFeignClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/order")
public class OrderController {
    private static final Logger log = LoggerFactory.getLogger(OrderController.class);

    @Autowired
    private InventoryFeignClient inventoryFeignClient;

    @PostMapping("/create")
    public String createOrder(@RequestParam String userId, @RequestParam String productId, @RequestParam Integer count) {
        log.info("收到创建订单请求:userId={}, productId={}, count={}", userId, productId, count);
        String result = inventoryFeignClient.deduct(productId, count);
        log.info("订单创建成功");
        return "order created, inventory result: " + result;
    }
}

现在依次启动两个服务,用 curl 模拟一下请求:

curl -X POST "http://localhost:8081/order/create?userId=1&productId=P001&count=1"

返回 order created, inventory result: success,调用链通了。但是没有追踪,下面就把 SkyWalking 接上去。

用 SkyWalking Agent 把链路照亮

SkyWalking 靠 Java Agent 实现无侵入埋点。你只需要在启动参数里加一个 jar 包路径,它就能自动拦截 Spring MVC、Feign、JDBC 等常见组件。

下载与 SkyWalking 9.7.0 对应的 Agent 包,解压到比如 /opt/skywalking-agent。里面有一个核心 skywalking-agent.jar 和 config 目录。

给每个服务准备一个配置文件,主要是声明服务名和 OAP 地址。在 config/ 下新建 order-service.config:

agent.service_name=order-service
collector.backend_service=127.0.0.1:11800

inventory-service.config:

agent.service_name=inventory-service
collector.backend_service=127.0.0.1:11800

然后启动时指定参数:

java -javaagent:/opt/skywalking-agent/skywalking-agent.jar \
     -Dskywalking.agent.config_path=/opt/skywalking-agent/config/order-service.config \
     -jar order-service/target/order-service-1.0.0.jar
java -javaagent:/opt/skywalking-agent/skywalking-agent.jar \
     -Dskywalking.agent.config_path=/opt/skywalking-agent/config/inventory-service.config \
     -jar inventory-service/target/inventory-service-1.0.0.jar

再跑一次 curl,然后打开 SkyWalking UI。你会看到一幅拓扑图,order 到 inventory 的箭头清清楚楚。点进 Trace,整条链的瀑布流展示每段耗时——200ms 的 Thread.sleep 一眼就能看见。

埋点不够自动?自己加两笔

自动追踪覆盖了大部分场景,但有些业务方法或者日志环节你想更细粒度,可以手动创建 Span。

在 order-service 里加一个插入数据库的操作(这里用 H2 演示),然后对数据库调用打个 Span。

先补充依赖(MyBatis、H2):

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.3.0</version>
</dependency>

配置数据源:

spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
    username: sa
    password: password

创建实体和 Mapper:

// Order.java
public class Order {
    private Long id;
    private String userId;
    private String productId;
    private Integer count;
    // getters and setters...
}
// OrderMapper.java
@Mapper
public interface OrderMapper {
    @Insert("INSERT INTO orders(user_id, product_id, count) VALUES(#{userId}, #{productId}, #{count})")
    void insert(Order order);
}

在 Service 里用 @Trace 给整个方法生成 Span,并对数据库保存单独包裹一个 TraceContext.createLocalSpan:

package com.example.order.service;

import com.example.order.feign.InventoryFeignClient;
import com.example.order.mapper.OrderMapper;
import com.example.order.model.Order;
import org.apache.skywalking.apm.toolkit.trace.Trace;
import org.apache.skywalking.apm.toolkit.trace.TraceContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderService {
    private static final Logger log = LoggerFactory.getLogger(OrderService.class);

    @Autowired
    private InventoryFeignClient inventoryFeignClient;

    @Autowired
    private OrderMapper orderMapper;

    @Trace
    public String createOrder(String userId, String productId, Integer count) {
        log.info("开始创建订单");
        try (final var ignored = TraceContext.createLocalSpan("OrderService.saveToDB")) {
            Order order = new Order();
            order.setUserId(userId);
            order.setProductId(productId);
            order.setCount(count);
            orderMapper.insert(order);
            log.info("订单已保存到数据库");
        }
        String result = inventoryFeignClient.deduct(productId, count);
        log.info("订单创建流程结束");
        return result;
    }
}

Controller 里注入 Service 调用即可。再次请求后,追踪详情里会多出一个 OrderService.saveToDB 的小片段,它的耗时就是那一次 Insert 的时间。

调一下告警,别等用户来报

能在 UI 上看到慢请求还不够,最好能自己喊出来。SkyWalking 在 OAP 端配置告警规则。

进入容器 docker exec -it skywalking-oap /bin/sh,编辑 config/alarm-settings.yml,加一条:如果 order-service 的 /order/create 平均响应时间超过 500ms,就告警:

rules:
  service_resp_time_rule:
    metrics-name: service_resp_time
    threshold: 500
    period: 10
    count: 1
    silence-period: 10
    include-names:
      - order-service
    message: Response time of service {name} is more than {value} ms in {period} minutes.
    webhooks: []  # 这里可以填你的钉钉或企业微信地址

重启容器 docker restart skywalking-oap。然后你在 order-service 里临时 Thread.sleep(600) 并发几个请求,等一会儿 SkyWalking UI 的 Alarm 页面就会出现告警记录。

日志里带上 TraceId

追踪的另一个好处是把 TraceId 塞进日志。只需要在 logback 配置里引入 SkyWalking 的 Layout。

添加依赖:

<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-logback-1.x</artifactId>
    <version>8.16.0</version>
</dependency>

logback-spring.xml:

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} - %msg%n</pattern>
            </layout>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

之后控制台日志就会变成:

2023-10-27 10:00:00.123 [TID:1234567890abcdef] [http-nio-8081-exec-1] INFO c.e.o.c.OrderController - 收到创建订单请求...

从 UI 的 Trace 详情里进 Logs 页面,就能顺着 TraceId 把调用过程中所有的日志串起来。前提是你的日志收集工具(比如 ELK)也接入了这个格式。

性能剖析:抓住那行慢代码

有时候不是整个方法慢,而是里面某一句 SQL 或循环搞鬼。SkyWalking 的 Performance Profiling 可以在生产环境下直接在线分析。

在 Trace 页找到一个慢请求,右上角点 Profile,设置采样间隔(比如 10ms)和最大采样数(比如 1000),就开始收集线程堆栈。结束之后你会得到一张火焰图,哪个方法花的时间最久一目了然。

这个功能在某些场景里比本地调试还管用,毕竟线上的真实数据和并发环境没法复刻。

上生产前注意几点

上面为了演示,用了 H2 内存存储和默认配置。如果要认真用,几个地方要动一下:

  • 存储换成 Elasticsearch:H2 重启就丢,ES 持久且扛得住大量数据。
  • 版本对齐:Agent 和 OAP 尽量保持相同大版本,免得追踪数据错乱。
  • 采样率:高并发下适当调低 agent.sample_n_per_3_secs,别让 OAP 被数据淹了。
  • 端口安全:OAP 的 11800(gRPC)和 12800(HTTP)别暴露到公网。
  • 监控 OAP 本身:OAP 要是崩了,整个可观测就瞎了,它的 CPU 和内存也要纳入日常巡检。

这些点处理好了,SkyWalking 能长期稳定地帮你看住微服务调用链。

目录

  1. 先把骨架跑起来
  2. 搭一个最简单的调用链
  3. 父工程统一管理依赖
  4. inventory-service:扣库存
  5. order-service:下单并调用库存
  6. 用 SkyWalking Agent 把链路照亮
  7. 埋点不够自动?自己加两笔
  8. 调一下告警,别等用户来报
  9. 日志里带上 TraceId
  10. 性能剖析:抓住那行慢代码
  11. 上生产前注意几点
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Ubuntu 磁盘空间与分区查看命令整理
  • AI 产品经理全流程工作指南:从需求定义到模型验收
  • Spring Boot 自动配置:原理、条件注解与手写 Starter
  • 让 AI 文案更像人写的 15 条提示词
  • 大模型 Agent Skills 与 OpenCode 的安装配置指南
  • Trae IDE 实战:利用 MCP Server - Figma AI Bridge 自动生成前端代码
  • 工业具身智能落地:从识别到执行的实操思路
  • Android 插件化开发:如何在插件中加载和使用 R 资源
  • Spring Boot RESTful API 开发与测试指南
  • 如何提升自身 WEB 渗透能力?
  • Studio One 7 核心功能解析:从创作到母带的一体化 DAW 工作流
  • Faiss 数据结构与索引类型详解
  • AI Agent 综述:核心概念、架构设计与开源实践
  • 转行 AI 产品经理:行业趋势与核心面试指南
  • Nano Banana AI绘画提示词资源站推荐与使用指南
  • 前端数据库 IndexedDB 详解:构建离线 Web 应用
  • 大语言模型存在“逆转诅咒”,反向推理准确率极低
  • 无人机视觉语言导航(一):基本概念与定义
  • AI 产品经理实战进阶指南:核心技能与职业转型路径
  • 本地部署 GLM-4-9B 开源大模型方法及对话测试

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online