Dubbo 在以下情况下会自动清理 RpcContext:
同步 RPC 调用结束后:
RpcContext.getContext() 的数据会被当前线程回收,但如果是线程池模式,数据可能被污染。
服务端调用完成后:
当 Dubbo 服务端 处理完请求后,会清理 RpcContext,防止数据泄漏到下一个请求。
消费者(Consumer)发起 RPC 调用时:
RpcContext 不会跨请求复用,下次新的 RpcContext.getContext() 仍然是新的实例。
✅ 示例(同步调用,Dubbo 自动清理)
public void testRpcContext() {
RpcContext.getContext().setAttachment("traceId", "abc123");
// Dubbo 远程调用
demoService.sayHello("Dubbo");
// RPC 结束后,traceId 会被清除
System.out.println(RpcContext.getContext().getAttachment("traceId")); // null
}
结果:
traceId 在 RPC 调用后被清空,Dubbo 自动清理。
Dubbo 什么时候不会自动清理 RpcContext?
虽然 Dubbo 在请求完成后会清理 RpcContext,但某些情况下 RpcContext 可能不会被正确清除,需要手动清理。
❌ 1. Dubbo 异步调用
Dubbo 异步调用不会自动清理 RpcContext。
因为线程池可能会复用 ThreadLocal 的数据,导致下一个请求仍然携带上一个请求的 RpcContext 数据。
public void asyncCall() {
RpcContext.getContext().setAttachment("traceId", "abc123");
CompletableFuture<String> future = demoService.sayHelloAsync("Dubbo");
future.whenComplete((result, ex) -> {
System.out.println("异步返回:" + result);
System.out.println("traceId:" + RpcContext.getContext().getAttachment("traceId")); // ❌ 可能还存在
});
// 手动清理
RpcContext.getContext().clearAttachments();
}
🔴 如果不手动清理,异步线程可能复用上一个请求的 RpcContext 数据!
❌ 2. 使用线程池(如 ExecutorService)
如果你的应用使用线程池(如 ExecutorService)处理 Dubbo 请求,线程池不会自动清理 RpcContext,导致数据污染。
ExecutorService executorService = Executors.newFixedThreadPool(5);
executorService.submit(() -> {
RpcContext.getContext().setAttachment("traceId", "abc123");
demoService.sayHello("Dubbo");
// 可能泄露到下一个任务
});
🔴 如果线程池中的线程未手动清理 RpcContext,后续任务可能复用 traceId,导致数据污染。
✅ 解决方案
- 在任务结束时手动clearAttachments():
try {
RpcContext.getContext().setAttachment("traceId", "abc123");
demoService.sayHello("Dubbo");
} finally {
RpcContext.getContext().clearAttachments();
}
- 使用 Dubbo InheritableThreadLocal(适用于 Dubbo 3.x)
RpcContext.removeContext(true);
❌ 3. 在 Filter 或拦截器中传递 RpcContext
如果你在 Dubbo Filter 里存放了 RpcContext 的数据,可能不会自动清理,影响后续请求。
public class CustomFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
RpcContext.getContext().setAttachment("traceId", UUID.randomUUID().toString());
try {
return invoker.invoke(invocation);
} finally {
// 手动清理
RpcContext.getContext().clearAttachments();
}
}
}
✅ 确保在 finally 里清理 RpcContext,防止数据污染。
解决方案:如何正确清理 RpcContext?
✅ 手动 clearAttachments()
无论 Dubbo 是否会自动清理,养成手动清理的习惯,避免数据污染。
try {
RpcContext.getContext().setAttachment("traceId", "abc123");
demoService.sayHello("Dubbo");
} finally {
RpcContext.getContext().clearAttachments();
}
✅ 使用 RpcContext.removeContext(true)(Dubbo 3.x 推荐)
Dubbo 3.x 提供了 removeContext(true),可以完全清理当前线程 RpcContext。
RpcContext.removeContext(true);
✅ 适用于:
- 线程池中使用 RpcContext
- Dubbo 异步调用
- 需要彻底清理 RpcContext
✅ 在 AOP 或 Filter 中全局清理
如果你的应用里 RpcContext 传递较多,可以在 AOP 或 Dubbo Filter 里统一清理,减少手动调用 clearAttachments() 的麻烦。
方式 1:Spring AOP 自动清理
@Aspect
@Component
public class RpcContextCleanAspect {
@AfterReturning("@annotation(org.apache.dubbo.config.annotation.Reference)")
public void clearRpcContext() {
RpcContext.getContext().clearAttachments();
}
}
优点:拦截所有 Dubbo 方法调用后自动清理 RpcContext。
方式 2:在 Dubbo Filter 里自动清理
public class RpcContextCleanFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
try {
return invoker.invoke(invocation);
} finally {
// 确保 RPC 调用结束后清理
RpcContext.getContext().clearAttachments();
}
}
}
然后在 dubbo.xml 或 dubbo.properties 里配置:
<dubbo:provider filter="rpcContextCleanFilter"/>
优点:全局自动清理 RpcContext,防止数据泄漏。
结论
|
情况 |
Dubbo 是否自动清理? |
推荐解决方案 |
|
同步 RPC 调用 |
✅ 是 |
默认安全,但建议 finally 里清理 |
|
异步调用(CompletableFuture) |
❌ 否 |
手动 clearAttachments() |
|
线程池复用(ExecutorService) |
❌ 否 |
finally 里清理 RpcContext |
|
Dubbo Filter 或 AOP |
❌ 否 |
拦截器里清理 RpcContext |
|
Dubbo 3.x 版本 |
❌ 否 |
RpcContext.removeContext(true) |
最佳实践
- 同步调用,建议 try-finally 里 clearAttachments()。
- 异步调用,必须 clearAttachments(),避免线程池数据污染。
- 线程池调用 Dubbo,使用 RpcContext.removeContext(true) 清理 ThreadLocal。
- 全局清理可以用 Dubbo Filter 或 Spring AOP 自动处理。
结论
Dubbo 默认会在请求完成后清理 RpcContext,但异步调用和线程池不会自动清理。
所以要手动清理,或者在拦截器里全局清理,避免数据污染。

被折叠的 条评论
为什么被折叠?



