You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Camel与Quarkus,如何无需JMX用Vega绘制调用流程序列图?

解决方案:基于Camel + Quarkus的无JMX调用链路跟踪与Vega可视化

一、无需JMX的步骤记录方案

1. 利用Camel事件监听器(EventNotifier)

Camel原生提供EventNotifier接口,可监听路由执行的关键事件,完全无需依赖JMX。在Quarkus中通过CDI注入即可实现全局跟踪:

import org.apache.camel.CamelContext;
import org.apache.camel.management.event.ExchangeCompletedEvent;
import org.apache.camel.management.event.ExchangeSentEvent;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.time.Instant;

@ApplicationScoped
public class RouteStepTracker {
    @Inject
    CamelContext camelContext;
    @Inject
    StepCache stepCache;

    void init(@Observes CamelContext context) {
        context.getManagementStrategy().addEventNotifier(new EventNotifier() {
            @Override
            public void notify(EventObject event) throws Exception {
                if (event instanceof ExchangeCompletedEvent completedEvent) {
                    trackRouteStep(completedEvent.getExchange());
                } else if (event instanceof ExchangeSentEvent sentEvent) {
                    trackExternalCall(sentEvent.getExchange());
                }
            }

            @Override
            public boolean isEnabled(EventObject event) {
                return event instanceof ExchangeCompletedEvent || event instanceof ExchangeSentEvent;
            }
        });
    }

    private void trackRouteStep(Exchange exchange) {
        String businessId = exchange.getIn().getHeader("X-Business-ID", String.class);
        if (businessId == null) return;
        String routeId = exchange.getFromRouteId();
        stepCache.addStep(businessId, new Step(routeId, StepType.ROUTE, Instant.now()));
    }

    private void trackExternalCall(Exchange exchange) {
        String businessId = exchange.getIn().getHeader("X-Business-ID", String.class);
        if (businessId == null) return;
        String endpointUri = exchange.getProperty(Exchange.SEND_ENDPOINT, String.class);
        stepCache.addStep(businessId, new Step(endpointUri, StepType.EXTERNAL_CALL, Instant.now()));
    }
}

2. 路由拦截器(Interceptor)实现细粒度跟踪

如果需要针对特定路由或节点做精准记录,可使用Camel的拦截器机制:

import org.apache.camel.builder.RouteBuilder;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.time.Instant;

@ApplicationScoped
public class TrackingRouteBuilder extends RouteBuilder {
    @Inject
    StepCache stepCache;

    @Override
    public void configure() throws Exception {
        // 全局拦截路由进入事件
        interceptFrom().process(exchange -> {
            String businessId = exchange.getIn().getHeader("X-Business-ID", String.class);
            if (businessId != null) {
                String routeId = exchange.getFromRouteId();
                stepCache.addStep(businessId, new Step(routeId, StepType.ROUTE_ENTRY, Instant.now()));
            }
        });

        // 拦截所有HTTP外部调用
        interceptSendToEndpoint("http:*").process(exchange -> {
            String businessId = exchange.getIn().getHeader("X-Business-ID", String.class);
            if (businessId != null) {
                String targetUrl = exchange.getIn().getHeader(Exchange.HTTP_URI, String.class);
                stepCache.addStep(businessId, new Step(targetUrl, StepType.EXTERNAL_CALL, Instant.now()));
            }
        });
    }
}

二、5分钟有效期缓存实现

基于Quarkus集成的Caffeine缓存实现TTL控制,无需额外文件存储:

import jakarta.enterprise.context.ApplicationScoped;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

@ApplicationScoped
public class StepCache {
    private final Cache<String, List<Step>> stepCache;

    public StepCache() {
        this.stepCache = Caffeine.newBuilder()
                .expireAfterWrite(Duration.ofMinutes(5))
                .build();
    }

    public void addStep(String businessId, Step step) {
        stepCache.asMap().computeIfAbsent(businessId, k -> new ArrayList<>()).add(step);
    }

    public List<Step> getSteps(String businessId) {
        return stepCache.getIfPresent(businessId);
    }
}

// 步骤数据载体
record Step(String identifier, StepType type, Instant timestamp) {}
enum StepType { ROUTE, EXTERNAL_CALL, ROUTE_ENTRY }

三、Vega序列图生成

1. 暴露跟踪数据的REST端点

创建Quarkus REST接口,根据业务ID返回对应链路数据:

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import java.util.List;

@Path("/api/trace")
public class TraceResource {
    @Inject
    StepCache stepCache;

    @GET
    public List<Step> getTrace(@QueryParam("businessId") String businessId) {
        return stepCache.getSteps(businessId);
    }
}

2. Vega序列图配置示例

将接口返回的步骤数据转换为可视化序列图,以下是基础配置(可嵌入前端页面):

{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "title": "业务ID调用链路序列图",
  "data": {
    "url": "/api/trace?businessId=YOUR_TARGET_ID",
    "format": {"type": "json"}
  },
  "mark": {"type": "line", "point": true},
  "encoding": {
    "x": {
      "field": "timestamp",
      "type": "temporal",
      "title": "执行时间"
    },
    "y": {
      "field": "identifier",
      "type": "nominal",
      "title": "节点名称",
      "sort": null
    },
    "color": {
      "field": "type",
      "type": "nominal",
      "title": "节点类型",
      "scale": {
        "domain": ["ROUTE", "EXTERNAL_CALL"],
        "range": ["#27ae60", "#3498db"]
      }
    },
    "tooltip": [
      {"field": "identifier", "title": "节点"},
      {"field": "type", "title": "类型"},
      {"field": "timestamp", "title": "时间"}
    ]
  }
}

若需要更贴近传统序列图的连线样式,可扩展Vega配置,使用link标记绘制节点间的关联关系。


内容的提问来源于stack exchange,提问作者Martel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 05:53:14