不能直接用 System.currentTimeMillis() 生成 TraceId,因其缺乏唯一性、节点标识和调用上下文,无法满足分布式链路追踪要求;应使用UUID、Snowflake变种或时间+随机数+实例ID组合等方式在网关层生成并透传标准TraceId。

不能直接用 System.currentTimeMillis() 生成 TraceId —— 它只是毫秒时间戳,不具备唯一性、可追溯性、跨进程传递能力,也不满足分布式链路追踪的基本要求(如全局唯一、低冲突、可排序、可解析)。
为什么 currentTimeMillis 不适合做 TraceId
核心问题有三个:
- 同一毫秒内并发请求会生成完全相同的值,TraceId 冲突导致链路混乱
- 没有服务实例标识,无法区分是哪个节点产生的日志或调用
- 缺少 spanId、parentId 等上下文信息,无法构建调用树结构
网关层生成 TraceId 的正确做法
应在网关收到请求的第一时间生成符合规范的 TraceId(例如 W3C Trace Context 或 SkyWalking/Sleuth 格式),并注入到请求头中向下游透传。常见方案如下:
-
使用 UUID:简单可靠,如
UUID.randomUUID().toString().replace("-", ""),128 位足够全局唯一,但不可排序、不可读 - Snowflake 变种:结合时间戳(毫秒级)、机器 ID、序列号,保证时序性和唯一性;需注意网关多实例部署时机器 ID 要隔离
-
基于时间的紧凑 ID(推荐):例如
System.currentTimeMillis() + 6 位随机数 + 2 位实例编号,长度可控(如 20 位字符串),兼顾可读性、唯一性和轻量性
在 Spring Cloud Gateway 中注入 TraceId 示例
以 GlobalFilter 方式实现:
public class TraceIdGlobalFilter implements GlobalFilter {
private final String instanceId = "gw-01"; // 可从配置或注册中心获取
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String traceId = generateTraceId();
ServerHttpRequest request = exchange.getRequest()
.mutate()
.header("trace-id", traceId)
.header("X-B3-TraceId", traceId) // 兼容 Zipkin 格式
.build();
return chain.filter(exchange.mutate().request(request).build());
}
private String generateTraceId() {
long ts = System.currentTimeMillis();
int rand = ThreadLocalRandom.current().nextInt(100000, 999999);
return String.format("%d%06d%s", ts, rand, instanceId);
}
}
注意:该 ID 需同步写入日志 MDC(如 MDC.put("traceId", traceId)),确保业务日志自动携带。
与主流链路系统对接的关键点
若已接入 SkyWalking、Zipkin 或 OpenTelemetry:
- 不要覆盖其自动生成的
trace-id/X-B3-TraceId头,优先复用已有上下文;仅当无上游 TraceId 时才新建 - 务必同时设置
span-id和parent-id(对网关首跳,parent-id 为空) - 推荐使用标准库(如
io.opentelemetry.api.trace.Tracer)生成,而非手拼字符串,避免格式不兼容

















