20260915:traceId日志链路追踪
This commit is contained in:
parent
8412b62996
commit
d7a524cba3
1213
docs/knowledge-base/07-TraceId 链路追踪实现.md
Normal file
1213
docs/knowledge-base/07-TraceId 链路追踪实现.md
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,7 @@
|
|||||||
package com.fastbee.common.config;
|
package com.fastbee.common.config;
|
||||||
|
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
|
import com.fastbee.common.trace.MdcTaskDecorator;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@ -60,7 +61,11 @@ public class DeviceTask {
|
|||||||
return builder(FastBeeConstant.TASK.FUNCTION_INVOKE_TASK);
|
return builder(FastBeeConstant.TASK.FUNCTION_INVOKE_TASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*内部消费线程*/
|
/**
|
||||||
|
* 内部消费线程
|
||||||
|
* 监听循环从消息恢复 TraceId 并逐条清理;
|
||||||
|
* TaskDecorator 用于让 @Async 消费方法继承监听线程的 TraceId
|
||||||
|
*/
|
||||||
@Bean(FastBeeConstant.TASK.MESSAGE_CONSUME_TASK)
|
@Bean(FastBeeConstant.TASK.MESSAGE_CONSUME_TASK)
|
||||||
public Executor messageConsumeTaskExecutor() {
|
public Executor messageConsumeTaskExecutor() {
|
||||||
return builder(FastBeeConstant.TASK.MESSAGE_CONSUME_TASK);
|
return builder(FastBeeConstant.TASK.MESSAGE_CONSUME_TASK);
|
||||||
@ -104,7 +109,7 @@ public class DeviceTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*组装线程池*/
|
/*组装线程池(带 TaskDecorator,用于跨线程传播 TraceId)*/
|
||||||
private ThreadPoolTaskExecutor builder(String threadNamePrefix) {
|
private ThreadPoolTaskExecutor builder(String threadNamePrefix) {
|
||||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
executor.setCorePoolSize(coreSize);
|
executor.setCorePoolSize(coreSize);
|
||||||
@ -115,6 +120,10 @@ public class DeviceTask {
|
|||||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
|
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
|
||||||
//线程池名的前缀
|
//线程池名的前缀
|
||||||
executor.setThreadNamePrefix(threadNamePrefix);
|
executor.setThreadNamePrefix(threadNamePrefix);
|
||||||
|
|
||||||
|
// 配置 TaskDecorator,自动传播 TraceId
|
||||||
|
executor.setTaskDecorator(new MdcTaskDecorator());
|
||||||
|
|
||||||
executor.initialize();
|
executor.initialize();
|
||||||
return executor;
|
return executor;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,8 @@ public interface FastBeeConstant {
|
|||||||
String MQTT = "mqtt";
|
String MQTT = "mqtt";
|
||||||
String PORT = "port";
|
String PORT = "port";
|
||||||
String ADAPTER = "adapter";
|
String ADAPTER = "adapter";
|
||||||
|
/** 链路追踪处理器名 */
|
||||||
|
String TRACE = "trace";
|
||||||
String FRAMEDECODER = "frameDecoder";
|
String FRAMEDECODER = "frameDecoder";
|
||||||
String DISPATCHER = "dispatcher";
|
String DISPATCHER = "dispatcher";
|
||||||
String DECODER = "decoder";
|
String DECODER = "decoder";
|
||||||
|
|||||||
@ -8,6 +8,9 @@ import lombok.Data;
|
|||||||
@Data
|
@Data
|
||||||
public class DeviceReplyBo {
|
public class DeviceReplyBo {
|
||||||
|
|
||||||
|
/** 链路追踪ID */
|
||||||
|
private String traceId;
|
||||||
|
|
||||||
/*设备下发消息id*/
|
/*设备下发消息id*/
|
||||||
private String messageId;
|
private String messageId;
|
||||||
/*标识符*/
|
/*标识符*/
|
||||||
|
|||||||
@ -24,6 +24,9 @@ import java.util.List;
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class DeviceReportBo {
|
public class DeviceReportBo {
|
||||||
|
|
||||||
|
/** 链路追踪ID */
|
||||||
|
private String traceId;
|
||||||
|
|
||||||
/*设备编号或IMEI号*/
|
/*设备编号或IMEI号*/
|
||||||
private String serialNumber;
|
private String serialNumber;
|
||||||
/*产品ID*/
|
/*产品ID*/
|
||||||
|
|||||||
@ -18,6 +18,10 @@ import java.util.Date;
|
|||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
public class DeviceStatusBo {
|
public class DeviceStatusBo {
|
||||||
|
|
||||||
|
/** 链路追踪ID */
|
||||||
|
private String traceId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备客户端id
|
* 设备客户端id
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -11,6 +11,9 @@ import java.util.List;
|
|||||||
@Data
|
@Data
|
||||||
public class DeviceTestReportBo {
|
public class DeviceTestReportBo {
|
||||||
|
|
||||||
|
/** 链路追踪ID */
|
||||||
|
private String traceId;
|
||||||
|
|
||||||
private Long productId;
|
private Long productId;
|
||||||
/**
|
/**
|
||||||
* 设备编号
|
* 设备编号
|
||||||
|
|||||||
@ -17,6 +17,9 @@ import java.util.Map;
|
|||||||
@Data
|
@Data
|
||||||
public class InvokeReqDto {
|
public class InvokeReqDto {
|
||||||
|
|
||||||
|
/** 链路追踪ID */
|
||||||
|
private String traceId;
|
||||||
|
|
||||||
@ApiModelProperty(value = "设备编号")
|
@ApiModelProperty(value = "设备编号")
|
||||||
private String serialNumber;
|
private String serialNumber;
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,9 @@ import lombok.NoArgsConstructor;
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class MQSendMessageBo {
|
public class MQSendMessageBo {
|
||||||
|
|
||||||
|
/** 链路追踪ID */
|
||||||
|
private String traceId;
|
||||||
|
|
||||||
public Boolean isShadow;
|
public Boolean isShadow;
|
||||||
public Long userId;
|
public Long userId;
|
||||||
public Long delay;
|
public Long delay;
|
||||||
|
|||||||
@ -17,6 +17,9 @@ import java.math.BigDecimal;
|
|||||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
public class OtaUpgradeBo {
|
public class OtaUpgradeBo {
|
||||||
|
|
||||||
|
/** 链路追踪ID */
|
||||||
|
private String traceId;
|
||||||
|
|
||||||
// 设备编码
|
// 设备编码
|
||||||
private String serialNumber;
|
private String serialNumber;
|
||||||
// 升级任务ID
|
// 升级任务ID
|
||||||
|
|||||||
@ -17,6 +17,11 @@ import java.util.concurrent.TimeUnit;
|
|||||||
@Data
|
@Data
|
||||||
public class OtaUpgradeDelayTask implements Delayed {
|
public class OtaUpgradeDelayTask implements Delayed {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 链路追踪ID
|
||||||
|
*/
|
||||||
|
private String traceId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 固件id
|
* 固件id
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -0,0 +1,75 @@
|
|||||||
|
package com.fastbee.common.trace;
|
||||||
|
|
||||||
|
import com.fastbee.common.trace.TraceContext.TraceSnapshot;
|
||||||
|
import org.springframework.core.task.TaskDecorator;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线程池 TraceId 传播装饰器
|
||||||
|
* 解决 @Async 异步任务 TraceId 丢失问题
|
||||||
|
*
|
||||||
|
* <p>使用方法:
|
||||||
|
* <pre>
|
||||||
|
* ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
* executor.setTaskDecorator(new MdcTaskDecorator());
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>工作原理:
|
||||||
|
* <ol>
|
||||||
|
* <li>在任务提交时,捕获当前线程的 TraceContext 快照</li>
|
||||||
|
* <li>在任务执行时,恢复快照到新线程(MDC 由 TraceContext 统一同步)</li>
|
||||||
|
* <li>在任务完成后,清理上下文防止线程池污染</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>说明:MDC 的写入/清理全部由 {@link TraceContext} 统一管理,
|
||||||
|
* 本装饰器只负责快照与恢复,避免 MDC 与 TraceContext 双重操作导致的不一致。
|
||||||
|
*
|
||||||
|
* @author FastBee
|
||||||
|
*/
|
||||||
|
public class MdcTaskDecorator implements TaskDecorator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Runnable decorate(Runnable runnable) {
|
||||||
|
// 捕获当前线程的 TraceContext 快照(跨线程传播的唯一依据)
|
||||||
|
TraceSnapshot snapshot = TraceContext.snapshot();
|
||||||
|
|
||||||
|
return () -> {
|
||||||
|
if (snapshot != null) {
|
||||||
|
TraceContext.restore(snapshot);
|
||||||
|
} else {
|
||||||
|
// 父线程无上下文,生成新的 TraceId
|
||||||
|
TraceContext.getOrCreateTraceId();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
runnable.run();
|
||||||
|
} finally {
|
||||||
|
// 清理上下文,防止线程池复用造成污染
|
||||||
|
TraceContext.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 装饰 Callable
|
||||||
|
*
|
||||||
|
* @param task Callable 任务
|
||||||
|
* @return 装饰后的 Callable
|
||||||
|
*/
|
||||||
|
public <T> Callable<T> decorate(Callable<T> task) {
|
||||||
|
TraceSnapshot snapshot = TraceContext.snapshot();
|
||||||
|
|
||||||
|
return () -> {
|
||||||
|
if (snapshot != null) {
|
||||||
|
TraceContext.restore(snapshot);
|
||||||
|
} else {
|
||||||
|
TraceContext.getOrCreateTraceId();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return task.call();
|
||||||
|
} finally {
|
||||||
|
TraceContext.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,270 @@
|
|||||||
|
package com.fastbee.common.trace;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.slf4j.MDC;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一追踪上下文
|
||||||
|
* 管理 traceId, tenantId, userId 等追踪信息
|
||||||
|
*
|
||||||
|
* <p>核心特性:
|
||||||
|
* <ul>
|
||||||
|
* <li>基于 ThreadLocal 实现线程隔离</li>
|
||||||
|
* <li>自动同步到 MDC 用于日志输出</li>
|
||||||
|
* <li>支持 snapshot/restore 用于跨线程传播</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>使用示例:
|
||||||
|
* <pre>
|
||||||
|
* // Web请求入口
|
||||||
|
* TraceContext.setTraceId(TraceIdGenerator.generate());
|
||||||
|
*
|
||||||
|
* // 异步任务(通过 TaskDecorator 自动传播)
|
||||||
|
* @Async
|
||||||
|
* public void asyncTask() {
|
||||||
|
* log.info("处理中"); // 日志自动包含 traceId
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // 手动传播(用于非 Spring 管理的线程池)
|
||||||
|
* TraceSnapshot snapshot = TraceContext.snapshot();
|
||||||
|
* executor.submit(() -> {
|
||||||
|
* TraceContext.restore(snapshot);
|
||||||
|
* try {
|
||||||
|
* // 业务逻辑
|
||||||
|
* } finally {
|
||||||
|
* TraceContext.clear();
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author FastBee
|
||||||
|
*/
|
||||||
|
public class TraceContext {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TraceId 在 MDC 中的 key
|
||||||
|
*/
|
||||||
|
public static final String TRACE_ID_KEY = "traceId";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TenantId 在 MDC 中的 key
|
||||||
|
* 预留字段,暂未使用
|
||||||
|
*/
|
||||||
|
public static final String TENANT_ID_KEY = "tenantId";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserId 在 MDC 中的 key
|
||||||
|
* 预留字段,暂未使用
|
||||||
|
*/
|
||||||
|
public static final String USER_ID_KEY = "userId";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线程本地上下文存储
|
||||||
|
*/
|
||||||
|
private static final ThreadLocal<TraceSnapshot> CONTEXT = new ThreadLocal<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 追踪上下文快照
|
||||||
|
* 用于跨线程传递
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class TraceSnapshot implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private String traceId;
|
||||||
|
private Long tenantId; // 预留字段
|
||||||
|
private Long userId; // 预留字段
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将快照应用到 MDC
|
||||||
|
* 先移除旧值再写入,保证 MDC 状态与快照精确一致(包括 null 字段)
|
||||||
|
*/
|
||||||
|
public void applyToMdc() {
|
||||||
|
MDC.remove(TRACE_ID_KEY);
|
||||||
|
MDC.remove(TENANT_ID_KEY);
|
||||||
|
MDC.remove(USER_ID_KEY);
|
||||||
|
if (traceId != null) {
|
||||||
|
MDC.put(TRACE_ID_KEY, traceId);
|
||||||
|
}
|
||||||
|
if (tenantId != null) {
|
||||||
|
MDC.put(TENANT_ID_KEY, String.valueOf(tenantId));
|
||||||
|
}
|
||||||
|
if (userId != null) {
|
||||||
|
MDC.put(USER_ID_KEY, String.valueOf(userId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================== Get Methods ==================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前 TraceId
|
||||||
|
*
|
||||||
|
* @return TraceId,如果不存在返回 null
|
||||||
|
*/
|
||||||
|
public static String getTraceId() {
|
||||||
|
TraceSnapshot snapshot = CONTEXT.get();
|
||||||
|
return snapshot != null ? snapshot.getTraceId() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前 TenantId(预留)
|
||||||
|
*
|
||||||
|
* @return TenantId,如果不存在返回 null
|
||||||
|
*/
|
||||||
|
public static Long getTenantId() {
|
||||||
|
TraceSnapshot snapshot = CONTEXT.get();
|
||||||
|
return snapshot != null ? snapshot.getTenantId() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前 UserId(预留)
|
||||||
|
*
|
||||||
|
* @return UserId,如果不存在返回 null
|
||||||
|
*/
|
||||||
|
public static Long getUserId() {
|
||||||
|
TraceSnapshot snapshot = CONTEXT.get();
|
||||||
|
return snapshot != null ? snapshot.getUserId() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================== Set Methods ==================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 TraceId
|
||||||
|
* 同时更新 ThreadLocal 和 MDC
|
||||||
|
*
|
||||||
|
* @param traceId TraceId
|
||||||
|
*/
|
||||||
|
public static void setTraceId(String traceId) {
|
||||||
|
TraceSnapshot snapshot = CONTEXT.get();
|
||||||
|
if (snapshot == null) {
|
||||||
|
snapshot = new TraceSnapshot();
|
||||||
|
CONTEXT.set(snapshot);
|
||||||
|
}
|
||||||
|
snapshot.setTraceId(traceId);
|
||||||
|
if (traceId != null) {
|
||||||
|
MDC.put(TRACE_ID_KEY, traceId);
|
||||||
|
} else {
|
||||||
|
MDC.remove(TRACE_ID_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 TenantId(预留)
|
||||||
|
* 同时更新 ThreadLocal 和 MDC
|
||||||
|
*
|
||||||
|
* @param tenantId TenantId
|
||||||
|
*/
|
||||||
|
public static void setTenantId(Long tenantId) {
|
||||||
|
TraceSnapshot snapshot = CONTEXT.get();
|
||||||
|
if (snapshot == null) {
|
||||||
|
snapshot = new TraceSnapshot();
|
||||||
|
CONTEXT.set(snapshot);
|
||||||
|
}
|
||||||
|
snapshot.setTenantId(tenantId);
|
||||||
|
if (tenantId != null) {
|
||||||
|
MDC.put(TENANT_ID_KEY, String.valueOf(tenantId));
|
||||||
|
} else {
|
||||||
|
MDC.remove(TENANT_ID_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 UserId(预留)
|
||||||
|
* 同时更新 ThreadLocal 和 MDC
|
||||||
|
*
|
||||||
|
* @param userId UserId
|
||||||
|
*/
|
||||||
|
public static void setUserId(Long userId) {
|
||||||
|
TraceSnapshot snapshot = CONTEXT.get();
|
||||||
|
if (snapshot == null) {
|
||||||
|
snapshot = new TraceSnapshot();
|
||||||
|
CONTEXT.set(snapshot);
|
||||||
|
}
|
||||||
|
snapshot.setUserId(userId);
|
||||||
|
if (userId != null) {
|
||||||
|
MDC.put(USER_ID_KEY, String.valueOf(userId));
|
||||||
|
} else {
|
||||||
|
MDC.remove(USER_ID_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================== Convenience Methods ==================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取或创建 TraceId
|
||||||
|
* 如果当前没有 TraceId,自动生成一个新的
|
||||||
|
*
|
||||||
|
* @return TraceId
|
||||||
|
*/
|
||||||
|
public static String getOrCreateTraceId() {
|
||||||
|
String traceId = getTraceId();
|
||||||
|
if (traceId == null || traceId.isEmpty()) {
|
||||||
|
traceId = TraceIdGenerator.generate();
|
||||||
|
setTraceId(traceId);
|
||||||
|
}
|
||||||
|
return traceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确保当前有 TraceId
|
||||||
|
* 如果传入的 traceId 不为空,使用传入的;否则生成新的
|
||||||
|
*
|
||||||
|
* @param traceId 可选的 TraceId
|
||||||
|
* @return 最终的 TraceId
|
||||||
|
*/
|
||||||
|
public static String ensureTraceId(String traceId) {
|
||||||
|
if (traceId != null && !traceId.isEmpty()) {
|
||||||
|
setTraceId(traceId);
|
||||||
|
return traceId;
|
||||||
|
}
|
||||||
|
return getOrCreateTraceId();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================== Snapshot & Restore ==================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建当前上下文的快照
|
||||||
|
* 用于跨线程传递
|
||||||
|
*
|
||||||
|
* @return TraceSnapshot 快照,如果当前无上下文返回 null
|
||||||
|
*/
|
||||||
|
public static TraceSnapshot snapshot() {
|
||||||
|
TraceSnapshot current = CONTEXT.get();
|
||||||
|
if (current == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
TraceSnapshot copy = new TraceSnapshot();
|
||||||
|
copy.setTraceId(current.getTraceId());
|
||||||
|
copy.setTenantId(current.getTenantId());
|
||||||
|
copy.setUserId(current.getUserId());
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从快照恢复上下文
|
||||||
|
*
|
||||||
|
* @param snapshot 快照,如果为 null 则清理上下文
|
||||||
|
*/
|
||||||
|
public static void restore(TraceSnapshot snapshot) {
|
||||||
|
if (snapshot == null) {
|
||||||
|
clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CONTEXT.set(snapshot);
|
||||||
|
snapshot.applyToMdc();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除当前上下文
|
||||||
|
* 同时清除 ThreadLocal 和 MDC
|
||||||
|
*/
|
||||||
|
public static void clear() {
|
||||||
|
CONTEXT.remove();
|
||||||
|
MDC.remove(TRACE_ID_KEY);
|
||||||
|
MDC.remove(TENANT_ID_KEY);
|
||||||
|
MDC.remove(USER_ID_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,106 @@
|
|||||||
|
package com.fastbee.common.trace;
|
||||||
|
|
||||||
|
import org.slf4j.MDC;
|
||||||
|
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TraceId 生成器
|
||||||
|
* 生成纯数字格式的 TraceId
|
||||||
|
*
|
||||||
|
* <p>格式:基于时间戳 + 序列号 + 随机数
|
||||||
|
* <p>示例:752431931275776594
|
||||||
|
*
|
||||||
|
* <p>特点:
|
||||||
|
* <ul>
|
||||||
|
* <li>纯数字格式,便于存储和索引</li>
|
||||||
|
* <li>时间有序,大致按生成时间排序</li>
|
||||||
|
* <li>线程安全,使用 synchronized 保证</li>
|
||||||
|
* <li>高并发友好,单机每秒可生成百万级 ID</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author FastBee
|
||||||
|
*/
|
||||||
|
public class TraceIdGenerator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 起始时间戳 (2023-01-01 00:00:00)
|
||||||
|
* 用于减少时间戳位数,延长 ID 可用年限
|
||||||
|
*/
|
||||||
|
private static final long START_TIMESTAMP = 1672531200000L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 序列号掩码:16 bit(0 ~ 65535)
|
||||||
|
*/
|
||||||
|
private static final long SEQUENCE_MASK = 0xFFFFL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 序列号,同一毫秒内递增
|
||||||
|
*/
|
||||||
|
private static final AtomicLong sequence = new AtomicLong(0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上次生成时间戳
|
||||||
|
*/
|
||||||
|
private static volatile long lastTimestamp = -1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成纯数字格式的 TraceId
|
||||||
|
*
|
||||||
|
* @return 纯数字 TraceId 字符串
|
||||||
|
*/
|
||||||
|
public static String generate() {
|
||||||
|
return String.valueOf(generateLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成 long 类型的 TraceId
|
||||||
|
* 使用 synchronized 保证线程安全
|
||||||
|
*
|
||||||
|
* <p>位域分配(低到高):
|
||||||
|
* <ul>
|
||||||
|
* <li>随机数 6 bit(第 0~5 位)</li>
|
||||||
|
* <li>序列号 16 bit(第 6~21 位)</li>
|
||||||
|
* <li>时间差 剩余位(第 22 位及以上)</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @return long 类型 TraceId
|
||||||
|
*/
|
||||||
|
public static synchronized long generateLong() {
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// 时钟回拨保护:时间不允许倒退,沿用上次时间戳
|
||||||
|
if (timestamp < lastTimestamp) {
|
||||||
|
timestamp = lastTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timestamp == lastTimestamp) {
|
||||||
|
// 同毫秒:序列号递增并回写(& MASK 防止溢出侵入时间位域)
|
||||||
|
sequence.set((sequence.get() + 1) & SEQUENCE_MASK);
|
||||||
|
if (sequence.get() == 0) {
|
||||||
|
// 序列号用尽,等待下一毫秒
|
||||||
|
timestamp = tilNextMillis(lastTimestamp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sequence.set(0);
|
||||||
|
}
|
||||||
|
lastTimestamp = timestamp;
|
||||||
|
|
||||||
|
// 时间差(高位) | 序列号(中位) | 随机数(低位 6 bit)
|
||||||
|
long timeDiff = timestamp - START_TIMESTAMP;
|
||||||
|
long random = ThreadLocalRandom.current().nextInt(64);
|
||||||
|
return (timeDiff << 22) | (sequence.get() << 6) | random;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 等待下一毫秒
|
||||||
|
*/
|
||||||
|
private static long tilNextMillis(long lastTimestamp) {
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
while (timestamp <= lastTimestamp) {
|
||||||
|
timestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,188 +1,64 @@
|
|||||||
package com.fastbee.common.utils;
|
package com.fastbee.common.utils;
|
||||||
|
|
||||||
import com.fastbee.common.utils.uuid.IdUtils;
|
import com.fastbee.common.trace.TraceContext;
|
||||||
import org.slf4j.MDC;
|
import com.fastbee.common.trace.TraceContext.TraceSnapshot;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 日志追踪ID工具类
|
* 日志追踪ID工具类
|
||||||
* 统一生成TraceId的逻辑,使用IdWorker和ThreadLocalRandom
|
* 提供向后兼容的 API,内部委托给 TraceContext
|
||||||
*
|
*
|
||||||
* @author Sumuxi
|
* @author Sumuxi
|
||||||
|
* @deprecated 建议直接使用 {@link TraceContext}
|
||||||
*/
|
*/
|
||||||
public class TraceIdUtils {
|
public class TraceIdUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TraceId在MDC中的key
|
* 包装 Runnable:透传 TraceContext(MDC 由 TraceContext 统一同步)
|
||||||
*/
|
* 用于非 Spring 管理的线程池(如 ScheduledExecutorService)
|
||||||
public static final String TRACE_ID_KEY = "traceId";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IdWorker实例(单例模式)
|
|
||||||
*/
|
|
||||||
private static volatile IdUtils idWorker;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取IdWorker实例(懒加载单例)
|
|
||||||
*
|
*
|
||||||
* @return IdWorker实例
|
* @param task Runnable 任务
|
||||||
*/
|
* @return 包装后的 Runnable
|
||||||
private static IdUtils getIdWorker() {
|
|
||||||
if (idWorker == null) {
|
|
||||||
synchronized (TraceIdUtils.class) {
|
|
||||||
if (idWorker == null) {
|
|
||||||
// 使用机器ID,可以根据实际情况配置
|
|
||||||
// 这里使用1作为默认workerId,可以通过配置文件读取
|
|
||||||
// IdUtils构造函数:workerId, datacenterId,-1表示随机生成
|
|
||||||
idWorker = new IdUtils(-1L, -1L);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return idWorker;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成TraceId
|
|
||||||
* 格式:IdWorker.nextId() * 1000 + ThreadLocalRandom.nextInt(1000)
|
|
||||||
* 参考:generateClueId() 的实现方式
|
|
||||||
* 使用Math.abs确保TraceId为正数
|
|
||||||
*
|
|
||||||
* @return TraceId
|
|
||||||
*/
|
|
||||||
public static long generateTraceId() {
|
|
||||||
long id = getIdWorker().nextId();
|
|
||||||
// 使用绝对值确保TraceId为正数,避免负数显示
|
|
||||||
// 注意:Math.abs(id) * 1000 可能会溢出,所以需要对最终结果再次使用Math.abs()
|
|
||||||
long traceId = Math.abs(id) * 1000 + ThreadLocalRandom.current().nextInt(1000);
|
|
||||||
// 确保最终结果为非负数(处理溢出情况)
|
|
||||||
traceId = Math.abs(traceId);
|
|
||||||
MDC.put(TRACE_ID_KEY, String.valueOf(traceId));
|
|
||||||
return traceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成TraceId并放入MDC
|
|
||||||
* 返回字符串格式的TraceId
|
|
||||||
*
|
|
||||||
* @return TraceId字符串
|
|
||||||
*/
|
|
||||||
public static String generateTraceIdString() {
|
|
||||||
long traceId = generateTraceId();
|
|
||||||
return String.valueOf(traceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置TraceId到MDC
|
|
||||||
*
|
|
||||||
* @param traceId TraceId
|
|
||||||
*/
|
|
||||||
public static void setTraceId(String traceId) {
|
|
||||||
if (traceId != null && !traceId.isEmpty()) {
|
|
||||||
MDC.put(TRACE_ID_KEY, traceId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置TraceId到MDC(long类型)
|
|
||||||
*
|
|
||||||
* @param traceId TraceId
|
|
||||||
*/
|
|
||||||
public static void setTraceId(long traceId) {
|
|
||||||
MDC.put(TRACE_ID_KEY, String.valueOf(traceId));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从MDC获取TraceId
|
|
||||||
*
|
|
||||||
* @return TraceId
|
|
||||||
*/
|
|
||||||
public static String getTraceId() {
|
|
||||||
return MDC.get(TRACE_ID_KEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 确保当前线程 MDC 中存在 TraceId。
|
|
||||||
* - 已存在:直接返回
|
|
||||||
* - 不存在:生成并写入 MDC
|
|
||||||
*/
|
|
||||||
public static String ensureTraceId() {
|
|
||||||
String traceId = getTraceId();
|
|
||||||
if (traceId == null || traceId.isEmpty()) {
|
|
||||||
traceId = generateTraceIdString();
|
|
||||||
}
|
|
||||||
return traceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用指定 traceId 覆盖当前线程 MDC(为空则生成)。
|
|
||||||
* 主要用于跨线程/跨进程透传后的落地。
|
|
||||||
*/
|
|
||||||
public static String ensureTraceId(String traceId) {
|
|
||||||
if (traceId == null || traceId.isEmpty()) {
|
|
||||||
return ensureTraceId();
|
|
||||||
}
|
|
||||||
setTraceId(traceId);
|
|
||||||
return traceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 包装 Runnable:透传提交线程的 MDC,上下文中无 traceId 则补齐;执行完自动清理/还原。
|
|
||||||
*/
|
*/
|
||||||
public static Runnable wrapWithMdc(Runnable task) {
|
public static Runnable wrapWithMdc(Runnable task) {
|
||||||
final Map<String, String> captured = MDC.getCopyOfContextMap();
|
final TraceSnapshot snapshot = TraceContext.snapshot();
|
||||||
|
|
||||||
return () -> {
|
return () -> {
|
||||||
Map<String, String> previous = MDC.getCopyOfContextMap();
|
if (snapshot != null) {
|
||||||
try {
|
TraceContext.restore(snapshot);
|
||||||
if (captured == null || captured.isEmpty()) {
|
|
||||||
MDC.clear();
|
|
||||||
} else {
|
} else {
|
||||||
MDC.setContextMap(captured);
|
TraceContext.getOrCreateTraceId();
|
||||||
}
|
}
|
||||||
ensureTraceId();
|
try {
|
||||||
task.run();
|
task.run();
|
||||||
} finally {
|
} finally {
|
||||||
if (previous == null || previous.isEmpty()) {
|
TraceContext.clear();
|
||||||
MDC.clear();
|
|
||||||
} else {
|
|
||||||
MDC.setContextMap(previous);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 包装 Callable:透传提交线程的 MDC,上下文中无 traceId 则补齐;执行完自动清理/还原。
|
* 包装 Callable:透传 TraceContext(MDC 由 TraceContext 统一同步)
|
||||||
|
* 用于非 Spring 管理的线程池(如 ScheduledExecutorService)
|
||||||
|
*
|
||||||
|
* @param task Callable 任务
|
||||||
|
* @return 包装后的 Callable
|
||||||
*/
|
*/
|
||||||
public static <T> Callable<T> wrapWithMdc(Callable<T> task) {
|
public static <T> Callable<T> wrapWithMdc(Callable<T> task) {
|
||||||
final Map<String, String> captured = MDC.getCopyOfContextMap();
|
final TraceSnapshot snapshot = TraceContext.snapshot();
|
||||||
|
|
||||||
return () -> {
|
return () -> {
|
||||||
Map<String, String> previous = MDC.getCopyOfContextMap();
|
if (snapshot != null) {
|
||||||
try {
|
TraceContext.restore(snapshot);
|
||||||
if (captured == null || captured.isEmpty()) {
|
|
||||||
MDC.clear();
|
|
||||||
} else {
|
} else {
|
||||||
MDC.setContextMap(captured);
|
TraceContext.getOrCreateTraceId();
|
||||||
}
|
}
|
||||||
ensureTraceId();
|
try {
|
||||||
return task.call();
|
return task.call();
|
||||||
} finally {
|
} finally {
|
||||||
if (previous == null || previous.isEmpty()) {
|
TraceContext.clear();
|
||||||
MDC.clear();
|
|
||||||
} else {
|
|
||||||
MDC.setContextMap(previous);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 清除MDC中的TraceId
|
|
||||||
*/
|
|
||||||
public static void clearTraceId() {
|
|
||||||
MDC.remove(TRACE_ID_KEY);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
package com.fastbee.framework.aspectj;
|
||||||
|
|
||||||
|
import com.fastbee.common.trace.TraceContext;
|
||||||
|
import com.fastbee.common.trace.TraceIdGenerator;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Scheduled 定时任务 TraceId 切面
|
||||||
|
* 自动为所有 Spring 定时任务方法生成 TraceId
|
||||||
|
*
|
||||||
|
* @author FastBee
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
@Order(1) // 高优先级,确保最先执行
|
||||||
|
public class ScheduledTraceAspect {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拦截所有 @Scheduled 方法
|
||||||
|
*/
|
||||||
|
@Around("@annotation(org.springframework.scheduling.annotation.Scheduled)")
|
||||||
|
public Object aroundScheduled(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||||
|
String methodName = joinPoint.getSignature().toShortString();
|
||||||
|
|
||||||
|
// 生成 TraceId
|
||||||
|
String traceId = TraceIdGenerator.generate();
|
||||||
|
TraceContext.setTraceId(traceId);
|
||||||
|
|
||||||
|
log.debug("定时任务开始执行: {}", methodName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return joinPoint.proceed();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
log.error("定时任务执行异常: {}", methodName, e);
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
log.debug("定时任务执行完成: {}", methodName);
|
||||||
|
TraceContext.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package com.fastbee.framework.config;
|
package com.fastbee.framework.config;
|
||||||
|
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
|
import com.fastbee.common.trace.MdcTaskDecorator;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
@ -30,6 +31,10 @@ public class OtaThreadPoolConfig {
|
|||||||
executor.setThreadNamePrefix(threadNamePrefix);
|
executor.setThreadNamePrefix(threadNamePrefix);
|
||||||
// 线程池对拒绝任务(无线程可用)的处理策略
|
// 线程池对拒绝任务(无线程可用)的处理策略
|
||||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||||
|
|
||||||
|
// 新增:配置 TaskDecorator,自动传播 TraceId
|
||||||
|
executor.setTaskDecorator(new MdcTaskDecorator());
|
||||||
|
|
||||||
// 进行加载
|
// 进行加载
|
||||||
executor.initialize();
|
executor.initialize();
|
||||||
return executor;
|
return executor;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.fastbee.framework.config;
|
package com.fastbee.framework.config;
|
||||||
|
|
||||||
|
import com.fastbee.common.trace.MdcTaskDecorator;
|
||||||
import com.fastbee.common.utils.Threads;
|
import com.fastbee.common.utils.Threads;
|
||||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@ -38,6 +39,10 @@ public class ThreadPoolConfig {
|
|||||||
executor.setKeepAliveSeconds(keepAliveSeconds);
|
executor.setKeepAliveSeconds(keepAliveSeconds);
|
||||||
// 线程池对拒绝任务(无线程可用)的处理策略
|
// 线程池对拒绝任务(无线程可用)的处理策略
|
||||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||||
|
|
||||||
|
// 新增:配置 TaskDecorator,自动传播 TraceId
|
||||||
|
executor.setTaskDecorator(new MdcTaskDecorator());
|
||||||
|
|
||||||
return executor;
|
return executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
package com.fastbee.framework.interceptor;
|
package com.fastbee.framework.interceptor;
|
||||||
|
|
||||||
import com.fastbee.common.utils.TraceIdUtils;
|
import com.fastbee.common.trace.TraceContext;
|
||||||
|
import com.fastbee.common.trace.TraceIdGenerator;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
@ -16,24 +16,26 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
@Component
|
@Component
|
||||||
public class TraceIdInterceptor implements HandlerInterceptor {
|
public class TraceIdInterceptor implements HandlerInterceptor {
|
||||||
|
|
||||||
|
private static final String TRACE_ID_HEADER = "X-Trace-Id";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||||
// 获取请求URI
|
// 获取请求URI
|
||||||
String requestURI = request.getRequestURI();
|
String requestURI = request.getRequestURI();
|
||||||
|
|
||||||
// 尝试从请求头中获取TraceId(支持分布式追踪)
|
// 尝试从请求头中获取TraceId(支持分布式追踪)
|
||||||
String traceId = request.getHeader("X-Trace-Id");
|
String traceId = request.getHeader(TRACE_ID_HEADER);
|
||||||
|
|
||||||
// 如果请求头中没有TraceId,则生成一个新的
|
// 如果请求头中没有TraceId,则生成一个新的
|
||||||
if (traceId == null || traceId.isEmpty()) {
|
if (traceId == null || traceId.isEmpty()) {
|
||||||
traceId = TraceIdUtils.generateTraceIdString();
|
traceId = TraceIdGenerator.generate();
|
||||||
} else {
|
|
||||||
// 如果请求头中有TraceId,设置到MDC
|
|
||||||
TraceIdUtils.setTraceId(traceId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置到 TraceContext(自动同步到 MDC)
|
||||||
|
TraceContext.setTraceId(traceId);
|
||||||
|
|
||||||
// 将TraceId放入响应头,方便前端获取
|
// 将TraceId放入响应头,方便前端获取
|
||||||
response.setHeader("X-Trace-Id", traceId);
|
response.setHeader(TRACE_ID_HEADER, traceId);
|
||||||
|
|
||||||
// 打印请求路径日志
|
// 打印请求路径日志
|
||||||
org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TraceIdInterceptor.class);
|
org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TraceIdInterceptor.class);
|
||||||
@ -44,8 +46,8 @@ public class TraceIdInterceptor implements HandlerInterceptor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
|
||||||
// 请求完成后清除MDC中的TraceId,避免内存泄漏
|
// 请求完成后清除 TraceContext,避免内存泄漏
|
||||||
TraceIdUtils.clearTraceId();
|
TraceContext.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,10 @@ package com.fastbee.iot.data.job;
|
|||||||
|
|
||||||
import com.fastbee.common.constant.Constants;
|
import com.fastbee.common.constant.Constants;
|
||||||
import com.fastbee.common.constant.ScheduleConstants;
|
import com.fastbee.common.constant.ScheduleConstants;
|
||||||
|
import com.fastbee.common.trace.TraceContext;
|
||||||
|
import com.fastbee.common.trace.TraceIdGenerator;
|
||||||
import com.fastbee.common.utils.ExceptionUtil;
|
import com.fastbee.common.utils.ExceptionUtil;
|
||||||
import com.fastbee.common.utils.StringUtils;
|
import com.fastbee.common.utils.StringUtils;
|
||||||
import com.fastbee.common.utils.TraceIdUtils;
|
|
||||||
import com.fastbee.common.utils.bean.BeanUtils;
|
import com.fastbee.common.utils.bean.BeanUtils;
|
||||||
import com.fastbee.common.utils.spring.SpringUtils;
|
import com.fastbee.common.utils.spring.SpringUtils;
|
||||||
import com.fastbee.iot.domain.DeviceJob;
|
import com.fastbee.iot.domain.DeviceJob;
|
||||||
@ -56,8 +57,8 @@ public abstract class DeviceAbstractQuartzJob implements Job {
|
|||||||
protected void before(JobExecutionContext context, DeviceJob deviceJob) {
|
protected void before(JobExecutionContext context, DeviceJob deviceJob) {
|
||||||
threadLocal.set(new Date());
|
threadLocal.set(new Date());
|
||||||
|
|
||||||
// 为设备定时任务生成TraceId,使用统一的生成逻辑
|
// 为设备定时任务生成 TraceId
|
||||||
TraceIdUtils.generateTraceId();
|
TraceContext.setTraceId(TraceIdGenerator.generate());
|
||||||
|
|
||||||
log.info("设备定时任务开始执行: {} - {}", deviceJob.getJobName(), deviceJob.getJobGroup());
|
log.info("设备定时任务开始执行: {} - {}", deviceJob.getJobName(), deviceJob.getJobGroup());
|
||||||
}
|
}
|
||||||
@ -106,10 +107,10 @@ public abstract class DeviceAbstractQuartzJob implements Job {
|
|||||||
// 写入数据库当中
|
// 写入数据库当中
|
||||||
SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
|
SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
|
||||||
|
|
||||||
// 清除TraceId,避免内存泄漏
|
|
||||||
TraceIdUtils.clearTraceId();
|
|
||||||
|
|
||||||
log.info("设备定时任务执行完成: {} - {}, 耗时: {}ms", deviceJob.getJobName(), deviceJob.getJobGroup(), runMs);
|
log.info("设备定时任务执行完成: {} - {}, 耗时: {}ms", deviceJob.getJobName(), deviceJob.getJobGroup(), runMs);
|
||||||
|
|
||||||
|
// 清除 TraceContext
|
||||||
|
TraceContext.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.fastbee.iot.data.listen;
|
|||||||
|
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
import com.fastbee.common.core.mq.DeviceReportBo;
|
import com.fastbee.common.core.mq.DeviceReportBo;
|
||||||
|
import com.fastbee.common.trace.TraceContext;
|
||||||
import com.fastbee.iot.data.consumer.DeviceOtherMsgConsumer;
|
import com.fastbee.iot.data.consumer.DeviceOtherMsgConsumer;
|
||||||
import com.fastbee.mq.queue.DeviceOtherQueue;
|
import com.fastbee.mq.queue.DeviceOtherQueue;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -26,7 +27,15 @@ public class DeviceOtherListen {
|
|||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
DeviceReportBo reportBo = DeviceOtherQueue.take();
|
DeviceReportBo reportBo = DeviceOtherQueue.take();
|
||||||
|
|
||||||
|
// 恢复 TraceId
|
||||||
|
TraceContext.ensureTraceId(reportBo.getTraceId());
|
||||||
|
|
||||||
|
try {
|
||||||
otherMsgConsumer.consume(reportBo);
|
otherMsgConsumer.consume(reportBo);
|
||||||
|
} finally {
|
||||||
|
TraceContext.clear();
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("=>emq数据转发异常");
|
log.error("=>emq数据转发异常");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.fastbee.iot.data.listen;
|
package com.fastbee.iot.data.listen;
|
||||||
|
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
|
import com.fastbee.common.trace.TraceContext;
|
||||||
import com.fastbee.iot.data.consumer.DevicePropFetchConsumer;
|
import com.fastbee.iot.data.consumer.DevicePropFetchConsumer;
|
||||||
import com.fastbee.iot.model.modbus.ModbusPollJob;
|
import com.fastbee.iot.model.modbus.ModbusPollJob;
|
||||||
import com.fastbee.mq.queue.DevicePropFetchQueue;
|
import com.fastbee.mq.queue.DevicePropFetchQueue;
|
||||||
@ -27,9 +28,20 @@ public class DevicePropFetchListen {
|
|||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
ModbusPollJob take = DevicePropFetchQueue.take();
|
ModbusPollJob take = DevicePropFetchQueue.take();
|
||||||
|
|
||||||
|
// 恢复 TraceId
|
||||||
|
TraceContext.ensureTraceId(take.getTraceId());
|
||||||
|
|
||||||
|
try {
|
||||||
devicePropFetchConsumer.consume(take);
|
devicePropFetchConsumer.consume(take);
|
||||||
|
} finally {
|
||||||
|
TraceContext.clear();
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("=>设备属性获取异常", e);
|
log.error("=>设备属性获取异常", e);
|
||||||
|
} finally {
|
||||||
|
// 确保任何情况下都清理
|
||||||
|
TraceContext.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.fastbee.iot.data.listen;
|
|||||||
|
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
import com.fastbee.common.core.mq.DeviceReportBo;
|
import com.fastbee.common.core.mq.DeviceReportBo;
|
||||||
|
import com.fastbee.common.trace.TraceContext;
|
||||||
import com.fastbee.iot.data.consumer.DeviceReplyMsgConsumer;
|
import com.fastbee.iot.data.consumer.DeviceReplyMsgConsumer;
|
||||||
import com.fastbee.mq.queue.DeviceReplyQueue;
|
import com.fastbee.mq.queue.DeviceReplyQueue;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -27,8 +28,16 @@ public class DeviceReplyListen {
|
|||||||
try {
|
try {
|
||||||
/*读队列消息*/
|
/*读队列消息*/
|
||||||
DeviceReportBo reportBo = DeviceReplyQueue.take();
|
DeviceReportBo reportBo = DeviceReplyQueue.take();
|
||||||
|
|
||||||
|
// 恢复 TraceId
|
||||||
|
TraceContext.ensureTraceId(reportBo.getTraceId());
|
||||||
|
|
||||||
|
try {
|
||||||
/*处理消息*/
|
/*处理消息*/
|
||||||
deviceReplyMsgHandler.consume(reportBo);
|
deviceReplyMsgHandler.consume(reportBo);
|
||||||
|
} finally {
|
||||||
|
TraceContext.clear();
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("=>设备回调消息监听异常", e);
|
log.error("=>设备回调消息监听异常", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.fastbee.iot.data.listen;
|
|||||||
|
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
import com.fastbee.common.core.mq.DeviceReportBo;
|
import com.fastbee.common.core.mq.DeviceReportBo;
|
||||||
|
import com.fastbee.common.trace.TraceContext;
|
||||||
import com.fastbee.iot.data.consumer.DeviceReportMsgConsumer;
|
import com.fastbee.iot.data.consumer.DeviceReportMsgConsumer;
|
||||||
import com.fastbee.mq.queue.DeviceReportQueue;
|
import com.fastbee.mq.queue.DeviceReportQueue;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -27,10 +28,21 @@ public class DeviceReportListen {
|
|||||||
try {
|
try {
|
||||||
/*取出数据*/
|
/*取出数据*/
|
||||||
DeviceReportBo reportBo = DeviceReportQueue.take();
|
DeviceReportBo reportBo = DeviceReportQueue.take();
|
||||||
|
|
||||||
|
// 恢复 TraceId
|
||||||
|
TraceContext.ensureTraceId(reportBo.getTraceId());
|
||||||
|
|
||||||
|
try {
|
||||||
/*处理数据*/
|
/*处理数据*/
|
||||||
reportMsgConsumer.consume(reportBo);
|
reportMsgConsumer.consume(reportBo);
|
||||||
|
} finally {
|
||||||
|
TraceContext.clear();
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("=>设备上报数据监听异常", e);
|
log.error("=>设备上报数据监听异常", e);
|
||||||
|
} finally {
|
||||||
|
// 确保任何情况下都清理上下文
|
||||||
|
TraceContext.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.fastbee.iot.data.listen;
|
|||||||
|
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
import com.fastbee.common.core.mq.DeviceStatusBo;
|
import com.fastbee.common.core.mq.DeviceStatusBo;
|
||||||
|
import com.fastbee.common.trace.TraceContext;
|
||||||
import com.fastbee.iot.data.consumer.DeviceStatusConsumer;
|
import com.fastbee.iot.data.consumer.DeviceStatusConsumer;
|
||||||
import com.fastbee.mq.queue.DeviceStatusQueue;
|
import com.fastbee.mq.queue.DeviceStatusQueue;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -26,10 +27,20 @@ public class DeviceStatusListen {
|
|||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
DeviceStatusBo status = DeviceStatusQueue.take();
|
DeviceStatusBo status = DeviceStatusQueue.take();
|
||||||
|
|
||||||
|
// 恢复 TraceId
|
||||||
|
TraceContext.ensureTraceId(status.getTraceId());
|
||||||
|
|
||||||
|
try {
|
||||||
deviceStatusConsumer.consume(status);
|
deviceStatusConsumer.consume(status);
|
||||||
|
} finally {
|
||||||
|
TraceContext.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("设备状态监听错误", e);
|
log.error("设备状态监听错误", e);
|
||||||
|
} finally {
|
||||||
|
TraceContext.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.fastbee.iot.data.listen;
|
|||||||
|
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
import com.fastbee.common.core.mq.DeviceTestReportBo;
|
import com.fastbee.common.core.mq.DeviceTestReportBo;
|
||||||
|
import com.fastbee.common.trace.TraceContext;
|
||||||
import com.fastbee.iot.data.consumer.DeviceTestConsumer;
|
import com.fastbee.iot.data.consumer.DeviceTestConsumer;
|
||||||
import com.fastbee.mq.queue.DeviceTestQueue;
|
import com.fastbee.mq.queue.DeviceTestQueue;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -26,7 +27,14 @@ public class DeviceTestListen {
|
|||||||
try {
|
try {
|
||||||
DeviceTestReportBo take = DeviceTestQueue.take();
|
DeviceTestReportBo take = DeviceTestQueue.take();
|
||||||
|
|
||||||
|
// 恢复 TraceId
|
||||||
|
TraceContext.ensureTraceId(take.getTraceId());
|
||||||
|
|
||||||
|
try {
|
||||||
deviceTestConsumer.consume(take);
|
deviceTestConsumer.consume(take);
|
||||||
|
} finally {
|
||||||
|
TraceContext.clear();
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("=>emq数据转发异常");
|
log.error("=>emq数据转发异常");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.fastbee.iot.data.listen;
|
|||||||
|
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||||
|
import com.fastbee.common.trace.TraceContext;
|
||||||
import com.fastbee.iot.data.consumer.FunctionInvokeConsumer;
|
import com.fastbee.iot.data.consumer.FunctionInvokeConsumer;
|
||||||
import com.fastbee.mq.queue.FunctionInvokeQueue;
|
import com.fastbee.mq.queue.FunctionInvokeQueue;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -26,7 +27,15 @@ public class FunctionInvokeListen {
|
|||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
MQSendMessageBo sendBo = FunctionInvokeQueue.take();
|
MQSendMessageBo sendBo = FunctionInvokeQueue.take();
|
||||||
|
|
||||||
|
// 恢复 TraceId
|
||||||
|
TraceContext.ensureTraceId(sendBo.getTraceId());
|
||||||
|
|
||||||
|
try {
|
||||||
functionInvokeConsumer.handler(sendBo);
|
functionInvokeConsumer.handler(sendBo);
|
||||||
|
} finally {
|
||||||
|
TraceContext.clear();
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("=>下发服务消费异常", e);
|
log.error("=>下发服务消费异常", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ package com.fastbee.iot.data.listen;
|
|||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
import com.fastbee.common.core.domain.entity.SysMenu;
|
import com.fastbee.common.core.domain.entity.SysMenu;
|
||||||
import com.fastbee.common.core.mq.ota.OtaUpgradeDelayTask;
|
import com.fastbee.common.core.mq.ota.OtaUpgradeDelayTask;
|
||||||
|
import com.fastbee.common.trace.TraceContext;
|
||||||
import com.fastbee.common.utils.StringUtils;
|
import com.fastbee.common.utils.StringUtils;
|
||||||
import com.fastbee.iot.data.service.IOtaUpgradeService;
|
import com.fastbee.iot.data.service.IOtaUpgradeService;
|
||||||
import com.fastbee.mq.queue.DelayUpgradeQueue;
|
import com.fastbee.mq.queue.DelayUpgradeQueue;
|
||||||
@ -48,12 +49,22 @@ public class UpGradeListener implements ApplicationRunner {
|
|||||||
OtaUpgradeDelayTask task = DelayUpgradeQueue.task();
|
OtaUpgradeDelayTask task = DelayUpgradeQueue.task();
|
||||||
if (StringUtils.isNotNull(task)) {
|
if (StringUtils.isNotNull(task)) {
|
||||||
Date startTime = task.getStartTime();
|
Date startTime = task.getStartTime();
|
||||||
|
|
||||||
|
// 恢复 TraceId
|
||||||
|
TraceContext.ensureTraceId(task.getTraceId());
|
||||||
|
|
||||||
|
try {
|
||||||
otaUpgradeService.upgrade(task);
|
otaUpgradeService.upgrade(task);
|
||||||
log.info("=>开始OTA升级时间{}", startTime);
|
log.info("=>开始OTA升级时间{}", startTime);
|
||||||
|
} finally {
|
||||||
|
TraceContext.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("=>OTA升级监听异常", e);
|
log.error("=>OTA升级监听异常", e);
|
||||||
|
} finally {
|
||||||
|
TraceContext.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.fastbee.iot.data.listen;
|
|||||||
|
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
import com.fastbee.common.core.mq.ota.OtaUpgradeBo;
|
import com.fastbee.common.core.mq.ota.OtaUpgradeBo;
|
||||||
|
import com.fastbee.common.trace.TraceContext;
|
||||||
import com.fastbee.iot.data.service.IMqttMessagePublish;
|
import com.fastbee.iot.data.service.IMqttMessagePublish;
|
||||||
import com.fastbee.mq.queue.OtaUpgradeQueue;
|
import com.fastbee.mq.queue.OtaUpgradeQueue;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -28,10 +29,20 @@ public class UpgradeListen {
|
|||||||
try {
|
try {
|
||||||
/*获取队列中的OTA升级消息*/
|
/*获取队列中的OTA升级消息*/
|
||||||
OtaUpgradeBo upgradeBo = OtaUpgradeQueue.take();
|
OtaUpgradeBo upgradeBo = OtaUpgradeQueue.take();
|
||||||
|
|
||||||
|
// 恢复 TraceId
|
||||||
|
TraceContext.ensureTraceId(upgradeBo.getTraceId());
|
||||||
|
|
||||||
|
try {
|
||||||
// OTA升级处理
|
// OTA升级处理
|
||||||
functionSendService.upGradeOTA(upgradeBo);
|
functionSendService.upGradeOTA(upgradeBo);
|
||||||
|
} finally {
|
||||||
|
TraceContext.clear();
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("->OTA消息监听异常", e);
|
log.error("->OTA消息监听异常", e);
|
||||||
|
} finally {
|
||||||
|
TraceContext.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|||||||
import com.fastbee.common.core.mq.ota.OtaUpgradeDelayTask;
|
import com.fastbee.common.core.mq.ota.OtaUpgradeDelayTask;
|
||||||
import com.fastbee.common.enums.OTAUpgrade;
|
import com.fastbee.common.enums.OTAUpgrade;
|
||||||
import com.fastbee.common.exception.ServiceException;
|
import com.fastbee.common.exception.ServiceException;
|
||||||
|
import com.fastbee.common.trace.TraceContext;
|
||||||
import com.fastbee.common.utils.DateUtils;
|
import com.fastbee.common.utils.DateUtils;
|
||||||
import com.fastbee.common.utils.MessageUtils;
|
import com.fastbee.common.utils.MessageUtils;
|
||||||
import com.fastbee.common.utils.StringUtils;
|
import com.fastbee.common.utils.StringUtils;
|
||||||
@ -140,7 +141,9 @@ public class FirmwareTaskServiceImpl extends ServiceImpl<FirmwareTaskMapper, Fir
|
|||||||
if (DateUtils.getNowDate().after(task.getStartTime())) {
|
if (DateUtils.getNowDate().after(task.getStartTime())) {
|
||||||
throw new ServiceException(MessageUtils.message("firmware.task.upgrade.failed.time.not.valid"));
|
throw new ServiceException(MessageUtils.message("firmware.task.upgrade.failed.time.not.valid"));
|
||||||
}
|
}
|
||||||
//预定升级的任务放到 延迟任务队列
|
// 预定升级的任务放到 延迟任务队列
|
||||||
|
// 记录当前 TraceId,供延迟任务执行时恢复
|
||||||
|
task.setTraceId(TraceContext.getOrCreateTraceId());
|
||||||
DelayUpgradeQueue.offerTask(task);
|
DelayUpgradeQueue.offerTask(task);
|
||||||
FirmwareTask updatePo = new FirmwareTask();
|
FirmwareTask updatePo = new FirmwareTask();
|
||||||
updatePo.setId(task.getTaskId());
|
updatePo.setId(task.getTaskId());
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import com.fastbee.common.core.mq.DeviceStatusBo;
|
|||||||
import com.fastbee.common.core.mq.DeviceTestReportBo;
|
import com.fastbee.common.core.mq.DeviceTestReportBo;
|
||||||
import com.fastbee.common.core.mq.MQSendMessageBo;
|
import com.fastbee.common.core.mq.MQSendMessageBo;
|
||||||
import com.fastbee.common.core.mq.ota.OtaUpgradeBo;
|
import com.fastbee.common.core.mq.ota.OtaUpgradeBo;
|
||||||
|
import com.fastbee.common.trace.TraceContext;
|
||||||
import com.fastbee.iot.model.modbus.ModbusPollJob;
|
import com.fastbee.iot.model.modbus.ModbusPollJob;
|
||||||
import com.fastbee.mq.queue.*;
|
import com.fastbee.mq.queue.*;
|
||||||
|
|
||||||
@ -15,26 +16,55 @@ import com.fastbee.mq.queue.*;
|
|||||||
*/
|
*/
|
||||||
public class MessageProducer {
|
public class MessageProducer {
|
||||||
|
|
||||||
|
private MessageProducer() {
|
||||||
|
// 工具类,禁止实例化
|
||||||
|
}
|
||||||
|
|
||||||
/*发送设备获取属性消息到队列*/
|
/*发送设备获取属性消息到队列*/
|
||||||
public static void sendPropFetch(ModbusPollJob deviceJob) {
|
public static void sendPropFetch(ModbusPollJob deviceJob) {
|
||||||
|
if (deviceJob == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 设置 TraceId
|
||||||
|
deviceJob.setTraceId(TraceContext.getOrCreateTraceId());
|
||||||
DevicePropFetchQueue.offer(deviceJob);
|
DevicePropFetchQueue.offer(deviceJob);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*发送设备服务下发消息到队列*/
|
/*发送设备服务下发消息到队列*/
|
||||||
public static void sendFunctionInvoke(MQSendMessageBo bo) {
|
public static void sendFunctionInvoke(MQSendMessageBo bo) {
|
||||||
|
if (bo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 设置 TraceId
|
||||||
|
bo.setTraceId(TraceContext.getOrCreateTraceId());
|
||||||
FunctionInvokeQueue.offer(bo);
|
FunctionInvokeQueue.offer(bo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*发送设备上报消息到队列*/
|
/*发送设备上报消息到队列*/
|
||||||
public static void sendPublishMsg(DeviceReportBo bo) {
|
public static void sendPublishMsg(DeviceReportBo bo) {
|
||||||
|
if (bo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 设置 TraceId
|
||||||
|
bo.setTraceId(TraceContext.getOrCreateTraceId());
|
||||||
DeviceReportQueue.offer(bo);
|
DeviceReportQueue.offer(bo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void sendOtherMsg(DeviceReportBo bo) {
|
public static void sendOtherMsg(DeviceReportBo bo) {
|
||||||
|
if (bo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 设置 TraceId
|
||||||
|
bo.setTraceId(TraceContext.getOrCreateTraceId());
|
||||||
DeviceOtherQueue.offer(bo);
|
DeviceOtherQueue.offer(bo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void sendStatusMsg(DeviceStatusBo bo) {
|
public static void sendStatusMsg(DeviceStatusBo bo) {
|
||||||
|
if (bo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 设置 TraceId
|
||||||
|
bo.setTraceId(TraceContext.getOrCreateTraceId());
|
||||||
DeviceStatusQueue.offer(bo);
|
DeviceStatusQueue.offer(bo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,16 +74,31 @@ public class MessageProducer {
|
|||||||
* @param bo
|
* @param bo
|
||||||
*/
|
*/
|
||||||
public static void sendDeviceTestMsg(DeviceTestReportBo bo) {
|
public static void sendDeviceTestMsg(DeviceTestReportBo bo) {
|
||||||
|
if (bo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 设置 TraceId
|
||||||
|
bo.setTraceId(TraceContext.getOrCreateTraceId());
|
||||||
DeviceTestQueue.offer(bo);
|
DeviceTestQueue.offer(bo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*发送OTA消息到队列*/
|
/*发送OTA消息到队列*/
|
||||||
public static void sendOtaUpgradeMsg(OtaUpgradeBo bo) {
|
public static void sendOtaUpgradeMsg(OtaUpgradeBo bo) {
|
||||||
|
if (bo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 设置 TraceId
|
||||||
|
bo.setTraceId(TraceContext.getOrCreateTraceId());
|
||||||
OtaUpgradeQueue.offer(bo);
|
OtaUpgradeQueue.offer(bo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*发送设备回复消息到队列*/
|
/*发送设备回复消息到队列*/
|
||||||
public static void sendDeviceReplyMsg(DeviceReportBo bo) {
|
public static void sendDeviceReplyMsg(DeviceReportBo bo) {
|
||||||
|
if (bo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 设置 TraceId
|
||||||
|
bo.setTraceId(TraceContext.getOrCreateTraceId());
|
||||||
DeviceReplyQueue.offer(bo);
|
DeviceReplyQueue.offer(bo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,10 @@ package com.fastbee.quartz.util;
|
|||||||
|
|
||||||
import com.fastbee.common.constant.Constants;
|
import com.fastbee.common.constant.Constants;
|
||||||
import com.fastbee.common.constant.ScheduleConstants;
|
import com.fastbee.common.constant.ScheduleConstants;
|
||||||
|
import com.fastbee.common.trace.TraceContext;
|
||||||
|
import com.fastbee.common.trace.TraceIdGenerator;
|
||||||
import com.fastbee.common.utils.ExceptionUtil;
|
import com.fastbee.common.utils.ExceptionUtil;
|
||||||
import com.fastbee.common.utils.StringUtils;
|
import com.fastbee.common.utils.StringUtils;
|
||||||
import com.fastbee.common.utils.TraceIdUtils;
|
|
||||||
import com.fastbee.common.utils.bean.BeanUtils;
|
import com.fastbee.common.utils.bean.BeanUtils;
|
||||||
import com.fastbee.common.utils.spring.SpringUtils;
|
import com.fastbee.common.utils.spring.SpringUtils;
|
||||||
import com.fastbee.quartz.domain.SysJob;
|
import com.fastbee.quartz.domain.SysJob;
|
||||||
@ -56,8 +57,8 @@ public abstract class AbstractQuartzJob implements Job {
|
|||||||
protected void before(JobExecutionContext context, SysJob sysJob) {
|
protected void before(JobExecutionContext context, SysJob sysJob) {
|
||||||
threadLocal.set(new Date());
|
threadLocal.set(new Date());
|
||||||
|
|
||||||
// 为定时任务生成TraceId,使用统一的生成逻辑
|
// 为定时任务生成 TraceId
|
||||||
TraceIdUtils.generateTraceId();
|
TraceContext.setTraceId(TraceIdGenerator.generate());
|
||||||
|
|
||||||
log.info("定时任务开始执行: {} - {}", sysJob.getJobName(), sysJob.getJobGroup());
|
log.info("定时任务开始执行: {} - {}", sysJob.getJobName(), sysJob.getJobGroup());
|
||||||
}
|
}
|
||||||
@ -91,8 +92,8 @@ public abstract class AbstractQuartzJob implements Job {
|
|||||||
// 写入数据库当中
|
// 写入数据库当中
|
||||||
SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
|
SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
|
||||||
|
|
||||||
// 清除TraceId,避免内存泄漏
|
// 清除 TraceContext
|
||||||
TraceIdUtils.clearTraceId();
|
TraceContext.clear();
|
||||||
|
|
||||||
log.info("定时任务执行完成: {} - {}, 耗时: {}ms", sysJob.getJobName(), sysJob.getJobGroup(), runMs);
|
log.info("定时任务执行完成: {} - {}, 耗时: {}ms", sysJob.getJobName(), sysJob.getJobGroup(), runMs);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.fastbee.record.config;
|
package com.fastbee.record.config;
|
||||||
|
|
||||||
|
import com.fastbee.common.trace.MdcTaskDecorator;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.scheduling.annotation.EnableAsync;
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
@ -52,6 +53,10 @@ public class ThreadPoolTaskConfig {
|
|||||||
// 线程池对拒绝任务的处理策略
|
// 线程池对拒绝任务的处理策略
|
||||||
// CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
|
// CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
|
||||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||||
|
|
||||||
|
// 新增:配置 TaskDecorator,自动传播 TraceId
|
||||||
|
executor.setTaskDecorator(new MdcTaskDecorator());
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
executor.initialize();
|
executor.initialize();
|
||||||
return executor;
|
return executor;
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
package com.fastbee.base.core.hanler;
|
||||||
|
|
||||||
|
import com.fastbee.common.trace.TraceContext;
|
||||||
|
import com.fastbee.common.trace.TraceIdGenerator;
|
||||||
|
import io.netty.channel.ChannelHandler;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Netty 链路追踪处理器
|
||||||
|
*
|
||||||
|
* <p>放置在协议 pipeline 的最前端(编解码器之前),为每条入站报文生成独立的 TraceId,
|
||||||
|
* 使解码、分发、业务处理全链路的日志都携带同一个 TraceId。
|
||||||
|
*
|
||||||
|
* <p>注意:Netty EventLoop 线程会被多连接/多报文复用,
|
||||||
|
* 因此必须在处理结束后清理,否则会造成 TraceId 串线。
|
||||||
|
*
|
||||||
|
* @author FastBee
|
||||||
|
*/
|
||||||
|
@ChannelHandler.Sharable
|
||||||
|
public class TraceHandler extends ChannelInboundHandlerAdapter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 共享实例(无状态,可跨 Channel 复用)
|
||||||
|
*/
|
||||||
|
public static final TraceHandler INSTANCE = new TraceHandler();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||||
|
// 每条设备报文都是独立业务事件,生成新的 TraceId
|
||||||
|
TraceContext.setTraceId(TraceIdGenerator.generate());
|
||||||
|
try {
|
||||||
|
ctx.fireChannelRead(msg);
|
||||||
|
} finally {
|
||||||
|
TraceContext.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package com.fastbee.server;
|
package com.fastbee.server;
|
||||||
|
|
||||||
|
import com.fastbee.base.core.hanler.TraceHandler;
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
import com.fastbee.server.config.NettyConfig;
|
import com.fastbee.server.config.NettyConfig;
|
||||||
import com.fastbee.server.handler.*;
|
import com.fastbee.server.handler.*;
|
||||||
@ -61,9 +62,11 @@ public class TCPServer extends Server {
|
|||||||
@Override
|
@Override
|
||||||
protected void initChannel(NioSocketChannel channel) throws Exception {
|
protected void initChannel(NioSocketChannel channel) throws Exception {
|
||||||
channel.pipeline()
|
channel.pipeline()
|
||||||
|
// 第一步、链路追踪(必须位于编解码器之前,让解码日志也带 TraceId)
|
||||||
|
.addLast(FastBeeConstant.SERVER.TRACE, TraceHandler.INSTANCE)
|
||||||
// 设置心跳时间
|
// 设置心跳时间
|
||||||
.addLast(new IdleStateHandler(config.readerIdleTime, config.writerIdleTime, config.allIdleTime))
|
.addLast(new IdleStateHandler(config.readerIdleTime, config.writerIdleTime, config.allIdleTime))
|
||||||
// 第一步、添加粘包处理器
|
// 第二步、添加粘包处理器
|
||||||
.addLast(FastBeeConstant.SERVER.FRAMEDECODER, new ProtocolSniffingDecoder(config.maxFrameLength))
|
.addLast(FastBeeConstant.SERVER.FRAMEDECODER, new ProtocolSniffingDecoder(config.maxFrameLength))
|
||||||
// 第二步、添加消息适配器
|
// 第二步、添加消息适配器
|
||||||
.addLast(FastBeeConstant.SERVER.ADAPTER, adapter)
|
.addLast(FastBeeConstant.SERVER.ADAPTER, adapter)
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.fastbee.server;
|
package com.fastbee.server;
|
||||||
|
|
||||||
|
import com.fastbee.base.core.hanler.TraceHandler;
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
import com.fastbee.server.config.NettyConfig;
|
import com.fastbee.server.config.NettyConfig;
|
||||||
import com.fastbee.server.handler.DispatcherHandler;
|
import com.fastbee.server.handler.DispatcherHandler;
|
||||||
@ -52,6 +53,8 @@ public class UDPServer extends Server {
|
|||||||
@Override
|
@Override
|
||||||
protected void initChannel(NioDatagramChannel channel) throws Exception {
|
protected void initChannel(NioDatagramChannel channel) throws Exception {
|
||||||
channel.pipeline()
|
channel.pipeline()
|
||||||
|
// 链路追踪(位于编解码器之前,让解码日志也带 TraceId)
|
||||||
|
.addLast(FastBeeConstant.SERVER.TRACE, TraceHandler.INSTANCE)
|
||||||
.addLast(FastBeeConstant.SERVER.ADAPTER, adapter)
|
.addLast(FastBeeConstant.SERVER.ADAPTER, adapter)
|
||||||
.addLast(FastBeeConstant.SERVER.DECODER, decoder)
|
.addLast(FastBeeConstant.SERVER.DECODER, decoder)
|
||||||
.addLast(FastBeeConstant.SERVER.ENCODER, encoder)
|
.addLast(FastBeeConstant.SERVER.ENCODER, encoder)
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import com.fastbee.common.core.mq.DeviceTestReportBo;
|
|||||||
import com.fastbee.common.core.protocol.Message;
|
import com.fastbee.common.core.protocol.Message;
|
||||||
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
|
||||||
import com.fastbee.common.enums.ServerType;
|
import com.fastbee.common.enums.ServerType;
|
||||||
|
import com.fastbee.common.trace.TraceContext;
|
||||||
import com.fastbee.common.utils.DateUtils;
|
import com.fastbee.common.utils.DateUtils;
|
||||||
import com.fastbee.mq.producer.MessageProducer;
|
import com.fastbee.mq.producer.MessageProducer;
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
@ -61,6 +62,7 @@ public class DispatcherHandler extends ChannelInboundHandlerAdapter {
|
|||||||
s.increment();
|
s.increment();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*TraceId 由 pipeline 头部的 TraceHandler 统一生成与清理*/
|
||||||
Packet packet = (Packet) msg;
|
Packet packet = (Packet) msg;
|
||||||
Message request = packet.message;
|
Message request = packet.message;
|
||||||
// 判断是否注册
|
// 判断是否注册
|
||||||
@ -127,7 +129,16 @@ public class DispatcherHandler extends ChannelInboundHandlerAdapter {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (handler.async) {
|
if (handler.async) {
|
||||||
executor.execute(() -> channelRead0(ctx, packet, handler));
|
/*异步执行:手动透传 TraceId(业务线程池非Spring管理,无 TaskDecorator)*/
|
||||||
|
final String traceId = TraceContext.getTraceId();
|
||||||
|
executor.execute(() -> {
|
||||||
|
TraceContext.ensureTraceId(traceId);
|
||||||
|
try {
|
||||||
|
channelRead0(ctx, packet, handler);
|
||||||
|
} finally {
|
||||||
|
TraceContext.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
channelRead0(ctx, packet, handler);
|
channelRead0(ctx, packet, handler);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,6 +55,8 @@ public class MqttMessageDelegate {
|
|||||||
log.error("=>客户端未连接");
|
log.error("=>客户端未连接");
|
||||||
throw new ServiceException("客户端未连接");
|
throw new ServiceException("客户端未连接");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*TraceId 由 pipeline 头部的 TraceHandler 统一生成与清理*/
|
||||||
Optional.of(processor.get(messageType))
|
Optional.of(processor.get(messageType))
|
||||||
.ifPresent(mqttHandler -> mqttHandler.handler(ctx, message));
|
.ifPresent(mqttHandler -> mqttHandler.handler(ctx, message));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.fastbee.mqtt.server;
|
package com.fastbee.mqtt.server;
|
||||||
|
|
||||||
|
import com.fastbee.base.core.hanler.TraceHandler;
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
import com.fastbee.mqtt.handler.adapter.MqttMessageAdapter;
|
import com.fastbee.mqtt.handler.adapter.MqttMessageAdapter;
|
||||||
import com.fastbee.server.Server;
|
import com.fastbee.server.Server;
|
||||||
@ -54,6 +55,8 @@ public class MqttServer extends Server {
|
|||||||
channel.pipeline()
|
channel.pipeline()
|
||||||
.addFirst(FastBeeConstant.SERVER.IDLE
|
.addFirst(FastBeeConstant.SERVER.IDLE
|
||||||
, new IdleStateHandler(config.readerIdleTime, config.writerIdleTime, config.allIdleTime, TimeUnit.SECONDS))
|
, new IdleStateHandler(config.readerIdleTime, config.writerIdleTime, config.allIdleTime, TimeUnit.SECONDS))
|
||||||
|
// 链路追踪(位于编解码器之前,让解码日志也带 TraceId)
|
||||||
|
.addFirst(FastBeeConstant.SERVER.TRACE, TraceHandler.INSTANCE)
|
||||||
.addLast(FastBeeConstant.SERVER.DECODER, new MqttDecoder(1024 * 1024 * 2))
|
.addLast(FastBeeConstant.SERVER.DECODER, new MqttDecoder(1024 * 1024 * 2))
|
||||||
.addLast(FastBeeConstant.SERVER.ENCODER, MqttEncoder.INSTANCE)
|
.addLast(FastBeeConstant.SERVER.ENCODER, MqttEncoder.INSTANCE)
|
||||||
.addLast(messageAdapter);
|
.addLast(messageAdapter);
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.fastbee.mqtt.server;
|
package com.fastbee.mqtt.server;
|
||||||
|
|
||||||
|
import com.fastbee.base.core.hanler.TraceHandler;
|
||||||
import com.fastbee.common.constant.FastBeeConstant;
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
import com.fastbee.mqtt.codec.WebSocketMqttCodec;
|
import com.fastbee.mqtt.codec.WebSocketMqttCodec;
|
||||||
import com.fastbee.mqtt.handler.adapter.MqttMessageAdapter;
|
import com.fastbee.mqtt.handler.adapter.MqttMessageAdapter;
|
||||||
@ -49,6 +50,8 @@ public class WebSocketServer extends Server {
|
|||||||
ch.pipeline()
|
ch.pipeline()
|
||||||
.addFirst(FastBeeConstant.WS.HEART_BEAT
|
.addFirst(FastBeeConstant.WS.HEART_BEAT
|
||||||
, new IdleStateHandler(0, 0, 70))
|
, new IdleStateHandler(0, 0, 70))
|
||||||
|
/*链路追踪(位于编解码器之前,让解码日志也带 TraceId)*/
|
||||||
|
.addFirst(FastBeeConstant.SERVER.TRACE, TraceHandler.INSTANCE)
|
||||||
/*http请求响应*/
|
/*http请求响应*/
|
||||||
.addLast(FastBeeConstant.WS.HTTP_SERVER_CODEC, new HttpServerCodec())
|
.addLast(FastBeeConstant.WS.HTTP_SERVER_CODEC, new HttpServerCodec())
|
||||||
/*聚合header与body组成完整的Http请求,最大数据量为1Mb*/
|
/*聚合header与body组成完整的Http请求,最大数据量为1Mb*/
|
||||||
|
|||||||
@ -11,6 +11,9 @@ import lombok.Data;
|
|||||||
@Data
|
@Data
|
||||||
public class ModbusPollJob {
|
public class ModbusPollJob {
|
||||||
|
|
||||||
|
/** 链路追踪ID */
|
||||||
|
private String traceId;
|
||||||
|
|
||||||
private ModbusPollMsg pollMsg;
|
private ModbusPollMsg pollMsg;
|
||||||
|
|
||||||
private DeviceJob deviceJob;
|
private DeviceJob deviceJob;
|
||||||
|
|||||||
@ -19,7 +19,7 @@ INSERT INTO `sys_job` (
|
|||||||
) VALUES (
|
) VALUES (
|
||||||
'日志清理任务', -- 任务名称
|
'日志清理任务', -- 任务名称
|
||||||
'SYSTEM', -- 任务组名
|
'SYSTEM', -- 任务组名
|
||||||
'logCleanTask.cleanLogs()', -- 调用目标字符串
|
'logCleanTask.cleanLogs(30)', -- 调用目标字符串
|
||||||
'0 0 0 * * ?', -- Cron 表达式:每天凌晨 00:00:00
|
'0 0 0 * * ?', -- Cron 表达式:每天凌晨 00:00:00
|
||||||
'3', -- 错误策略:不触发立即执行
|
'3', -- 错误策略:不触发立即执行
|
||||||
'1', -- 禁止并发执行
|
'1', -- 禁止并发执行
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user