当前位置: 首页 > news >正文

Dubbo RPC 调用中用户上下文传递问题的解决

2025.10.14 正在将在医院管理系统项目重构成微服务架构。之前业务逻辑直接在 Web 层调用,没有经过 Dubbo。最近重构后,统一改为 Dubbo RPC调用。重构时有注意到model上下文传递问题,但是这个tokenService藏在反射处理里没发现。。

10.14二编:有藏得很深的来了 放了3个切面注解放在mapper上层的RepositoryImpl文件里

文章目录

    • 一、架构和修改原则
    • 二、问题
    • 三、分析
      • 3.1 代码分析
      • 3.2 根本原因
    • 四、解决方案
      • 4.1 设计思路
      • 4.2 技术实现
    • 4.3 技术要点
    • 五、解决的
    • 六、二编,问题升级
      • 6.1 解决方案
    • 七 流程

一、架构和修改原则

重构使用Spring Cloud + Dubbo 混合架构
Web 层(dovip-hospital-3b-web):处理 HTTP 请求,负责用户交互
Business 层(dovip-hospital-3b-business):Dubbo Provider,提供业务逻辑服务

设计原则
遵循"开闭原则",对扩展开放,对修改关闭
提供降级方案,保证系统的健壮性

二、问题

在开发考试权限管理功能时,遇到了一个严重的生产问题:
症状:
用户登录后访问考试管理页面时,系统抛出 NullPointerException
错误堆栈显示:at com.xz.auth.core.TokenService.getLoginUser(TokenService.java:70)
该问题只在 Dubbo RPC 调用时出现,本地直接调用时正常
错误日志:

三、分析

3.1 代码分析

首先查看了 TokenService 的实现:
关键发现: TokenService 依赖 ServletUtils.getRequest() 获取 HttpServletRequest,然后从 request 的 attribute 中获取登录用户信息。

3.2 根本原因

Web 层的用户认证流程:
ArgumentResolverInterceptor 拦截器从 token 获取用户信息
将 LoginUser 对象设置到 request.setAttribute(“loginUser”, loginUser)
TokenService 从 request.getAttribute(“loginUser”) 获取用户信息

Dubbo RPC 调用的问题:
Web 层的 HttpServletRequest 不会自动传递到 Dubbo 服务端
Dubbo 服务端运行在独立的线程中,没有 HttpServletRequest 对象
ServletUtils.getRequest() 通过 RequestContextHolder.getRequestAttributes() 获取 request
在 Dubbo Provider 端,RequestContextHolder 是空的
导致 getRequest() 返回 null,调用 getAttribute() 时抛出 NPE

四、解决方案

4.1 设计思路

核心:通过 Dubbo 的 RpcContext 传递用户上下文
Dubbo 提供了 RpcContext 机制,可以在 Consumer 和 Provider 之间传递上下文信息:
Consumer 端:通过 RpcContext.getContext().setAttachment() 设置上下文
Provider 端:通过 RpcContext.getContext().getAttachment() 获取上下文

4.2 技术实现

方案一:创建 Dubbo Filter 传递上下文

  1. 创建 DubboContextFilter
  2. 创建 LoginUserContextHolder
  3. 修改业务代码

方案二被我pass了不说了- -

4.3 技术要点

  • Dubbo Filter 机制
    使用 @Activate 注解自动激活 Filter
    通过 isConsumerSide 和 isProviderSide 区分调用端
    在 finally 块中清理 ThreadLocal,避免内存泄漏

  • ThreadLocal 的使用
    在 Provider 端使用 ThreadLocal 存储用户信息
    确保线程安全,每个线程有独立的用户上下文
    在 finally 块中清理,避免内存泄漏

  • 降级策略
    如果 Dubbo 上下文中没有用户信息,回退到数据库查询
    保证系统的健壮性和向后兼容性

五、解决的

解决了 Dubbo RPC 调用中用户上下文丢失的问题
实现了透明的用户上下文传递机制
保证了系统的稳定性和健壮性

性能影响
上下文传递的性能开销极小
没有额外的网络开销
不影响现有业务逻辑的性能

可维护性
职责分明,易于扩展(可以传递其他上下文信息)

六、二编,问题升级

