淘客返利系统的日志追踪与链路监控:SkyWalking 与 OpenTelemetry 集成方案

淘客返利系统的日志追踪与链路监控:SkyWalking 与 OpenTelemetry 集成方案

大家好,我是 微赚淘客系统3.0 的研发者省赚客!

微赚淘客系统3.0由订单、返利、账户、通知等12个微服务组成,用户一次"下单→返利到账"操作涉及5+次跨服务调用。早期缺乏全链路追踪能力,故障定位平均耗时超30分钟。我们基于 SkyWalking + OpenTelemetry 构建统一可观测体系,实现毫秒级链路还原、服务依赖拓扑自动生成、慢接口自动告警。

一、SkyWalking Agent 无侵入接入

所有 Java 服务通过 JVM 参数挂载 SkyWalking Agent:

bash 复制代码
-javaagent:/opt/skywalking-agent/skywalking-agent.jar
-Dskywalking.agent.service_name=rebate-service
-Dskywalking.collector.backend_service=skywalking-oap:11800

无需修改代码即可自动埋点 Spring MVC、Feign、Redis、JDBC 等组件。例如 juwatech.cn.rebate.controller.RebateController 的 /api/rebate/query 接口自动成为 Trace 入口。

二、OpenTelemetry 手动埋点关键业务逻辑

对于异步任务、消息消费等 Agent 无法覆盖的场景,使用 OpenTelemetry API 手动创建 Span:

java 复制代码
// juwatech.cn.rebate.consumer.OrderPlacedConsumer
@Component
public class OrderPlacedConsumer {

    private static final Tracer tracer = GlobalOpenTelemetry.getTracer("rebate-service");

    @KafkaListener(topics = "order.placed")
    public void handle(String eventJson, ConsumerRecord<String, String> record) {
        // 从 Kafka Headers 恢复上下文
        Context parentContext = KafkaPropagation.extract(Context.current(), record.headers());

        Span span = tracer.spanBuilder("process.rebate.calculation")
            .setParent(parentContext)
            .startSpan();

        try (Scope ignored = span.makeCurrent()) {
            OrderPlacedEvent event = JSON.parseObject(eventJson, OrderPlatedEvent.class);
            
            // 添加业务标签
            span.setAttribute("order.id", event.getOrderId());
            span.setAttribute("user.id", event.getUserId().toString());

            // 核心计算逻辑
            BigDecimal amount = rebateCalculator.calculate(event.getTrackId());
            
            // 记录关键事件
            span.addEvent("rebate.calculated", Attributes.of(
                AttributeKey.stringKey("amount"), amount.toPlainString()
            ));

            rebateRecordMapper.insert(new RebateRecord(event.getOrderId(), amount));
        } catch (Exception e) {
            span.setStatus(StatusCode.ERROR, e.getMessage());
            throw e;
        } finally {
            span.end();
        }
    }
}

三、跨服务上下文透传

在 Feign 调用中自动注入 Trace Header:

java 复制代码
// juwatech.cn.common.feign.TbkClient
@FeignClient(name = "tbk-service", url = "${tbk.api.url}")
public interface TbkClient {

    @PostMapping("/v1/relate")
    RelateResponse getRelate(@RequestBody RelateRequest request);
}

SkyWalking Agent 自动拦截 Feign 请求,在 HTTP Header 中添加 sw8 字段,下游服务自动关联至同一 Trace。

对于 RabbitMQ/Kafka,需手动注入:

java 复制代码
// juwatech.cn.order.service.OrderService
public void publishEvent(OrderPlacedEvent event) {
    Span currentSpan = Span.fromContext(Context.current());
    Map<String, String> headers = new HashMap<>();
    OpenTelemetry.getPropagators().getTextMapPropagator()
        .inject(Context.current(), headers, Map::put);

    Message message = MessageBuilder.withPayload(JSON.toJSONString(event))
        .setHeader("traceparent", headers.get("traceparent"))
        .build();

    rabbitTemplate.send("order.exchange", "placed", message);
}

