Dubbo RPCContext存储一些通用数据,这个用手动清除吗?

文章内容收录到个人网站,方便阅读 :hardyfish.top/

Dubbo 在以下情况下会自动清理 RpcContext:

同步 RPC 调用结束后:

  • RpcContext.getContext() 的数据会被当前线程回收,但如果是线程池模式,数据可能被污染。

服务端调用完成后:

  • 当 Dubbo 服务端 处理完请求后,会清理 RpcContext,防止数据泄漏到下一个请求。

消费者(Consumer)发起 RPC 调用时:

  • RpcContext 不会跨请求复用 ,下次新的 RpcContext.getContext() 仍然是新的实例。

✅ 示例(同步调用,Dubbo 自动清理)

java 复制代码
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 数据。

java 复制代码
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,导致数据污染。

java 复制代码
ExecutorService executorService = Executors.newFixedThreadPool(5);

executorService.submit(() -> {
    RpcContext.getContext().setAttachment("traceId", "abc123");
    demoService.sayHello("Dubbo");

    // 可能泄露到下一个任务
});

🔴 如果线程池中的线程未手动清理 RpcContext,后续任务可能复用 traceId,导致数据污染。

✅ 解决方案

  • 在任务结束时手动clearAttachments():

    java 复制代码
    try {
        RpcContext.getContext().setAttachment("traceId", "abc123");
        demoService.sayHello("Dubbo");
    } finally {
        RpcContext.getContext().clearAttachments();
    }
  • 使用 Dubbo InheritableThreadLocal(适用于 Dubbo 3.x)

    java 复制代码
    RpcContext.removeContext(true);

❌ 3. 在 Filter 或拦截器中传递 RpcContext

如果你在 Dubbo Filter 里存放了 RpcContext 的数据,可能不会自动清理,影响后续请求。

java 复制代码
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 是否会自动清理,养成手动清理的习惯,避免数据污染。

java 复制代码
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。

java 复制代码
RpcContext.removeContext(true);

✅ 适用于:

  • 线程池中使用 RpcContext
  • Dubbo 异步调用
  • 需要彻底清理 RpcContext

✅ 在 AOP 或 Filter 中全局清理

如果你的应用里 RpcContext 传递较多,可以在 AOP 或 Dubbo Filter 里统一清理 ,减少手动调用 clearAttachments() 的麻烦。

方式 1:Spring AOP 自动清理

java 复制代码
@Aspect
@Component
public class RpcContextCleanAspect {
    @AfterReturning("@annotation(org.apache.dubbo.config.annotation.Reference)")
    public void clearRpcContext() {
        RpcContext.getContext().clearAttachments();
    }
}

优点 :拦截所有 Dubbo 方法调用后自动清理 RpcContext。

方式 2:在 Dubbo Filter 里自动清理

java 复制代码
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 里配置:

xml 复制代码
<dubbo:provider filter="rpcContextCleanFilter"/>

优点 :全局自动清理 RpcContext,防止数据泄漏。

结论

情况 Dubbo 是否自动清理? 推荐解决方案
同步 RPC 调用 ✅ 是 默认安全,但建议 finally 里清理
异步调用(CompletableFuture) ❌ 否 手动 clearAttachments()
线程池复用(ExecutorService) ❌ 否 finally 里清理 RpcContext
Dubbo Filter 或 AOP ❌ 否 拦截器里清理 RpcContext
Dubbo 3.x 版本 ❌ 否 RpcContext.removeContext(true)

最佳实践

  1. 同步调用,建议 try-finally 里 clearAttachments()。
  2. 异步调用,必须 clearAttachments(),避免线程池数据污染。
  3. 线程池调用 Dubbo,使用 RpcContext.removeContext(true) 清理 ThreadLocal。
  4. 全局清理可以用 Dubbo Filter 或 Spring AOP 自动处理。

结论

Dubbo 默认会在请求完成后清理 RpcContext ,但异步调用和线程池不会自动清理。

所以要手动清理,或者在拦截器里全局清理,避免数据污染。

相关推荐
宸津-代码粉碎机6 小时前
OpenAI 连夜迎战 Grok Bot 和 Muse:AI 智能体从 “会聊天” 到 “能办事”,现在入场还来得及吗
java·大数据·人工智能·分布式·python
噢,我明白了7 小时前
java中唯一键和幂等键的应用
java·后端
程序猿乐锅7 小时前
【黑马点评 | 第八篇】Redisson分布式锁
java·数据库·spring boot·redis·分布式·spring·缓存
GreenTea8 小时前
我把 Agent 的 while 循环拆掉了:一种你可能没想到的 Agent 架构
前端·后端·架构
考虑考虑8 小时前
synchronized字符串常量
java·后端·java ee
长谷深风1118 小时前
Tool与Skill:AI能力设计的分水岭
java·人工智能·ai·大模型·aiagent
m0_587383008 小时前
广州24小时自助健身房解决方案实战指南与系统部署要点
java·spring·小程序·架构·需求分析
Escalating_xu8 小时前
【C 语言】深入理解指针(1·下):指针运算、野指针、assert 与传址实战
java·c语言·开发语言
周杰偷奶茶8 小时前
【Java】数据类型与变量
java·开发语言
code斗8 小时前
Java数据结构:堆详解
java·开发语言·数据结构