还有一个 DataScopeAspect AOP 切面也在使用 TokenService.getLoginUser(),而且这个切面在多个地方被触发。我跪了T T

    @DataScope(queryScopeKey = DataScopeAspect.EXAM_SCOPE_KEY, dataScopeArray = [DataScopeEnums.DEPARTMENT])override fun findPagedListByCriterion(pageNumber: Int, pageSize: Int, examinationAccountUUID: String, criterion: ExaminationCriterion): DovipPage<Examination> {PageHelper.startPage<Examination>(pageNumber, pageSize)...BeanUtils.copyProperties(PageInfo(resultList), page)return page}

6.1 解决方案

  1. DubboContextFilter - 传递用户上下文
@Activate(group = [CommonConstants.CONSUMER, CommonConstants.PROVIDER])
class DubboContextFilter : Filter {// Consumer 端:从 HttpServletRequest 获取 LoginUser,通过 RpcContext 传递// Provider 端:从 RpcContext 获取 LoginUser,设置到 LoginUserContextHolder
}
  1. LoginUserContextHolder - 存储用户上下文
object LoginUserContextHolder {private val context = ThreadLocal<LoginUser>()// 使用 ThreadLocal 存储 LoginUser 对象
}
  1. TokenServiceAspect - AOP 切面 - 拦截 TokenService.getLoginUser() 方法
@Aspect
@Component
@Order(1)
class TokenServiceAspect {@Around("execution(* com.xz.auth.core.TokenService.getLoginUser())")fun aroundGetLoginUser(joinPoint: ProceedingJoinPoint): LoginUser? {// 优先从 Dubbo 上下文获取val loginUser = LoginUserContextHolder.get()if (loginUser != null) {return loginUser}// 否则执行原方法return joinPoint.proceed() as? LoginUser}
}

七 流程

Web层(Consumer端):
从Spring的ThreadLocal中获取HttpServletRequest
从中提取loginUser属性
通过Dubbo的RpcContext传递给服务端

Business层(Provider端):
从RpcContext接收loginUser
存储到自定义的LoginUserContextHolder的ThreadLocal中
供业务代码使用

所以这两个是完全独立的ThreadLocal,服务于不同的目的

http://www.dtcms.com/a/482320.html

相关文章:

  • 推广网站设计推广方案用户体验设计软件
  • 权威的电商网站建设销售平台建设方案
  • 基于浏览器的DOCX文件编辑器:实现导入、编辑与导出功能
  • Android adb 基础使用指南
  • 哈尔滨网站运营服务商宁夏省住房城乡建设厅网站
  • Html播放m3u8视频
  • 鞍山网站网站建设做一个交易平台网站的成本
  • 幂等机制
  • 2025.10月报 Cherry Stuido 1.6.4、Ollama 0.12.5、Dify 1.9.1升级使用摘要
  • 抗衰≠智商税:WJCZ(唯诺因)麦角硫因的细胞级抗衰技术解析
  • 做团购网站有什么难处百度联盟官网
  • 栈与队列相关知识以及算法题
  • 网站制作 wordpress游戏门户网站 织梦
  • 【第五章:计算机视觉-计算机视觉在工业制造领域中的应用】1.工业缺陷分割-(2)BiseNet系列算法详解
  • CNC 加工中的 GLTF 文件:连接设计与制造
  • 02117 信息组织【第一章】
  • rust主要用于哪些领域
  • 【OpenHarmony】分布式文件服务模块架构
  • 具身智能数据采集高校实训方案:开启机器人教育实训新篇章
  • C++中std::forward_iterator_tag 和 std::ptrdiff_t使用详解
  • 网站建设的基本因素是什么东莞模板网站
  • k8s具体安装步骤
  • 无代码网站开发上海谷歌推广
  • 48.文本预处理:给文字数据洗个澡
  • 宠物智能用品:当毛孩子遇上 AI,是便利还是过度?
  • ESP32 想提高传输速度该如何解决?
  • 百度网站网址是什么网站设计网页版
  • 公司网站留言板网站建立不安全怎么设置通过
  • 启动监控页面监控vllm,大模型,显存的占用情况
  • JavaWeb中字节流与字符流的本质区别