消费者端反向提取:

java 复制代码
public void onMessage(Message message) {
    Map<String, String> carrier = Collections.singletonMap(
        "traceparent", message.getMessageProperties().getHeader("traceparent")
    );
    Context extracted = OpenTelemetry.getPropagators().getTextMapPropagator()
        .extract(Context.current(), carrier, MapGetter.INSTANCE);
    
    try (Scope scope = extracted.makeCurrent()) {
        // 处理消息...
    }
}

四、日志与 Trace ID 关联

通过 MDC 将 Trace ID 注入日志:

java 复制代码
// juwatech.cn.common.logging.TraceIdFilter
@Component
public class TraceIdFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        String traceId = Span.current().getSpanContext().getTraceId();
        MDC.put("traceId", traceId);
        try {
            chain.doFilter(request, response);
        } finally {
            MDC.clear();
        }
    }
}

Logback 配置:

xml 复制代码
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [traceId=%X{traceId}] - %msg%n</pattern>
    </encoder>
</appender>

日志示例:

复制代码
14:23:01.123 [http-nio-8080-exec-5] INFO  juwatech.cn.rebate.service.RebateService [traceId=4a7b8c9d0e1f2a3b] - Calculating rebate for order O123456

五、自定义指标与告警

在关键路径上报业务指标:

java 复制代码
// juwatech.cn.rebate.service.RebateService
private static final Meter meter = GlobalOpenTelemetry.getMeter("rebate-meter");
private static final Counter rebateCounter = meter.counterBuilder("rebate.total")
    .setDescription("Total rebate amount")
    .setUnit("CNY")
    .build();

public void creditRebate(Long userId, BigDecimal amount) {
    rebateCounter.add(amount.doubleValue(), Attributes.of(
        AttributeKey.stringKey("status"), "success"
    ));
}

SkyWalking OAP 聚合指标后,在 UI 中配置告警规则:

  • 当 service_sla < 99.9% 持续5分钟 → 企业微信告警
  • 当 endpoint_avg_response_time > 2000ms → 邮件通知

六、部署架构

  • OAP Server:3节点集群,接收 Agent 数据,存储于 Elasticsearch
  • UI:提供拓扑图、Trace 查询、Metrics 展示
  • OpenTelemetry Collector(可选):统一接收 OTLP 协议数据,转发至 SkyWalking

服务启动时自动注册到 SkyWalking 拓扑图,清晰展示 order-service → rebate-service → account-service 调用链。

本文著作权归 微赚淘客系统3.0 研发团队,转载请注明出处!

相关推荐
波力海苔夹心脆6758 小时前
C# 值类型与引用类型详解:存储位置、赋值机制、参数传递、相等比较、装箱拆箱与常见陷阱
开发语言·jvm·经验分享·笔记·c#·.net
估值探索者8 小时前
【Python量化策略实战 #02】多因子合成mom和vol两因子打分合成
开发语言·c++·python·数据挖掘·c#
淡海水19 小时前
01-04-认知篇-Unity内存全景
unity·c#·游戏引擎·.net·gc
此时不提桶,更待何时19 小时前
06-09-A-Kafka架构与存储原理详解
架构·kafka·linq
淡海水1 天前
01-03-认知篇-C#内存模型深度解析
unity·c#·游戏引擎·.net·gc
lzhdim1 天前
C#不为人知的10个魔法特性:资深开发者也会震惊的底层奥秘
java·前端·javascript·算法·c#
前端 贾公子2 天前
LangChain核心组件 == 提示词(Prompts)
开发语言·c#
码艺-Alimjan2 天前
Web 网站打包桌面应用的另一种方式,超级简单(C# exe 33Kb)
开发语言·前端·c#
海天鹰2 天前
C#入门:显示窗口
c#
淡海水2 天前
16-02-C#常用数据结构源码-附录B-源码索引速查
unity·c#·游戏引擎·il2cpp