乐趣区

关于dubbo:采坑经验Dubbo-26x版本中隐式参数attachment的错误使用方式稍不注意就会出现生产事故

前言

  • 接触 dubbo 分布式框架的开发也有一段时间了,其中为了解决我的项目中遇到的一些杂症,还特意学习了一下 Dubbo 服务裸露和服务引入的一些源码知识点。最近在我的项目开发的过程中,有应用到了 dubbo 的隐式参数技术点,但发现了几个在应用上非常容易出错并且一出错就是生产事变的 ,当初记录一下。

一、理解 Dubbo 隐式参数之前先理解下 Dubbo 的上下文信息

  • 什么是 Dubbo 的上下文信息?这里总结下本人的了解:

    上下文中寄存的是以后 调用过程 中所需的环境信息。所有的配置信息都将转换成 URL 的参数。RpcContext 类就是 Dubbo 的上下文,然而它仅仅是一个 ThreadLocal 级别的 长期状态记录器 ,当接管到 RPC 申请或发动 RPC 申请时,RpcContext 的状态都会变动。比方:A 调用 B、B 再调用 C 的状况下。 B 机器 的 RpcContext 会有如下的状况产生:在 B 调用 C 之前,RpcContext 记录的是 A 调 B 的信息,在 B 调用 C 之后,RpcContext 记录的是 B 调 C 的信息。

  • 比方:咱们想要获取到服务调用者的 host 相干信息,那么咱们能够在服务提供者中获取以后生产此服务的消费者的 host 信息,其代码如下所示:

    // 获取调用方的 host 信息
    String serverIP = RpcContext.getContext().getRemoteHost();

二、Dubbo 上下文携带的隐式参数 attachment

  • 不晓得各位在开发的时候,有没有遇到一种 须要额定传递给上游服务的参数 (比方标识以后用户申请的jwt、记录分布式系统全链路跟踪的 全局 traceId)。当有这方面的需要时,咱们不可能批改办法的参数签名,这与业务耦合了。此时,可能就须要应用 Dubbo 的隐式参数 attachment 了。什么是 attachment?能够把它认定为 Dubbo 协定中的一个扩大点。就有点相似于 Http 协定,咱们能够自定义申请头信息。而在 Dubbo 中,咱们也能够自定义 RPC 申请中的参数。
  • 举个例子:用户在执行下单这个业务,最终必定会通过后盾的 订单服务、库存服务 等服务。当初有个需要:在订单服务中,要明确晓得这个订单是哪个用户创立的。在库存服务中,要明确晓得这个商品最终是用户的哪个操作导致缩小的。整个需要外面有个外围:就是要晓得操作者是谁!假如我的项目用的是 jwt 技术来记录用户的状态,那么订单服务和库存服务就必须要晓得这个 jwt 字符串,将 jwt 解码后,就能晓得以后申请是由哪个用户发动的。在这样的一个场景中,应用 dubbo 的隐式参数能够达到上述的目标。实现的伪代码如下所示:

    RpcContext.getContext().setAttachment("jwt", "xxxxxxxxxxjwt 字符串 xxxxxxxx");
    // dubbo rpc 调用库存服务:缩小库存
    warehouseService.decrement();
    // dubbo rpc 调用订单服务:创立订单
    orderService.create();

这里先总结下 attachment 在应用上的几个特点:

1、key 名称不能以小驼峰命名,上游服务序列化后,会将 key 名称变成全小写(Dubbo 2.6.x 版本,在 2.7.x 版本被修复了)

2、隐式参数设置后,仅在第一次 RPC 申请失效,后续的 RPC 申请将无奈获取到隐式参数

因为 attachment 有上述的两个特点,因而咱们很容易如下的两个谬误:

易犯谬误 1 易犯谬误 2
咱们在 warehouseService.decrement() 的上游服务中能顺利的从 attachment 中获取 jwt 参数,而在 orderService.create() 的上游服务中曾经无奈顺利的从 attachment 中获取 jwt 参数了 在本例中,增加到 attachment 中的 key 为 jwt,是 ok 的。但如果咱们把 key 设置成大驼峰的命名形式,比方:userJwt。在通过 Dubbo 的一系列解决后,在 warehouseService.decrement() 上游服务中的 rpcContext 对象中的 attachment 中的 key 曾经变成了userjwt,曾经无奈获取到 key 为 jwt 的参数了。

三、筛选出最优的解决方案

  • 针对谬误 1,咱们有三个实现计划,其对应的计划策略如下所示:

    计划 长处 毛病
    计划 1:在每一次发动 RPC 之前,都手动执行一次 RpcContext.getContext().setAttachment("jwt", "xxxxxxxxxxjwt 字符串 xxxxxxxx"); 代码 能解决问题,但不是最优计划 减少编码的复杂度和代码的反复度。
    计划 2:应用 spring 的 aop 的 before 机制,在执行 rpc 发动近程服务之前,先把 jwt 放入到 attachment 中 能解决问题 dubbo 的近程调用对象自身就很分量,当初再增加一层代理,不利于定位问题。
    计划 3:应用 Dubbo 的 filter 机制,在对指定近程服务增加一层 filter,filter 的逻辑就是将 jwt 放入到 attachment 中去 比拟好的一种解决方案,充分利用到了 Dubbo 框架本身提供的 filter 扩大。这也是比拟通用的解决方案,全链路追踪的 traceId 也是这么玩的。(举荐 代码浏览性不高,filter 同 aop 一样,都是解耦的,不利于定位问题。
  • 针对谬误 2,在不对源码进行扩大的状况下,最简略的形式就是批改 key 的命名形式,这里能够应用两种形式:

    形式 1 参考 Dubbo 源码的 org.apache.dubbo.common.constants.CommonConstants 类中对增加到 URL 中的 key 的命令形式,多个单词用 . 做辨别
    形式 2 单词与单词间应用自定义的符号做分隔,比方 _,# 等符号。这种形式也能够辨别于 key 是本人增加的还是 Dubbo 框架自带的。

四、总结

  • 避坑指南:有波及到隐式参数的代码改变时,肯定要多测试。若某个环节被疏忽,很容易造成生产事变
  • 如果你感觉我的文章有用的话,欢送点赞和关注。
  • I’m a slow walker, but I never walk backwards
退出移动版