基于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
相关产品推荐
相关产品推荐

